123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- typedef struct {
- int xml_ch;
- int xml_len;
- char xml_txt[8];
- } xml_xlate_t;
- static xml_xlate_t const xml_xlate[] = {
- { '&', 4, "amp;" },
- { '<', 3, "lt;" },
- { '>', 3, "gt;" },
- { '"', 5, "quot;" },
- { '\'',5, "apos;" }
- };
- #ifndef ENOMSG
- #define ENOMSG ENOENT
- #endif
- static void
- remove_continuation(char * src)
- {
- char * pzD;
- do {
- while (*src == NL) src++;
- pzD = strchr(src, NL);
- if (pzD == NULL)
- return;
-
- src = pzD--;
- if (*pzD != '\\')
- pzD++;
- } while (pzD == src);
-
- for (;;) {
- char ch = ((*pzD++) = *(src++));
- switch (ch) {
- case NUL: return;
- case '\\':
- if (*src == NL)
- --pzD;
- }
- }
- }
- static char const *
- scan_q_str(char const * pzTxt)
- {
- char q = *(pzTxt++);
- for (;;) {
- char ch = *(pzTxt++);
- if (ch == NUL)
- return pzTxt-1;
- if (ch == q)
- return pzTxt;
- if (ch == '\\') {
- ch = *(pzTxt++);
-
- if (ch == NUL)
- return pzTxt - 2;
-
- if ((ch == q) || (ch == '\\')) {
- if (*(pzTxt++) == NUL)
- return pzTxt-1;
- }
- }
- }
- }
- static tOptionValue *
- add_string(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len)
- {
- tOptionValue * pNV;
- size_t sz = nm_len + d_len + sizeof(*pNV);
- pNV = AGALOC(sz, "option name/str value pair");
- if (val == NULL) {
- pNV->valType = OPARG_TYPE_NONE;
- pNV->pzName = pNV->v.strVal;
- } else {
- pNV->valType = OPARG_TYPE_STRING;
- if (d_len > 0) {
- char const * src = val;
- char * pzDst = pNV->v.strVal;
- int ct = (int)d_len;
- do {
- int ch = *(src++) & 0xFF;
- if (ch == NUL) goto data_copy_done;
- if (ch == '&')
- ch = get_special_char(&src, &ct);
- *(pzDst++) = (char)ch;
- } while (--ct > 0);
- data_copy_done:
- *pzDst = NUL;
- } else {
- pNV->v.strVal[0] = NUL;
- }
- pNV->pzName = pNV->v.strVal + d_len + 1;
- }
- memcpy(pNV->pzName, name, nm_len);
- pNV->pzName[ nm_len ] = NUL;
- addArgListEntry(pp, pNV);
- return pNV;
- }
- static tOptionValue *
- add_bool(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len)
- {
- size_t sz = nm_len + sizeof(tOptionValue) + 1;
- tOptionValue * new_val = AGALOC(sz, "bool val");
-
- while (IS_WHITESPACE_CHAR(*val) && (d_len > 0)) {
- d_len--; val++;
- }
- if (d_len == 0)
- new_val->v.boolVal = 0;
- else if (IS_DEC_DIGIT_CHAR(*val))
- new_val->v.boolVal = (unsigned)atoi(val);
- else new_val->v.boolVal = ! IS_FALSE_TYPE_CHAR(*val);
- new_val->valType = OPARG_TYPE_BOOLEAN;
- new_val->pzName = (char *)(new_val + 1);
- memcpy(new_val->pzName, name, nm_len);
- new_val->pzName[ nm_len ] = NUL;
- addArgListEntry(pp, new_val);
- return new_val;
- }
- static tOptionValue *
- add_number(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len)
- {
- size_t sz = nm_len + sizeof(tOptionValue) + 1;
- tOptionValue * new_val = AGALOC(sz, "int val");
-
- while (IS_WHITESPACE_CHAR(*val) && (d_len > 0)) {
- d_len--; val++;
- }
- if (d_len == 0)
- new_val->v.longVal = 0;
- else
- new_val->v.longVal = strtol(val, 0, 0);
- new_val->valType = OPARG_TYPE_NUMERIC;
- new_val->pzName = (char *)(new_val + 1);
- memcpy(new_val->pzName, name, nm_len);
- new_val->pzName[ nm_len ] = NUL;
- addArgListEntry(pp, new_val);
- return new_val;
- }
- static tOptionValue *
- add_nested(void ** pp, char const * name, size_t nm_len,
- char * val, size_t d_len)
- {
- tOptionValue * new_val;
- if (d_len == 0) {
- size_t sz = nm_len + sizeof(*new_val) + 1;
- new_val = AGALOC(sz, "empty nest");
- new_val->v.nestVal = NULL;
- new_val->valType = OPARG_TYPE_HIERARCHY;
- new_val->pzName = (char *)(new_val + 1);
- memcpy(new_val->pzName, name, nm_len);
- new_val->pzName[ nm_len ] = NUL;
- } else {
- new_val = optionLoadNested(val, name, nm_len);
- }
- if (new_val != NULL)
- addArgListEntry(pp, new_val);
- return new_val;
- }
- static char const *
- scan_name(char const * name, tOptionValue * res)
- {
- tOptionValue * new_val;
- char const * pzScan = name+1;
- char const * pzVal;
- size_t nm_len = 1;
- size_t d_len = 0;
-
- pzScan = SPN_VALUE_NAME_CHARS(name + 1);
- if (pzScan[-1] == ':')
- pzScan--;
- nm_len = (size_t)(pzScan - name);
- pzScan = SPN_HORIZ_WHITE_CHARS(pzScan);
- re_switch:
- switch (*pzScan) {
- case '=':
- case ':':
- pzScan = SPN_HORIZ_WHITE_CHARS(pzScan + 1);
- if ((*pzScan == '=') || (*pzScan == ':'))
- goto default_char;
- goto re_switch;
- case NL:
- case ',':
- pzScan++;
-
- case NUL:
- add_string(&(res->v.nestVal), name, nm_len, NULL, (size_t)0);
- break;
- case '"':
- case '\'':
- pzVal = pzScan;
- pzScan = scan_q_str(pzScan);
- d_len = (size_t)(pzScan - pzVal);
- new_val = add_string(&(res->v.nestVal), name, nm_len, pzVal,
- d_len);
- if ((new_val != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
- ao_string_cook(new_val->v.strVal, NULL);
- break;
- default:
- default_char:
-
- pzVal = pzScan;
- for (;;) {
- char ch = *(pzScan++);
- switch (ch) {
- case NUL:
- pzScan--;
- d_len = (size_t)(pzScan - pzVal);
- goto string_done;
-
- case NL:
- if ( (pzScan > pzVal + 2)
- && (pzScan[-2] == '\\')
- && (pzScan[ 0] != NUL))
- continue;
-
- case ',':
- d_len = (size_t)(pzScan - pzVal) - 1;
- string_done:
- new_val = add_string(&(res->v.nestVal), name, nm_len,
- pzVal, d_len);
- if (new_val != NULL)
- remove_continuation(new_val->v.strVal);
- goto leave_scan_name;
- }
- }
- break;
- } leave_scan_name:;
- return pzScan;
- }
- static char const *
- unnamed_xml(char const * txt)
- {
- switch (*txt) {
- default:
- txt = NULL;
- break;
- case '!':
- txt = strstr(txt, "-->");
- if (txt != NULL)
- txt += 3;
- break;
- case '?':
- txt = strchr(txt, '>');
- if (txt != NULL)
- txt++;
- break;
- }
- return txt;
- }
- static char const *
- scan_xml_name(char const * name, size_t * nm_len, tOptionValue * val)
- {
- char const * scan = SPN_VALUE_NAME_CHARS(name + 1);
- *nm_len = (size_t)(scan - name);
- if (*nm_len > 64)
- return NULL;
- val->valType = OPARG_TYPE_STRING;
- if (IS_WHITESPACE_CHAR(*scan)) {
-
- scan = SPN_WHITESPACE_CHARS(scan);
- scan = parse_attrs(NULL, scan, &option_load_mode, val);
- if (scan == NULL)
- return NULL;
- }
- if (! IS_END_XML_TOKEN_CHAR(*scan))
- return NULL;
- if (*scan == '/') {
-
- if (*++scan != '>')
- return NULL;
- val->valType = OPARG_TYPE_NONE;
- }
- return scan+1;
- }
- static char const *
- find_end_xml(char const * src, size_t nm_len, char const * val, size_t * len)
- {
- char z[72] = "</";
- char * dst = z + 2;
- do {
- *(dst++) = *(src++);
- } while (--nm_len > 0);
- *(dst++) = '>';
- *dst = NUL;
- {
- char const * res = strstr(val, z);
- if (res != NULL) {
- char const * end = (option_load_mode != OPTION_LOAD_KEEP)
- ? SPN_WHITESPACE_BACK(val, res)
- : res;
- *len = (size_t)(end - val);
- res = SPN_WHITESPACE_CHARS(res + (dst - z));
- }
- return res;
- }
- }
- static char const *
- scan_xml(char const * xml_name, tOptionValue * res_val)
- {
- size_t nm_len, v_len;
- char const * scan;
- char const * val_str;
- tOptionValue valu;
- tOptionLoadMode save_mode = option_load_mode;
- if (! IS_VAR_FIRST_CHAR(*++xml_name))
- return unnamed_xml(xml_name);
-
- val_str = scan_xml_name(xml_name, &nm_len, &valu);
- if (val_str == NULL)
- goto bail_scan_xml;
- if (valu.valType == OPARG_TYPE_NONE)
- scan = val_str;
- else {
- if (option_load_mode != OPTION_LOAD_KEEP)
- val_str = SPN_WHITESPACE_CHARS(val_str);
- scan = find_end_xml(xml_name, nm_len, val_str, &v_len);
- if (scan == NULL)
- goto bail_scan_xml;
- }
-
- switch (valu.valType) {
- case OPARG_TYPE_NONE:
- add_string(&(res_val->v.nestVal), xml_name, nm_len, NULL, 0);
- break;
- case OPARG_TYPE_STRING:
- {
- tOptionValue * new_val = add_string(
- &(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
- if (option_load_mode != OPTION_LOAD_KEEP)
- munge_str(new_val->v.strVal, option_load_mode);
- break;
- }
- case OPARG_TYPE_BOOLEAN:
- add_bool(&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
- break;
- case OPARG_TYPE_NUMERIC:
- add_number(&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
- break;
- case OPARG_TYPE_HIERARCHY:
- {
- char * pz = AGALOC(v_len+1, "h scan");
- memcpy(pz, val_str, v_len);
- pz[v_len] = NUL;
- add_nested(&(res_val->v.nestVal), xml_name, nm_len, pz, v_len);
- AGFREE(pz);
- break;
- }
- case OPARG_TYPE_ENUMERATION:
- case OPARG_TYPE_MEMBERSHIP:
- default:
- break;
- }
- option_load_mode = save_mode;
- return scan;
- bail_scan_xml:
- option_load_mode = save_mode;
- return NULL;
- }
- static void
- unload_arg_list(tArgList * arg_list)
- {
- int ct = arg_list->useCt;
- char const ** pnew_val = arg_list->apzArgs;
- while (ct-- > 0) {
- tOptionValue * new_val = (tOptionValue *)VOIDP(*(pnew_val++));
- if (new_val->valType == OPARG_TYPE_HIERARCHY)
- unload_arg_list(new_val->v.nestVal);
- AGFREE(new_val);
- }
- AGFREE(arg_list);
- }
- void
- optionUnloadNested(tOptionValue const * opt_val)
- {
- if (opt_val == NULL) return;
- if (opt_val->valType != OPARG_TYPE_HIERARCHY) {
- errno = EINVAL;
- return;
- }
- unload_arg_list(opt_val->v.nestVal);
- AGFREE(opt_val);
- }
- static void
- sort_list(tArgList * arg_list)
- {
- int ix;
- int lm = arg_list->useCt;
-
- for (ix = 0; ++ix < lm;) {
- int iy = ix-1;
- tOptionValue * new_v = C(tOptionValue *, arg_list->apzArgs[ix]);
- tOptionValue * old_v = C(tOptionValue *, arg_list->apzArgs[iy]);
-
- while (strcmp(old_v->pzName, new_v->pzName) > 0) {
- arg_list->apzArgs[iy+1] = VOIDP(old_v);
- old_v = (tOptionValue *)VOIDP(arg_list->apzArgs[--iy]);
- if (iy < 0)
- break;
- }
-
- arg_list->apzArgs[iy+1] = VOIDP(new_v);
- }
- }
- static tOptionValue *
- optionLoadNested(char const * text, char const * name, size_t nm_len)
- {
- tOptionValue * res_val;
-
- if (text == NULL) {
- errno = EINVAL;
- return NULL;
- }
- text = SPN_WHITESPACE_CHARS(text);
- if (*text == NUL) {
- errno = ENOMSG;
- return NULL;
- }
- res_val = AGALOC(sizeof(*res_val) + nm_len + 1, "nest args");
- res_val->valType = OPARG_TYPE_HIERARCHY;
- res_val->pzName = (char *)(res_val + 1);
- memcpy(res_val->pzName, name, nm_len);
- res_val->pzName[nm_len] = NUL;
- {
- tArgList * arg_list = AGALOC(sizeof(*arg_list), "nest arg l");
- res_val->v.nestVal = arg_list;
- arg_list->useCt = 0;
- arg_list->allocCt = MIN_ARG_ALLOC_CT;
- }
-
- do {
- text = SPN_WHITESPACE_CHARS(text);
- if (IS_VAR_FIRST_CHAR(*text))
- text = scan_name(text, res_val);
- else switch (*text) {
- case NUL:
- goto scan_done;
- case '<':
- text = scan_xml(text, res_val);
- if (text == NULL)
- goto woops;
- if (*text == ',')
- text++;
- break;
- case '#':
- text = strchr(text, NL);
- break;
- default:
- goto woops;
- }
- } while (text != NULL); scan_done:;
- {
- tArgList * al = res_val->v.nestVal;
- if (al->useCt == 0) {
- errno = ENOMSG;
- goto woops;
- }
- if (al->useCt > 1)
- sort_list(al);
- }
- return res_val;
- woops:
- AGFREE(res_val->v.nestVal);
- AGFREE(res_val);
- return NULL;
- }
- void
- optionNestedVal(tOptions * opts, tOptDesc * od)
- {
- if (opts < OPTPROC_EMIT_LIMIT)
- return;
- if (od->fOptState & OPTST_RESET) {
- tArgList * arg_list = od->optCookie;
- int ct;
- char const ** av;
- if (arg_list == NULL)
- return;
- ct = arg_list->useCt;
- av = arg_list->apzArgs;
- while (--ct >= 0) {
- void * p = VOIDP(*(av++));
- optionUnloadNested((tOptionValue const *)p);
- }
- AGFREE(od->optCookie);
- } else {
- tOptionValue * opt_val = optionLoadNested(
- od->optArg.argString, od->pz_Name, strlen(od->pz_Name));
- if (opt_val != NULL)
- addArgListEntry(&(od->optCookie), VOIDP(opt_val));
- }
- }
- static int
- get_special_char(char const ** ppz, int * ct)
- {
- char const * pz = *ppz;
- if (*ct < 3)
- return '&';
- if (*pz == '#') {
- int base = 10;
- int retch;
- pz++;
- if (*pz == 'x') {
- base = 16;
- pz++;
- }
- retch = (int)strtoul(pz, (char **)&pz, base);
- if (*pz != ';')
- return '&';
- base = (int)(++pz - *ppz);
- if (base > *ct)
- return '&';
- *ct -= base;
- *ppz = pz;
- return retch;
- }
- {
- int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
- xml_xlate_t const * xlatp = xml_xlate;
- for (;;) {
- if ( (*ct >= xlatp->xml_len)
- && (strncmp(pz, xlatp->xml_txt, (size_t)xlatp->xml_len) == 0)) {
- *ppz += xlatp->xml_len;
- *ct -= xlatp->xml_len;
- return xlatp->xml_ch;
- }
- if (--ctr <= 0)
- break;
- xlatp++;
- }
- }
- return '&';
- }
- static void
- emit_special_char(FILE * fp, int ch)
- {
- int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
- xml_xlate_t const * xlatp = xml_xlate;
- putc('&', fp);
- for (;;) {
- if (ch == xlatp->xml_ch) {
- fputs(xlatp->xml_txt, fp);
- return;
- }
- if (--ctr <= 0)
- break;
- xlatp++;
- }
- fprintf(fp, XML_HEX_BYTE_FMT, (ch & 0xFF));
- }
|