/* text2qos.c - Converts textual representation of QOS parameters to binary
		encoding */

/* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

#include "atm.h"


#define fetch __atmlib_fetch


#define RATE_ERROR -2


int __t2q_get_rate(const char **text,int up)
{
    const char mult[] = "kKmMgGg";
    const char *multiplier;
    char *end;
    unsigned int rate,fract;
    int power;

    if (!strncmp(*text,"max",3)) {
	*text += 3;
	return ATM_MAX_PCR;
    }
    rate = strtoul(*text,&end,10);
    power = fract = 0;
    if (*end == '.')
	for (end++; *end && isdigit(*end); end++) {
	    fract = fract*10+*end-48;
	    if (--power == -9) break;
	}
    multiplier = NULL;
    if (*end && (multiplier = strchr(mult,*end))) {
	while (multiplier >= mult) {
	    if (rate > UINT_MAX/1000) return RATE_ERROR;
	    rate *= 1000;
	    power += 3;
	    multiplier -= 2;
	}
	end++;
    }
    while (power && fract)
	if (power < 0) {
	    fract /= 10;
	    power++;
	}
	else {
	    fract *= 10;
	    power--;
	}
    rate += fract;
    if (strlen(end) < 3) {
	if (multiplier) return RATE_ERROR;
    }
    else if (!strncmp(end,"cps",3)) end += 3;
	else if (!strncmp(end,"bps",3)) {
		rate = (rate+(up ? 8*ATM_CELL_PAYLOAD-1 : 0))/8/
		  ATM_CELL_PAYLOAD;
		end += 3;
	    }
	    else if (multiplier) return RATE_ERROR;
    if (rate > INT_MAX) return RATE_ERROR;
    *text = end;
    return rate;
}


static int params(const char **text,struct atm_trafprm *a,
  struct atm_trafprm *b)
{
    int value;
    char *end;

    if (*(*text)++ != ':') return -1;
    while (1) {
	if (!**text) return -1;
	switch (fetch(text,"max_pcr=","pcr=","min_pcr=","max_sdu=","sdu=","scr=","mbs=", NULL)) {
	    case 0:
		if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1;
		if (a) a->max_pcr = value;
		if (b) b->max_pcr = value;
		break;
	    case 1:
		if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1;
		if (a) a->pcr = value;
		if (b) b->pcr = value;
		break;
	    case 2:
		if ((value = __t2q_get_rate(text,1)) == RATE_ERROR) return -1;
		if (value == ATM_MAX_PCR) return -1;
		if (a) a->min_pcr = value;
		if (b) b->min_pcr = value;
		break;
	    case 3:
	    case 4:
		value = strtol(*text,&end,10);
		if (value < 0) return -1;
		*text = end;
		if (a) a->max_sdu = value;
		if (b) b->max_sdu = value;
		break;
	    case 5:
	    	if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1;
		if (a) a->scr = value;
		if (b) b->scr = value;
		break;
	    case 6:
		value = strtol(*text,&end,10);
		if (value < 0) return -1;
		*text = end;
		if (a) a->mbs = value;
		if (b) b->mbs = value;
		break;
	    default:
		return 0;
	}
	if (!**text) break;
	if (*(*text)++ != ',') return -1;
    }
    return 0;
}


int text2qos(const char *text,struct atm_qos *qos,int flags)
{
    int traffic_class,aal;

    traffic_class = ATM_NONE;
    aal = ATM_NO_AAL;
    do {
	static const unsigned char aal_number[] = { ATM_AAL0, ATM_AAL5 };
	int item;

	item = fetch(&text,"!none","ubr","cbr","vbr-nrt","vbr-rt","abr","aal0","aal5",NULL);
	switch (item) {
	    case 1:
	    case 2:
	    case 3:
	    case 4:
	    case 5:
		traffic_class = item;
		break;
	    case 6:
	    case 7:
		aal = aal_number[item-6];
		break;
	    default:
		return -1;
	}
    }
    while (*text == ',' ? text++ : 0);
    if (!traffic_class) return -1;
    if (qos && !(flags & T2Q_DEFAULTS)) memset(qos,0,sizeof(*qos));
    if (qos) qos->txtp.traffic_class = qos->rxtp.traffic_class = traffic_class;
    if (qos && aal) qos->aal = aal;
    if (!*text) return 0;
    if (params(&text,qos ? &qos->txtp : NULL,qos ? &qos->rxtp : NULL))
	return -1;
    if (!*text) return 0;
    switch (fetch(&text,"tx","rx",NULL)) {
	case 0:
	    if (!fetch(&text,":none",NULL)) {
		if (qos) qos->txtp.traffic_class = ATM_NONE;
		if (*text == ',') text++;
		break;
	    }
	    if (params(&text,qos ? &qos->txtp : NULL,NULL)) return -1;
	    break;
	case 1:
	    text -= 2;
	    break;
	default:
	    return -1;
    }
    if (!*text) return 0;
    if (fetch(&text,"rx",NULL)) return -1;
    if (!fetch(&text,":none",NULL) && qos) qos->rxtp.traffic_class = ATM_NONE;
    else if (params(&text,qos ? &qos->rxtp : NULL,NULL)) return -1;
    return *text ? -1 : 0;
}
