|
|
1.1 root 1: /*
2: * $Source: /mit/kerberos/src/lib/krb/RCS/kparse.c,v $
3: * $Author: jtkohl $
4: *
5: * Copyright 1988 by the Massachusetts Institute of Technology.
6: *
7: * For copying and distribution information, please see the file
8: * <mit-copyright.h>.
9: *
10: * Purpose:
11: * This module was developed to parse the "~/.klogin" files for
12: * Kerberos-authenticated rlogin/rcp/rsh services. However, it is
13: * general purpose and can be used to parse any such parameter file.
14: *
15: * The parameter file should consist of one or more entries, with each
16: * entry on a separate line and consisting of zero or more
17: * "keyword=value" combinations. The keyword is case insensitive, but
18: * the value is not. Any string may be enclosed in quotes, and
19: * c-style "\" literals are supported. A comma may be used to
20: * separate the k/v combinations, and multiple commas are ignored.
21: * Whitespace (blank or tab) may be used freely and is ignored.
22: *
23: * Full error processing is available. When PS_BAD_KEYWORD or
24: * PS_SYNTAX is returned from fGetParameterSet(), the string ErrorMsg
25: * contains a meaningful error message.
26: *
27: * Keywords and their default values are programmed by an external
28: * table.
29: *
30: * Routines:
31: * fGetParameterSet() parse one line of the parameter file
32: * fGetKeywordValue() parse one "keyword=value" combo
33: * fGetToken() parse one token
34: */
35:
36: #ifndef lint
37: static char rcsid_kparse_c[] =
38: "$Header: kparse.c,v 4.5 89/01/21 17:20:39 jtkohl Exp $";
39: #endif lint
40:
41: #include <mit-copyright.h>
42: #include <stdio.h>
43: #include <ctype.h>
44: #include <kparse.h>
45:
46: #ifndef FALSE
47: #define FALSE 0
48: #define TRUE 1
49: #endif
50:
51: #define void int
52:
53: #define MAXKEY 80
54: #define MAXVALUE 80
55:
56: char *malloc();
57: char *strcpy();
58:
59: int LineNbr=1; /* current line nbr in parameter file */
60: char ErrorMsg[80]; /* meaningful only when KV_SYNTAX, PS_SYNTAX,
61: * or PS_BAD_KEYWORD is returned by
62: * fGetKeywordValue or fGetParameterSet */
63:
64: int fGetParameterSet( fp,parm,parmcount )
65: FILE *fp;
66: parmtable parm[];
67: int parmcount;
68: {
69: int rc,i;
70: char keyword[MAXKEY];
71: char value[MAXVALUE];
72:
73: while (TRUE) {
74: rc=fGetKeywordValue(fp,keyword,MAXKEY,value,MAXVALUE);
75:
76: switch (rc) {
77:
78: case KV_EOF:
79: return(PS_EOF);
80:
81: case KV_EOL:
82: return(PS_OKAY);
83:
84: case KV_SYNTAX:
85: return(PS_SYNTAX);
86:
87: case KV_OKAY:
88: /*
89: * got a reasonable keyword/value pair. Search the
90: * parameter table to see if we recognize the keyword; if
91: * not, return an error. If we DO recognize it, make sure
92: * it has not already been given. If not already given,
93: * save the value.
94: */
95: for (i=0; i<parmcount; i++) {
96: if (strcmp(strutol(keyword),parm[i].keyword)==0) {
97: if (parm[i].value) {
98: sprintf(ErrorMsg,"duplicate keyword \"%s\" found",
99: keyword);
100: return(PS_BAD_KEYWORD);
101: }
102: parm[i].value = strsave( value );
103: break;
104: }
105: }
106: if (i >= parmcount) {
107: sprintf(ErrorMsg, "unrecognized keyword \"%s\" found",
108: keyword);
109: return(PS_BAD_KEYWORD);
110: }
111: break;
112:
113: default:
114: sprintf(ErrorMsg,
115: "panic: bad return (%d) from fGetToken()",rc);
116: break;
117: }
118: }
119: }
120:
121: /*
122: * Routine: ParmCompare
123: *
124: * Purpose:
125: * ParmCompare checks a specified value for a particular keyword.
126: * fails if keyword not found or keyword found but the value was
127: * different. Like strcmp, ParmCompare returns 0 for a match found, -1
128: * otherwise
129: */
130: int ParmCompare( parm, parmcount, keyword, value )
131: parmtable parm[];
132: int parmcount;
133: char *keyword;
134: char *value;
135: {
136: int i;
137:
138: for (i=0; i<parmcount; i++) {
139: if (strcmp(parm[i].keyword,keyword)==0) {
140: if (parm[i].value) {
141: return(strcmp(parm[i].value,value));
142: } else {
143: return(strcmp(parm[i].defvalue,value));
144: }
145: }
146: }
147: return(-1);
148: }
149:
150: void FreeParameterSet(parm,parmcount)
151: parmtable parm[];
152: int parmcount;
153: {
154: int i;
155:
156: for (i=0; i<parmcount; i++) {
157: if (parm[i].value) {
158: free(parm[i].value);
159: parm[i].value = (char *)NULL;
160: }
161: }
162: }
163:
164: int fGetKeywordValue( fp, keyword, klen, value, vlen )
165: FILE *fp;
166: char *keyword;
167: int klen;
168: char *value;
169: int vlen;
170: {
171: int rc;
172: int gotit;
173:
174: *keyword = *value = '\0'; /* preset strings to NULL */
175:
176: /*
177: * Looking for a keyword.
178: * return an exception for EOF or BAD_QSTRING
179: * ignore leading WHITEspace
180: * ignore any number of leading commas
181: * newline means we have all the parms for this
182: * statement; give an indication that there is
183: * nothing more on this line.
184: * stop looking if we find QSTRING, STRING, or NUMBER
185: * return syntax error for any other PUNKtuation
186: */
187: gotit = FALSE;
188: do {
189: rc = fGetToken(fp,keyword,klen);
190:
191: switch (rc) {
192:
193: case GTOK_WHITE:
194: break;
195:
196: case GTOK_EOF:
197: return(KV_EOF);
198:
199: case GTOK_BAD_QSTRING:
200: sprintf(ErrorMsg,"unterminated string \"%s found",keyword);
201: return(KV_SYNTAX);
202:
203: case GTOK_PUNK:
204: if (strcmp("\n",keyword)==0) {
205: return(KV_EOL);
206: } else if (strcmp(",",keyword)!=0) {
207: sprintf(ErrorMsg,"expecting rvalue, found \'%s\'",keyword);
208: }
209: break;
210:
211: case GTOK_STRING:
212: case GTOK_QSTRING:
213: case GTOK_NUMBER:
214: gotit = TRUE;
215: break;
216:
217: default:
218: sprintf(ErrorMsg,"panic: bad return (%d) from fGetToken()",rc);
219: return(KV_SYNTAX);
220: }
221:
222: } while (!gotit);
223:
224: /*
225: * now we expect an equal sign.
226: * skip any whitespace
227: * stop looking if we find an equal sign
228: * anything else causes a syntax error
229: */
230: gotit = FALSE;
231: do {
232: rc = fGetToken(fp,value,vlen);
233:
234: switch (rc) {
235:
236: case GTOK_WHITE:
237: break;
238:
239: case GTOK_BAD_QSTRING:
240: sprintf(ErrorMsg,
241: "expecting \'=\', found unterminated string \"%s",
242: value);
243: return(KV_SYNTAX);
244:
245: case GTOK_PUNK:
246: if (strcmp("=",value)==0) {
247: gotit = TRUE;
248: } else {
249: if (strcmp("\n",value)==0) {
250: sprintf(ErrorMsg,"expecting \"=\", found newline");
251: fUngetChar('\n',fp);
252: } else {
253: sprintf(ErrorMsg,
254: "expecting rvalue, found \'%s\'",keyword);
255: }
256: return(KV_SYNTAX);
257: }
258: break;
259:
260: case GTOK_STRING:
261: case GTOK_QSTRING:
262: case GTOK_NUMBER:
263: sprintf(ErrorMsg,"expecting \'=\', found \"%s\"",value);
264: return(KV_SYNTAX);
265:
266: case GTOK_EOF:
267: sprintf(ErrorMsg,"expecting \'=\', found EOF");
268: return(KV_SYNTAX);
269:
270: default:
271: sprintf(ErrorMsg,
272: "panic: bad return (%d) from fGetToken()",rc);
273: return(KV_SYNTAX);
274: }
275:
276: } while ( !gotit );
277:
278: /*
279: * got the keyword and equal sign, now get a value.
280: * ignore any whitespace
281: * any punctuation is a syntax error
282: */
283: gotit = FALSE;
284: do {
285: rc = fGetToken(fp,value,vlen);
286:
287: switch (rc) {
288:
289: case GTOK_WHITE:
290: break;
291:
292: case GTOK_EOF:
293: sprintf(ErrorMsg,"expecting rvalue, found EOF");
294: return(KV_SYNTAX);
295:
296: case GTOK_BAD_QSTRING:
297: sprintf(ErrorMsg,"unterminated quoted string \"%s",value);
298: return(KV_SYNTAX);
299:
300: case GTOK_PUNK:
301: if (strcmp("\n",value)==0) {
302: sprintf(ErrorMsg,"expecting rvalue, found newline");
303: fUngetChar('\n',fp);
304: } else {
305: sprintf(ErrorMsg,
306: "expecting rvalue, found \'%s\'",value);
307: }
308: return(KV_SYNTAX);
309: break;
310:
311: case GTOK_STRING:
312: case GTOK_QSTRING:
313: case GTOK_NUMBER:
314: gotit = TRUE;
315: return(KV_OKAY);
316:
317: default:
318: sprintf(ErrorMsg,
319: "panic: bad return (%d) from fGetToken()",rc);
320: return(KV_SYNTAX);
321: }
322:
323: } while ( !gotit );
324: /*NOTREACHED*/
325: }
326:
327: /*
328: * Routine Name: fGetToken
329: *
330: * Function: read the next token from the specified file.
331: * A token is defined as a group of characters
332: * terminated by a white space char (SPACE, CR,
333: * LF, FF, TAB). The token returned is stripped of
334: * both leading and trailing white space, and is
335: * terminated by a NULL terminator. An alternate
336: * definition of a token is a string enclosed in
337: * single or double quotes.
338: *
339: * Explicit Parameters:
340: * fp pointer to the input FILE
341: * dest pointer to destination buffer
342: * maxlen length of the destination buffer. The buffer
343: * length INCLUDES the NULL terminator.
344: *
345: * Implicit Parameters: stderr where the "token too long" message goes
346: *
347: * External Procedures: fgetc
348: *
349: * Side Effects: None
350: *
351: * Return Value: A token classification value, as
352: * defined in kparse.h. Note that the
353: * classification for end of file is
354: * always zero.
355: */
356: int fGetToken(fp, dest, maxlen)
357: FILE *fp;
358: char *dest;
359: int maxlen;
360: {
361: int ch='\0';
362: int len=0;
363: char *p = dest;
364: int digits;
365:
366: ch=fGetChar(fp);
367:
368: /*
369: * check for a quoted string. If found, take all characters
370: * that fit until a closing quote is found. Note that this
371: * algorithm will not behave well for a string which is too long.
372: */
373: if (ISQUOTE(ch)) {
374: int done = FALSE;
375: do {
376: ch = fGetChar(fp);
377: done = ((maxlen<++len)||ISLINEFEED(ch)||(ch==EOF)
378: ||ISQUOTE(ch));
379: if (ch=='\\')
380: ch = fGetLiteral(fp);
381: if (!done)
382: *p++ = ch;
383: else if ((ch!=EOF) && !ISQUOTE(ch))
384: fUngetChar(ch,fp);
385: } while (!done);
386: *p = '\0';
387: if (ISLINEFEED(ch)) return(GTOK_BAD_QSTRING);
388: return(GTOK_QSTRING);
389: }
390:
391: /*
392: * Not a quoted string. If its a token character (rules are
393: * defined via the ISTOKENCHAR macro, in kparse.h) take it and all
394: * token chars following it until we run out of space.
395: */
396: digits=TRUE;
397: if (ISTOKENCHAR(ch)) {
398: while ( (ISTOKENCHAR(ch)) && len<maxlen-1 ) {
399: if (!isdigit(ch)) digits=FALSE;
400: *p++ = ch;
401: len++;
402: ch = fGetChar(fp);
403: };
404: *p = '\0';
405:
406: if (ch!=EOF) {
407: fUngetChar(ch,fp);
408: }
409: if (digits) {
410: return(GTOK_NUMBER);
411: } else {
412: return(GTOK_STRING);
413: }
414: }
415:
416: /*
417: * Neither a quoted string nor a token character. Return a string
418: * with just that one character in it.
419: */
420: if (ch==EOF) {
421: return(GTOK_EOF);
422: }
423: if (!ISWHITESPACE(ch)) {
424: *p++ = ch;
425: *p='\0';
426: } else {
427: *p++ = ' '; /* white space is always the
428: * blank character */
429: *p='\0';
430: /*
431: * The character is a white space. Flush all additional white
432: * space.
433: */
434: while (ISWHITESPACE(ch) && ((ch=fGetChar(fp)) != EOF))
435: ;
436: if (ch!=EOF) {
437: fUngetChar(ch,fp);
438: }
439: return(GTOK_WHITE);
440: }
441: return(GTOK_PUNK);
442: }
443:
444: /*
445: * fGetLiteral is called after we find a '\' in the input stream. A
446: * string of numbers following the backslash are converted to the
447: * appropriate value; hex (0xn), octal (0n), and decimal (otherwise)
448: * are all supported. If the char after the \ is not a number, we
449: * special case certain values (\n, \f, \r, \b) or return a literal
450: * otherwise (useful for \", for example).
451: */
452: fGetLiteral(fp)
453: FILE *fp;
454: {
455: int ch;
456: int n=0;
457: int base;
458:
459: ch = fGetChar(fp);
460:
461: if (!isdigit(ch)) {
462: switch (ch) {
463: case 'n': return('\n');
464: case 'f': return('\f');
465: case 'r': return('\r');
466: case 'b': return('\b');
467: default: return(ch);
468: }
469: }
470:
471: /*
472: * got a number. might be decimal (no prefix), octal (prefix 0),
473: * or hexadecimal (prefix 0x). Set the base appropriately.
474: */
475: if (ch!='0') {
476: base=10; /* its a decimal number */
477: } else {
478: /*
479: * found a zero, its either hex or octal
480: */
481: ch = fGetChar(fp);
482: if ((ch!='x') && (ch!='X')) {
483: base=010;
484: } else {
485: ch = fGetChar(fp);
486: base=0x10;
487: }
488: }
489:
490: switch (base) {
491:
492: case 010: /* octal */
493: while (ISOCTAL(ch)) {
494: n = (n*base) + ch - '0';
495: ch = fGetChar(fp);
496: }
497: break;
498:
499: case 10: /* decimal */
500: while (isdigit(ch)) {
501: n = (n*base) + ch - '0';
502: ch = fGetChar(fp);
503: }
504: break;
505: case 0x10: /* hexadecimal */
506: while (isxdigit(ch)) {
507: if (isdigit(ch)) {
508: n = (n*base) + ch - '0';
509: } else {
510: n = (n*base) + toupper(ch) - 'A' + 0xA ;
511: }
512: ch = fGetChar(fp);
513: }
514: break;
515: default:
516: fprintf(stderr,"fGetLiteral() died real bad. Fix gettoken.c.");
517: exit(1);
518: break;
519: }
520: fUngetChar(ch,fp);
521: return(n);
522: }
523:
524: /*
525: * exactly the same as ungetc(3) except that the line number of the
526: * input file is maintained.
527: */
528: fUngetChar(ch,fp)
529: int ch;
530: FILE *fp;
531: {
532: if (ch=='\n') LineNbr--;
533: return(ungetc(ch,fp));
534: }
535:
536:
537: /*
538: * exactly the same as fgetc(3) except that the line number of the
539: * input file is maintained.
540: */
541: fGetChar(fp)
542: FILE *fp;
543: {
544: int ch = fgetc(fp);
545: if (ch=='\n') LineNbr++;
546: return(ch);
547: }
548:
549:
550: /*
551: * Routine Name: strsave
552: *
553: * Function: return a pointer to a saved copy of the
554: * input string. the copy will be allocated
555: * as large as necessary.
556: *
557: * Explicit Parameters: pointer to string to save
558: *
559: * Implicit Parameters: None
560: *
561: * External Procedures: malloc,strcpy,strlen
562: *
563: * Side Effects: None
564: *
565: * Return Value: pointer to copied string
566: *
567: */
568: char * strsave(p)
569: char *p;
570: {
571: return(strcpy(malloc(strlen(p)+1),p));
572: }
573:
574:
575: /*
576: * strutol changes all characters in a string to lower case, in place.
577: * the pointer to the beginning of the string is returned.
578: */
579:
580: char * strutol( start )
581: char *start;
582: {
583: char *q;
584: for (q=start; *q; q++)
585: if (isupper(*q))
586: *q=tolower(*q);
587: return(start);
588: }
589:
590: #ifdef GTOK_TEST /* mainline test routine for fGetToken() */
591:
592: #define MAXTOKEN 100
593:
594: char *pgm = "gettoken";
595:
596: main(argc,argv)
597: int argc;
598: char **argv;
599: {
600: char *p;
601: int type;
602: FILE *fp;
603:
604: if (--argc) {
605: fp = fopen(*++argv,"ra");
606: if (fp == (FILE *)NULL) {
607: fprintf(stderr,"can\'t open \"%s\"\n",*argv);
608: }
609: } else
610: fp = stdin;
611:
612: p = malloc(MAXTOKEN);
613: while (type = fGetToken(fp,p,MAXTOKEN)) {
614: switch(type) {
615: case GTOK_BAD_QSTRING:
616: printf("BAD QSTRING!\t");
617: break;
618: case GTOK_EOF:
619: printf("EOF!\t");
620: break;
621: case GTOK_QSTRING:
622: printf("QSTRING\t");
623: break;
624: case GTOK_STRING:
625: printf("STRING\t");
626: break;
627: case GTOK_NUMBER:
628: printf("NUMBER\t");
629: break;
630: case GTOK_PUNK:
631: printf("PUNK\t");
632: break;
633: case GTOK_WHITE:
634: printf("WHITE\t");
635: break;
636: default:
637: printf("HUH?\t");
638: break;
639: }
640: if (*p=='\n')
641: printf("\\n\n");
642: else
643: printf("%s\n",p);
644: }
645: exit(0);
646: }
647: #endif
648:
649: #ifdef KVTEST
650:
651: main(argc,argv)
652: int argc;
653: char **argv;
654: {
655: int rc,ch;
656: FILE *fp;
657: char key[MAXKEY],valu[MAXVALUE];
658: char *filename;
659:
660: if (argc != 2) {
661: fprintf(stderr,"usage: test <filename>\n");
662: exit(1);
663: }
664:
665: if (!(fp=fopen(*++argv,"r"))) {
666: fprintf(stderr,"can\'t open input file \"%s\"\n",filename);
667: exit(1);
668: }
669: filename = *argv;
670:
671: while ((rc=fGetKeywordValue(fp,key,MAXKEY,valu,MAXVALUE))!=KV_EOF){
672:
673: switch (rc) {
674:
675: case KV_EOL:
676: printf("%s, line %d: nada mas.\n",filename,LineNbr-1);
677: break;
678:
679: case KV_SYNTAX:
680: printf("%s, line %d: syntax error: %s\n",
681: filename,LineNbr,ErrorMsg);
682: while ( ((ch=fGetChar(fp))!=EOF) && (ch!='\n') );
683: break;
684:
685: case KV_OKAY:
686: printf("%s, line %d: okay, %s=\"%s\"\n",
687: filename,LineNbr,key,valu);
688: break;
689:
690: default:
691: printf("panic: bad return (%d) from fGetKeywordValue\n",rc);
692: break;
693: }
694: }
695: printf("EOF");
696: fclose(fp);
697: exit(0);
698: }
699: #endif
700:
701: #ifdef PSTEST
702:
703: parmtable kparm[] = {
704: /* keyword, default, found value */
705: { "user", "", (char *)NULL },
706: { "realm", "Athena", (char *)NULL },
707: { "instance", "", (char *)NULL }
708: };
709:
710: main(argc,argv)
711: int argc;
712: char **argv;
713: {
714: int rc,i,ch;
715: FILE *fp;
716: char *filename;
717:
718: if (argc != 2) {
719: fprintf(stderr,"usage: test <filename>\n");
720: exit(1);
721: }
722:
723: if (!(fp=fopen(*++argv,"r"))) {
724: fprintf(stderr,"can\'t open input file \"%s\"\n",filename);
725: exit(1);
726: }
727: filename = *argv;
728:
729: while ((rc=fGetParameterSet(fp,kparm,PARMCOUNT(kparm))) != PS_EOF) {
730:
731: switch (rc) {
732:
733: case PS_BAD_KEYWORD:
734: printf("%s, line %d: %s\n",filename,LineNbr,ErrorMsg);
735: while ( ((ch=fGetChar(fp))!=EOF) && (ch!='\n') );
736: break;
737:
738: case PS_SYNTAX:
739: printf("%s, line %d: syntax error: %s\n",
740: filename,LineNbr,ErrorMsg);
741: while ( ((ch=fGetChar(fp))!=EOF) && (ch!='\n') );
742: break;
743:
744: case PS_OKAY:
745: printf("%s, line %d: valid parameter set found:\n",
746: filename,LineNbr-1);
747: for (i=0; i<PARMCOUNT(kparm); i++) {
748: printf("\t%s = \"%s\"\n",kparm[i].keyword,
749: (kparm[i].value ? kparm[i].value
750: : kparm[i].defvalue));
751: }
752: break;
753:
754: default:
755: printf("panic: bad return (%d) from fGetParameterSet\n",rc);
756: break;
757: }
758: FreeParameterSet(kparm,PARMCOUNT(kparm));
759: }
760: printf("EOF");
761: fclose(fp);
762: exit(0);
763: }
764: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.