1/*
2 * Copyright 1999 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5#pragma ident	"%Z%%M%	%I%	%E% SMI"
6
7/******************************************************************
8	Copyright 1989, 1991, 1992 by Carnegie Mellon University
9
10                      All Rights Reserved
11
12Permission to use, copy, modify, and distribute this software and its
13documentation for any purpose and without fee is hereby granted,
14provided that the above copyright notice appear in all copies and that
15both that copyright notice and this permission notice appear in
16supporting documentation, and that the name of CMU not be
17used in advertising or publicity pertaining to distribution of the
18software without specific, written prior permission.
19
20CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
21ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
22CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
23ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
24WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
26SOFTWARE.
27******************************************************************/
28/*
29 * parse.c
30 */
31
32/* HISTORY
33 * Jerry Yeung	6-6-96
34 * Jerry Yeung	1-9-97	fix 4019317
35 */
36
37#include <stdio.h>
38#include <ctype.h>
39#include <stdlib.h>
40#include <sys/types.h>
41#include "parse.h"
42
43struct trap_item *trap_list = NULL;
44
45/* A quoted string value-- too long for a general "token" */
46char *quoted_string_buffer;
47
48/*
49 * This is one element of an object identifier with either an integer
50 * subidentifier, or a textual string label, or both.
51 * The subid is -1 if not present, and label is NULL if not present.
52 */
53struct subid {
54    int subid;
55    char *label;
56};
57
58#define MAXTC	128
59struct tc {	/* textual conventions */
60    int type;
61    char descriptor[MAXTOKEN];
62    struct enum_list *enums;
63} tclist[MAXTC];
64
65
66
67int Line = 1;
68
69#define SYNTAX_MASK	0x80
70/* types of tokens
71 Tokens wiht the SYNTAX_MASK bit set are syntax tokens */
72#define	CONTINUE    -1
73#define ENDOFFILE   0
74#define LABEL	    1
75#define SUBTREE	    2
76#define SYNTAX	    3
77#define PARSE_OBJID	    (4 | SYNTAX_MASK)
78#define OCTETSTR    (5 | SYNTAX_MASK)
79#define PARSE_INTEGER	    (6 | SYNTAX_MASK)
80#define NETADDR	    (7 | SYNTAX_MASK)
81#define	IPADDR	    (8 | SYNTAX_MASK)
82#define PARSE_COUNTER	    (9 | SYNTAX_MASK)
83#define PARSE_GUAGE	    (10 | SYNTAX_MASK)
84#define PARSE_TIMETICKS   (11 | SYNTAX_MASK)
85#define PARSE_OPAQUE	    (12 | SYNTAX_MASK)
86#define NUL	    (13 | SYNTAX_MASK)
87#define SEQUENCE    14
88#define OF	    15	/* SEQUENCE OF */
89#define OBJTYPE	    16
90#define ACCESS	    17
91#define READONLY    18
92#define READWRITE   19
93#define	WRITEONLY   20
94#define NOACCESS    21
95#define STATUS	    22
96#define MANDATORY   23
97#define OPTIONAL    24
98#define OBSOLETE    25
99/* #define RECOMMENDED 26 */
100#define PUNCT	    27
101#define EQUALS	    28
102#define NUMBER	    29
103#define LEFTBRACKET 30
104#define RIGHTBRACKET 31
105#define	LEFTPAREN   32
106#define RIGHTPAREN  33
107#define COMMA	    34
108#define DESCRIPTION 35
109#define QUOTESTRING 36
110#define INDEX       37
111#define DEFVAL      38
112#define DEPRECATED  39
113#define SIZE        40
114#define BITSTRING   (41 | SYNTAX_MASK)
115#define NSAPADDRESS (42 | SYNTAX_MASK)
116#define PARSE_COUNTER64   (43 | SYNTAX_MASK)
117#define OBJGROUP    44
118#define NOTIFTYPE   45
119#define AUGMENTS    46
120#define COMPLIANCE  47
121#define READCREATE  48
122#define UNITS       49
123#define REFERENCE   50
124#define NUM_ENTRIES 51
125#define MODULEIDENTITY 52
126#define LASTUPDATED 53
127#define ORGANIZATION 54
128#define CONTACTINFO 55
129#define UINTEGER32 (56 | SYNTAX_MASK)
130#define CURRENT	    57
131#define DEFINITIONS 58
132#define END         59
133#define SEMI        60
134
135/*
136-- Olivier Reisacher 95/2/14
137*/
138#define PSEUDO_TOKEN_TABLE        61
139#define PSEUDO_TOKEN_ENTRY        62
140
141/* Jerry Yeung 6-6-96 (trap-support) */
142#define TRAPTYPE 63
143#define ENTERPRISE 64
144#define VARIABLES 65
145
146struct tok {
147	char *name;			/* token name */
148	int len;			/* length not counting nul */
149	int token;			/* value */
150	int hash;			/* hash of name */
151	struct tok *next;		/* pointer to next in hash table */
152};
153
154
155struct tok tokens[] = {
156	{ "obsolete", sizeof ("obsolete")-1, OBSOLETE },
157	{ "Opaque", sizeof ("Opaque")-1, PARSE_OPAQUE },
158/*	{ "recommended", sizeof("recommended")-1, RECOMMENDED },  */
159	{ "optional", sizeof ("optional")-1, OPTIONAL },
160	{ "LAST-UPDATED", sizeof ("LAST-UPDATED")-1, LASTUPDATED },
161	{ "ORGANIZATION", sizeof ("ORGANIZATION")-1, ORGANIZATION },
162	{ "CONTACT-INFO", sizeof ("CONTACT-INFO")-1, CONTACTINFO },
163	{ "MODULE-IDENTITY", sizeof ("MODULE-IDENTITY")-1, MODULEIDENTITY },
164	{ "MODULE-COMPLIANCE", sizeof ("MODULE-COMPLIANCE")-1, COMPLIANCE },
165        { "DEFINITIONS", sizeof("DEFINITIONS")-1, DEFINITIONS},
166        { "END", sizeof("END")-1, END},
167        { ";", sizeof(";")-1, SEMI},
168	{ "AUGMENTS", sizeof ("AUGMENTS")-1, AUGMENTS },
169	{ "not-accessible", sizeof ("not-accessible")-1, NOACCESS },
170	{ "write-only", sizeof ("write-only")-1, WRITEONLY },
171	{ "NsapAddress", sizeof("NsapAddress")-1, NSAPADDRESS},
172	{ "UNITS", sizeof("Units")-1, UNITS},
173	{ "REFERENCE", sizeof("REFERENCE")-1, REFERENCE},
174	{ "NUM-ENTRIES", sizeof("NUM-ENTRIES")-1, NUM_ENTRIES},
175	{ "BITSTRING", sizeof("BitString")-1, BITSTRING},
176	{ "BIT", sizeof("BIT")-1, CONTINUE},
177	{ "Counter64", sizeof("Counter64")-1, PARSE_COUNTER64},
178	{ "TimeTicks", sizeof ("TimeTicks")-1, PARSE_TIMETICKS },
179	{ "NOTIFICATION-TYPE", sizeof ("NOTIFICATION-TYPE")-1, NOTIFTYPE },
180	{ "OBJECT-GROUP", sizeof ("OBJECT-GROUP")-1, OBJGROUP },
181	{ "OBJECTIDENTIFIER", sizeof ("OBJECTIDENTIFIER")-1, PARSE_OBJID },
182	/*
183	 * This CONTINUE appends the next word onto OBJECT,
184	 * hopefully matching OBJECTIDENTIFIER above.
185	 */
186	{ "OBJECT", sizeof ("OBJECT")-1, CONTINUE },
187	{ "NetworkAddress", sizeof ("NetworkAddress")-1, NETADDR },
188	{ "Gauge", sizeof ("Gauge")-1, PARSE_GUAGE },
189	{ "read-write", sizeof ("read-write")-1, READWRITE },
190	{ "read-create", sizeof ("read-create")-1, READCREATE },
191	{ "OCTETSTRING", sizeof ("OCTETSTRING")-1, OCTETSTR },
192	{ "OCTET", sizeof ("OCTET")-1, -1 },
193	{ "OF", sizeof ("OF")-1, OF },
194	{ "SEQUENCE", sizeof ("SEQUENCE")-1, SEQUENCE },
195	{ "NULL", sizeof ("NULL")-1, NUL },
196	{ "IpAddress", sizeof ("IpAddress")-1, IPADDR },
197	{ "UInteger32", sizeof ("UInteger32")-1, UINTEGER32 },
198	{ "INTEGER", sizeof ("INTEGER")-1, PARSE_INTEGER },
199	{ "Counter", sizeof ("Counter")-1, PARSE_COUNTER },
200	{ "read-only", sizeof ("read-only")-1, READONLY },
201        { "DESCRIPTION", sizeof ("DESCRIPTION")-1, DESCRIPTION },
202        { "INDEX", sizeof ("INDEX")-1, INDEX },
203        { "DEFVAL", sizeof ("DEFVAL")-1, DEFVAL },
204        { "deprecated", sizeof ("deprecated")-1, DEPRECATED },
205        { "SIZE", sizeof ("SIZE")-1, SIZE },
206	{ "MAX-ACCESS", sizeof ("MAX-ACCESS")-1, ACCESS },
207	{ "ACCESS", sizeof ("ACCESS")-1, ACCESS },
208	{ "mandatory", sizeof ("mandatory")-1, MANDATORY },
209	{ "current", sizeof ("current")-1, CURRENT },
210	{ "STATUS", sizeof ("STATUS")-1, STATUS },
211	{ "SYNTAX", sizeof ("SYNTAX")-1, SYNTAX },
212	{ "OBJECT-TYPE", sizeof ("OBJECT-TYPE")-1, OBJTYPE },
213	{ "{", sizeof ("{")-1, LEFTBRACKET },
214	{ "}", sizeof ("}")-1, RIGHTBRACKET },
215	{ "::=", sizeof ("::=")-1, EQUALS },
216	{ "(", sizeof ("(")-1, LEFTPAREN },
217	{ ")", sizeof (")")-1, RIGHTPAREN },
218	{ ",", sizeof (",")-1, COMMA },
219	{ "TRAP-TYPE", sizeof ("TRAP-TYPE")-1, TRAPTYPE },
220	{ "ENTERPRISE", sizeof ("ENTERPRISE")-1, ENTERPRISE },
221	{ "VARIABLES", sizeof ("VARIABLES")-1, VARIABLES },
222	{ NULL }
223};
224
225#define	HASHSIZE	32
226#define	BUCKET(x)	(x & 0x01F)
227
228struct tok	*buckets[HASHSIZE];
229static void
230do_subtree(struct tree *root, struct node **nodes);
231static int
232get_token(register FILE *fp, register char *token);
233static int
234parseQuoteString(register FILE *fp, register char *token);
235static int
236tossObjectIdentifier(register FILE *fp);
237
238int number_value;
239
240static void
241hash_init()
242{
243	register struct tok	*tp;
244	register char	*cp;
245	register int	h;
246	register int	b;
247
248/*
249-- Olivier Reisacher 95/2/14
250	bzero((char *)buckets, sizeof(buckets));
251*/
252	memset((void *) buckets, 0, sizeof(buckets));
253
254	for (tp = tokens; tp->name; tp++) {
255		for (h = 0, cp = tp->name; *cp; cp++)
256			h += *cp;
257		tp->hash = h;
258		b = BUCKET(h);
259		if (buckets[b])
260		    tp->next = buckets[b]; /* BUG ??? */
261		buckets[b] = tp;
262	}
263}
264
265#define NHASHSIZE    128
266#define NBUCKET(x)   (x & 0x7F)
267struct node *nbuckets[NHASHSIZE];
268
269void init_node_hash(nodes)
270     struct node *nodes;
271{
272     register struct node *np, *nextp;
273     register char *cp;
274     register int hash;
275
276/*
277-- Olivier Reisacher 95/2/14
278     bzero((char *)nbuckets,sizeof(nbuckets));
279*/
280     memset((void *) nbuckets, 0, sizeof(nbuckets));
281
282     for(np = nodes; np;){
283         nextp = np->next;
284         hash = 0;
285	 for(cp = np->parent; *cp; cp++)
286	     hash += *cp;
287	 np->next = nbuckets[NBUCKET(hash)];
288	 nbuckets[NBUCKET(hash)] = np;
289	 np = nextp;
290     }
291}
292
293static char *
294Malloc(num)
295    unsigned num;
296{
297    char     *buf;
298
299    /* this is to fix (what seems to be) a problem with the IBM RT C
300library malloc */
301    if (num < 16)
302	num = 16;
303    buf = (char *)malloc (num);
304    if (buf == NULL) {
305      fprintf(stderr, "malloc failed.  Exiting\n");
306      exit(1);
307    }
308    return (char *)buf;
309}
310
311static void
312print_error(string, token, type)
313    char *string;
314    char *token;
315    int type;
316{
317    if (type == ENDOFFILE)
318	fprintf(stderr, "%s(EOF): On or around line %d\n", string, Line);
319    else if (token)
320	fprintf(stderr, "%s(%s): On or around line %d\n", string, token, Line);
321    else
322	fprintf(stderr, "%s: On or around line %d\n", string, Line);
323}
324
325/*
326-- Olivier Reisacher 95/2/14
327#ifdef TEST
328print_subtree(tree, count)
329*/
330void print_subtree(tree, count)
331    struct tree *tree;
332    int count;
333{
334    struct tree *tp;
335    int i;
336
337    for(i = 0; i < count; i++)
338	printf("  ");
339    printf("Children of %s:\n", tree->label);
340    count++;
341    for(tp = tree->child_list; tp; tp = tp->next_peer){
342	for(i = 0; i < count; i++)
343	    printf("  ");
344	printf("%s\n", tp->label);
345    }
346    for(tp = tree->child_list; tp; tp = tp->next_peer){
347	print_subtree(tp, count);
348    }
349}
350/*
351-- Olivier Reisacher 95/2/14
352#endif
353*/
354
355int translation_table[256];
356
357void build_translation_table(){
358    int count;
359
360    for(count = 0; count < 256; count++){
361	switch(count){
362	    case PARSE_OBJID:
363		translation_table[count] = TYPE_OBJID;
364		break;
365	    case OCTETSTR:
366		translation_table[count] = TYPE_OCTETSTR;
367		break;
368	    case PARSE_INTEGER:
369		translation_table[count] = TYPE_INTEGER;
370		break;
371	    case NETADDR:
372		translation_table[count] = TYPE_IPADDR;
373		break;
374	    case IPADDR:
375		translation_table[count] = TYPE_IPADDR;
376		break;
377	    case PARSE_COUNTER:
378		translation_table[count] = TYPE_COUNTER;
379		break;
380	    case PARSE_GUAGE:
381		translation_table[count] = TYPE_GAUGE;
382		break;
383	    case PARSE_TIMETICKS:
384		translation_table[count] = TYPE_TIMETICKS;
385		break;
386	    case PARSE_OPAQUE:
387		translation_table[count] = TYPE_OPAQUE;
388		break;
389	    case NUL:
390		translation_table[count] = TYPE_NULL;
391		break;
392	    case PARSE_COUNTER64:
393		translation_table[count] = TYPE_COUNTER64;
394		break;
395	    case BITSTRING:
396		translation_table[count] = TYPE_BITSTRING;
397		break;
398	    case NSAPADDRESS:
399		translation_table[count] = TYPE_NSAPADDRESS;
400		break;
401	    case UINTEGER32:
402		translation_table[count] = TYPE_UINTEGER;
403		break;
404
405/*
406-- Olivier Reisacher 95/2/14
407*/
408		case PSEUDO_TOKEN_TABLE:
409			translation_table[count] = TYPE_TABLE;
410			break;
411		case PSEUDO_TOKEN_ENTRY:
412			translation_table[count] = TYPE_ENTRY;
413			break;
414
415	    default:
416		translation_table[count] = TYPE_OTHER;
417		break;
418	}
419    }
420}
421
422/*
423-- Olivier Reisacher 95/2/14
424static struct tree *
425*/
426struct tree *
427build_tree(nodes)
428    struct node *nodes;
429{
430    struct node *np;
431    struct tree *tp;
432    int bucket, nodes_left = 0;
433
434    build_translation_table();
435    /* grow tree from this root node */
436    init_node_hash(nodes);
437
438/*
439-- Olivier Reisacher 95/2/14
440     build root node
441    tp = (struct tree *)Malloc(sizeof(struct tree));
442    tp->parent = NULL;
443    tp->next_peer = NULL;
444    tp->child_list = NULL;
445    tp->enums = NULL;
446    strcpy(tp->label, "joint-iso-ccitt");
447    tp->subid = 2;
448    tp->type = 0;
449    tp->description = 0;
450     XXX nodes isn't needed in do_subtree() ???
451    do_subtree(tp, &nodes);
452    lasttp = tp;
453
454     build root node
455    tp = (struct tree *)Malloc(sizeof(struct tree));
456    tp->parent = NULL;
457    tp->next_peer = lasttp;
458    tp->child_list = NULL;
459    tp->enums = NULL;
460    strcpy(tp->label, "ccitt");
461    tp->subid = 0;
462    tp->type = 0;
463    tp->description = 0;
464     XXX nodes isn't needed in do_subtree() ???
465    do_subtree(tp, &nodes);
466    lasttp = tp;
467*/
468
469    /* build root node */
470    tp = (struct tree *)Malloc(sizeof(struct tree));
471    tp->parent = NULL;
472/*
473-- Olivier Reisacher 95/2/14
474    tp->next_peer = lasttp;
475*/
476    tp->next_peer = NULL;
477    tp->child_list = NULL;
478    tp->enums = NULL;
479    strcpy(tp->label, "iso");
480    tp->subid = 1;
481    tp->type = 0;
482    tp->description = 0;
483    /* XXX nodes isn't needed in do_subtree() ??? */
484    do_subtree(tp, &nodes);
485/*
486-- Olivier Reisacher 95/2/14
487*/
488	tp->access = 0;
489	tp->indexs = NULL;
490	tp->n_indexs = 0;
491
492
493#ifdef TEST
494    print_subtree(tp, 0);
495#endif /* TEST */
496    /* If any nodes are left, the tree is probably inconsistent */
497    for(bucket = 0; bucket < NHASHSIZE; bucket++){
498        if (nbuckets[bucket]){
499	    nodes_left = 1;
500	    break;
501	}
502    }
503    if (nodes_left){
504	fprintf(stderr, "The mib description doesn't seem to be consistent.\n");
505	fprintf(stderr, "Some nodes couldn't be linked under the \"iso\" tree.\n");
506	fprintf(stderr, "these nodes are left:\n");
507	for(bucket = 0; bucket < NHASHSIZE; bucket++){
508	    for(np = nbuckets[bucket]; np; np = np->next)
509	        fprintf(stderr, "%s ::= { %s %d } (%d)\n", np->label,
510			np->parent, np->subid, np->type);
511	}
512    }
513    return tp;
514}
515
516/*
517 * Find all the children of root in the list of nodes.  Link them into the
518 * tree and out of the nodes list.
519 */
520static void
521do_subtree(root, nodes)
522    struct tree *root;
523    struct node **nodes;
524{
525    register struct tree *tp;
526    register struct node *np, **headp;
527    struct node *oldnp = NULL, *child_list = NULL, *childp = NULL;
528    char *cp;
529    int hash;
530
531    tp = root;
532    hash = 0;
533    for(cp = tp->label; *cp; cp++)
534        hash += *cp;
535    headp = &nbuckets[NBUCKET(hash)];
536    /*
537     * Search each of the nodes for one whose parent is root, and
538     * move each into a separate list.
539     */
540    for(np = *headp; np; np = np->next){
541	if ((*tp->label != *np->parent) || strcmp(tp->label, np->parent)){
542	    if ((*tp->label == *np->label) && !strcmp(tp->label, np->label)){
543		/* if there is another node with the same label, assume that
544		 * any children after this point in the list belong to the other node.
545		 * This adds some scoping to the table and allows vendors to
546		 * reuse names such as "ip".
547		 */
548		break;
549	    }
550	    oldnp = np;
551	} else {
552	    if (child_list == NULL){
553		child_list = childp = np;   /* first entry in child list */
554	    } else {
555		childp->next = np;
556		childp = np;
557	    }
558	    /* take this node out of the node list */
559	    if (oldnp == NULL){
560		*headp = np->next;  /* fix root of node list */
561	    } else {
562		oldnp->next = np->next;	/* link around this node */
563	    }
564	}
565    }
566    if (childp)
567	childp->next = 0;	/* re-terminate list */
568    /*
569     * Take each element in the child list and place it into the tree.
570     */
571    for(np = child_list; np; np = np->next){
572	tp = (struct tree *)Malloc(sizeof(struct tree));
573	tp->parent = root;
574	tp->next_peer = NULL;
575	tp->child_list = NULL;
576	strcpy(tp->label, np->label);
577	tp->subid = np->subid;
578	tp->type = translation_table[np->type];
579        tp->oct_str_len = np->oct_str_len;
580	tp->enums = np->enums;
581	np->enums = NULL;	/* so we don't free them later */
582	tp->description = np->description; /* steals memory from np */
583	np->description = NULL; /* so we don't free it later */
584/*
585-- Olivier Reisacher 95/2/14
586*/
587	tp->access = np->access;
588	tp->indexs = np->indexs;
589	np->indexs = NULL;
590	tp->n_indexs = np->n_indexs;
591/*
592-- Olivier Reisacher 95/2/14
593-- The goal of this modification is to order the
594-- tree according to the subid
595
596	if (root->child_list == NULL){
597	    root->child_list = tp;
598	} else {
599	    peer->next_peer = tp;
600	}
601	peer = tp;
602*/
603	if(root->child_list == NULL)
604	{
605		root->child_list = tp;
606	}
607	else
608	{
609		struct tree *t = root->child_list;
610		struct tree *l = NULL;
611
612		while(t)
613		{
614			if(tp->subid < t->subid)
615			{
616				break;
617			}
618			l = t;
619			t = t->next_peer;
620		}
621		if(l == NULL)
622		{
623			tp->next_peer = root->child_list;
624			root->child_list = tp;
625		}
626		else
627		{
628			tp->next_peer = l->next_peer;
629			l->next_peer = tp;
630		}
631	}
632
633/*	if (tp->type == TYPE_OTHER) */
634	    do_subtree(tp, nodes);	/* recurse on this child if it isn't
635					   an end node */
636    }
637    /* free all nodes that were copied into tree */
638    oldnp = NULL;
639    for(np = child_list; np; np = np->next){
640	if (oldnp)
641	    free(oldnp);
642	oldnp = np;
643    }
644    if (oldnp)
645	free(oldnp);
646}
647
648
649/*
650 * Takes a list of the form:
651 * { iso org(3) dod(6) 1 }
652 * and creates several nodes, one for each parent-child pair.
653 * Returns NULL on error.
654 */
655static int
656getoid(fp, oid,  length)
657    register FILE *fp;
658    register struct subid *oid;	/* an array of subids */
659    int length;	    /* the length of the array */
660{
661    register int count;
662    int type;
663    char token[MAXTOKEN];
664    register char *cp;
665
666#ifdef TRACE_PROC
667printf("getoid() invoked\n");
668#endif
669
670    if ((type = get_token(fp, token)) != LEFTBRACKET){
671	print_error("Expected \"{\"", token, type);
672	return NULL;
673    }
674    type = get_token(fp, token);
675    for(count = 0; count < length; count++, oid++){
676	oid->label = 0;
677	oid->subid = -1;
678	if (type == RIGHTBRACKET){
679	    return count;
680	} else if (type != LABEL && type != NUMBER){
681	    print_error("Not valid for object identifier", token, type);
682	    return NULL;
683	}
684	if (type == LABEL){
685	    /* this entry has a label */
686	    cp = (char *)Malloc((unsigned)strlen(token) + 1);
687	    strcpy(cp, token);
688	    oid->label = cp;
689	    type = get_token(fp, token);
690	    if (type == LEFTPAREN){
691		type = get_token(fp, token);
692		if (type == NUMBER){
693		    oid->subid = atoi(token);
694		    if ((type = get_token(fp, token)) != RIGHTPAREN){
695			print_error("Unexpected a closing parenthesis", token, type);
696			return NULL;
697		    }
698		} else {
699		    print_error("Expected a number", token, type);
700		    return NULL;
701		}
702	    } else {
703		continue;
704	    }
705	} else {
706	    /* this entry  has just an integer sub-identifier */
707	    oid->subid = atoi(token);
708	}
709	type = get_token(fp, token);
710    }
711    return count;
712
713
714}
715
716static void
717free_node(np)
718    struct node *np;
719{
720    struct enum_list *ep, *tep;
721/*
722-- Olivier Reisacher 95/2/14
723*/
724	struct index_list *ip, *tip;
725
726	ip = np->indexs;
727	while(ip)
728	{
729		tip = ip;
730		ip = ip->next;
731		free((char *)tip);
732	}
733
734
735    ep = np->enums;
736    while(ep){
737	tep = ep;
738	ep = ep->next;
739	free((char *)tep);
740    }
741    free((char *)np);
742}
743
744int parse_traptype(fp,name)
745FILE *fp;
746char *name;
747{
748   int type;
749   char token[MAXTOKEN];
750   struct trap_item *ti;
751   struct index_list *ip;
752   static struct trap_item *last_trap_item=NULL;
753
754#ifdef TRACE_PROC
755printf("parse_traptype() invoked\n");
756#endif
757
758   type = get_token(fp,token);
759   if( type == ENTERPRISE){
760     if( (type = get_token(fp,token)) == LABEL){
761	/* create a trap item */
762        ti = (struct trap_item*)calloc(1, sizeof(struct trap_item));
763        if(ti==NULL){
764	 fprintf(stderr,"calloc failed\n");
765	 return 0;
766        }
767	strcpy(ti->label,name);
768	strcpy(ti->enterprise_label,token);
769	ti->enterprise_subids[0] = (u_long)-1;
770
771	type = get_token(fp,token);
772	while(type != EQUALS){
773	  switch(type){
774	    case VARIABLES:
775		type = get_token(fp,token);
776		if(type != LEFTBRACKET){
777		  print_error("{ expected in VARIABLES clause",token,type);
778		  free(ti);
779		  return 0;
780		}
781		type = get_token(fp,token);
782		while(type != RIGHTBRACKET)
783		{
784			if(type != LABEL)
785			{
786				print_error("LABEL expected in VARIABLES1 clause",token,type);
787				free(ti);
788				return 0;
789			}
790
791
792			(ti->n_variables)++;
793		    	if(ti->var_list == NULL)
794			{
795				ip = ti->var_list = (struct index_list *)
796					Malloc(sizeof(struct index_list));
797			}
798			else
799			{
800				ip->next = (struct index_list *)
801					Malloc(sizeof(struct enum_list));
802				ip = ip->next;
803			}
804			ip->next = 0;
805			ip->tp = NULL;
806
807			ip->label =
808				(char *)Malloc((unsigned)strlen(token) + 1);
809			strcpy(ip->label, token);
810
811			type = get_token(fp, token);
812
813			switch(type)
814			{
815				case COMMA:
816					type = get_token(fp, token);
817					break;
818
819				case RIGHTBRACKET:
820					break;
821
822				default:
823					print_error(", or } expected in VARIABLES clause",token,type);
824					free(ti);
825					return 0;
826
827			}
828		}
829		break;
830
831	    case DESCRIPTION:
832		type = get_token(fp,token);
833		if(type != QUOTESTRING){
834		  print_error("Bad DESCRIPTION",token,type);
835		  free(ti);
836		  return 0;
837		}
838		ti->description = quoted_string_buffer;
839		quoted_string_buffer = (char*)malloc(MAXQUOTESTR);
840		if(quoted_string_buffer==NULL){
841		  fprintf(stderr,"malloc failed\n");
842		  return 0;
843		}
844		break;
845	    case REFERENCE:
846		type = get_token(fp,token);
847		if(type != QUOTESTRING){
848		  print_error("Bad DESCRIPTION",token,type);
849		  free(ti);
850		  return 0;
851		}
852		break;
853	    default:
854		/* NOTHING*/
855		break;
856	  }
857	  type = get_token(fp,token);
858	}
859	/* get the integer */
860	if( (type = get_token(fp,token)) == NUMBER){
861		ti->value = atoi(token);
862		/* attach the item to list */
863
864
865
866	}else{
867        	print_error("Expected a number",token,type);
868		free(ti);
869		return 0;
870	}
871     }else{
872        print_error("Expected \"enterprise name\"",token,type);
873        return 0;
874     }
875   }else{
876     print_error("Expected \"ENTERPRISE\"",token,type);
877     return 0;
878   }
879   if(trap_list == NULL){
880	last_trap_item = trap_list = ti;
881   }else{
882	last_trap_item->next = ti;
883	last_trap_item = ti;
884   }
885   return 1;
886}
887
888/*
889 * Parse an entry of the form:
890 * label OBJECT IDENTIFIER ::= { parent 2 }
891 * The "label OBJECT IDENTIFIER" portion has already been parsed.
892 * Returns 0 on error.
893 */
894static struct node *
895parse_objectid(fp, name)
896    FILE *fp;
897    char *name;
898{
899    int type;
900    char token[MAXTOKEN];
901    register int count;
902    register struct subid *op, *nop;
903    int length;
904    struct subid oid[32];
905    struct node *np, *root, *oldnp = NULL;
906
907    type = get_token(fp, token);
908    if (type != EQUALS){
909	print_error("Bad format", token, type);
910	return 0;
911    }
912    if ((length = getoid(fp, oid, 32)) != 0){
913	np = root = (struct node *)Malloc(sizeof(struct node));
914
915/*
916-- Olivier Reisacher 95/2/14
917	bzero((char *)np, sizeof(struct node));
918*/
919	memset((void *) np, 0, sizeof(struct node));
920
921	/*
922	 * For each parent-child subid pair in the subid array,
923	 * create a node and link it into the node list.
924	 */
925	for(count = 0, op = oid, nop=oid+1; count < (length - 2); count++,
926	    op++, nop++){
927	    /* every node must have parent's name and child's name or number */
928	    if (op->label && (nop->label || (nop->subid != -1))){
929		strcpy(np->parent, op->label);
930		if (nop->label)
931		    strcpy(np->label, nop->label);
932		if (nop->subid != -1)
933		    np->subid = nop->subid;
934		np->type = 0;
935		np->enums = 0;
936/*
937-- Olivier Reisacher 95/2/14
938*/
939		np->access = 0;
940		np->n_indexs = 0;
941		np->indexs = NULL;
942
943		/* set up next entry */
944		np->next = (struct node *)Malloc(sizeof(*np->next));
945
946/*
947-- Olivier Reisacher 95/2/14
948		bzero((char *)np->next, sizeof(struct node));
949*/
950		memset((void *) np->next, 0, sizeof(struct node));
951
952		oldnp = np;
953		np = np->next;
954	    }
955	}
956	np->next = (struct node *)NULL;
957	/*
958	 * The above loop took care of all but the last pair.  This pair is taken
959	 * care of here.  The name for this node is taken from the label for this
960	 * entry.
961	 * np still points to an unused entry.
962	 */
963	if (count == (length - 2)){
964	    if (op->label){
965		strcpy(np->parent, op->label);
966		strcpy(np->label, name);
967		if (nop->subid != -1)
968		    np->subid = nop->subid;
969		else
970		    print_error("Warning: This entry is pretty silly",
971				np->label, type);
972	    } else {
973		free_node(np);
974		if (oldnp)
975		    oldnp->next = NULL;
976		else
977		    return NULL;
978	    }
979	} else {
980	    print_error("Missing end of oid", (char *)NULL, type);
981	    free_node(np);   /* the last node allocated wasn't used */
982	    if (oldnp)
983		oldnp->next = NULL;
984	    return NULL;
985	}
986	/* free the oid array */
987	for(count = 0, op = oid; count < length; count++, op++){
988	    if (op->label)
989		free(op->label);
990	    op->label = 0;
991	}
992	return root;
993    } else {
994	print_error("Bad object identifier", (char *)NULL, type);
995	return 0;
996    }
997}
998
999static int
1000get_tc(descriptor, ep)
1001    char *descriptor;
1002    struct enum_list **ep;
1003{
1004    int i;
1005
1006    for(i = 0; i < MAXTC; i++){
1007	if (tclist[i].type == 0)
1008	    break;
1009	if (!strcmp(descriptor, tclist[i].descriptor)){
1010	    *ep = tclist[i].enums;
1011	    return tclist[i].type;
1012	}
1013    }
1014    return LABEL;
1015}
1016
1017/*
1018 * Parses an asn type.  Structures are ignored by this parser.
1019 * Returns NULL on error.
1020 */
1021static int
1022parse_asntype(fp, name, ntype, ntoken)
1023    FILE *fp;
1024    char *name;
1025    int *ntype;
1026    char *ntoken;
1027{
1028    int type, i;
1029    char token[MAXTOKEN];
1030    struct enum_list *ep;
1031    struct tc *tcp;
1032    int level;
1033
1034#ifdef TRACE_PROC
1035printf("parse_asntype() invoked\n");
1036#endif
1037
1038    type = get_token(fp, token);
1039    if (type == SEQUENCE){
1040	while((type = get_token(fp, token)) != ENDOFFILE){
1041	    if (type == RIGHTBRACKET){
1042		*ntype = get_token(fp, ntoken);
1043		return 1;
1044	    }
1045	}
1046	print_error("Expected \"}\"", token, type);
1047	return 0;
1048    } else {
1049	if (!strcmp(token, "TEXTUAL-CONVENTION")){
1050	    while (type != SYNTAX)
1051		type = get_token(fp, token);
1052	    type = get_token(fp, token);
1053	}
1054	/* textual convention */
1055	for(i = 0; i < MAXTC; i++){
1056	    if (tclist[i].type == 0)
1057		break;
1058	}
1059	if (i == MAXTC){
1060	    print_error("No more textual conventions possible.", token, type);
1061	    return 0;
1062	}
1063	tcp = &tclist[i];
1064	strcpy(tcp->descriptor, name);
1065	if (!(type & SYNTAX_MASK)){
1066
1067/*
1068-- Olivier Reisacher 95/2/14
1069	    print_error("Textual convention doesn't map to real type.", token,
1070			type);
1071	    return 0;
1072	}
1073*/
1074		int w;
1075
1076		w = get_tc(token, &ep);
1077		if(!(w & SYNTAX_MASK))
1078		{
1079	    		print_error("Textual convention doesn't map to real type.", token, type);
1080	    		return 0;
1081		}
1082		type = w;
1083	}
1084
1085	tcp->type = type;
1086	*ntype = get_token(fp, ntoken);
1087	if (*ntype == LEFTPAREN){
1088	    level = 1;
1089	    /* don't record any constraints for now */
1090	    while(level > 0){
1091		*ntype = get_token(fp, ntoken);
1092		if (*ntype == LEFTPAREN)
1093		    level++;
1094		if (*ntype == RIGHTPAREN)
1095		    level--;
1096	    }
1097	    *ntype = get_token(fp, ntoken);
1098	} else if (*ntype == LEFTBRACKET) {
1099	    /* if there is an enumeration list, parse it */
1100	    while((type = get_token(fp, token)) != ENDOFFILE){
1101		if (type == RIGHTBRACKET)
1102		    break;
1103		if (type == LABEL){
1104		    /* this is an enumerated label */
1105		    if (tcp->enums == 0){
1106			ep = tcp->enums = (struct enum_list *)
1107			    Malloc(sizeof(struct enum_list));
1108		    } else {
1109			ep->next = (struct enum_list *)
1110			    Malloc(sizeof(struct enum_list));
1111			ep = ep->next;
1112		    }
1113		    ep->next = 0;
1114		    /* a reasonable approximation for the length */
1115		    ep->label =
1116			(char *)Malloc((unsigned)strlen(token) + 1);
1117		    strcpy(ep->label, token);
1118		    type = get_token(fp, token);
1119		    if (type != LEFTPAREN){
1120			print_error("Expected \"(\"", token, type);
1121			/* free_node(np); */
1122			return 0;
1123		    }
1124		    type = get_token(fp, token);
1125		    if (type != NUMBER){
1126			print_error("Expected integer", token, type);
1127			/* free_node(np); */
1128			return 0;
1129		    }
1130		    ep->value = atoi(token);
1131		    type = get_token(fp, token);
1132		    if (type != RIGHTPAREN){
1133			print_error("Expected \")\"", token, type);
1134			/* free_node(np); */
1135			return 0;
1136		    }
1137		}
1138	    }
1139	    if (type == ENDOFFILE){
1140		print_error("Expected \"}\"", token, type);
1141		/* free_node(np); */
1142		return 0;
1143	    }
1144	    *ntype = get_token(fp, ntoken);
1145	}
1146	return 1;
1147    }
1148}
1149
1150
1151/*
1152 * Parses an OBJECT TYPE macro.
1153 * Returns 0 on error.
1154 */
1155static struct node *
1156parse_objecttype(fp, name)
1157    register FILE *fp;
1158    char *name;
1159{
1160    register int type;
1161    char token[MAXTOKEN];
1162    int count, length;
1163    struct subid oid[32];
1164    char syntax[MAXTOKEN];
1165    int nexttype, tctype;
1166    char nexttoken[MAXTOKEN];
1167    register struct node *np;
1168    register struct enum_list *ep;
1169/*
1170-- Olivier Reisacher 95/2/14
1171*/
1172	struct index_list *ip;
1173
1174    type = get_token(fp, token);
1175    if (type != SYNTAX){
1176	print_error("Bad format for OBJECT TYPE", token, type);
1177	return 0;
1178    }
1179    np = (struct node *)Malloc(sizeof(struct node));
1180    np->next = 0;
1181    np->enums = 0;
1182    np->description = NULL;        /* default to an empty description */
1183/*
1184-- Olivier Reisacher 95/2/14
1185*/
1186	np->access = 0;
1187	np->n_indexs = 0;
1188	np->indexs = 0;
1189
1190    type = get_token(fp, token);
1191    if (type == LABEL){
1192	tctype = get_tc(token, &(np->enums));
1193#if 0
1194	if (tctype == LABEL){
1195	    print_error("No known translation for type", token, type);
1196	    return 0;
1197	}
1198#endif
1199	type = tctype;
1200    }
1201    np->type = type;
1202    np->oct_str_len = 0;
1203    nexttype = get_token(fp, nexttoken);
1204    switch(type){
1205	case SEQUENCE:
1206	    strcpy(syntax, token);
1207	    if (nexttype == OF){
1208		strcat(syntax, " ");
1209		strcat(syntax, nexttoken);
1210		nexttype = get_token(fp, nexttoken);
1211		strcat(syntax, " ");
1212		strcat(syntax, nexttoken);
1213		nexttype = get_token(fp, nexttoken);
1214
1215/*
1216-- Olivier Reisacher 95/2/14
1217*/
1218		np->type = PSEUDO_TOKEN_TABLE;
1219	    }
1220
1221	    break;
1222	case PARSE_INTEGER:
1223	case UINTEGER32:
1224	    strcpy(syntax, token);
1225	    if (nexttype == LEFTBRACKET) {
1226		/* if there is an enumeration list, parse it */
1227		while((type = get_token(fp, token)) != ENDOFFILE){
1228		    if (type == RIGHTBRACKET)
1229			break;
1230		    if (type == LABEL){
1231			/* this is an enumerated label */
1232			if (np->enums == 0){
1233			    ep = np->enums = (struct enum_list *)
1234					Malloc(sizeof(struct enum_list));
1235			} else {
1236			    ep->next = (struct enum_list *)
1237					Malloc(sizeof(struct enum_list));
1238			    ep = ep->next;
1239			}
1240			ep->next = 0;
1241			/* a reasonable approximation for the length */
1242			ep->label =
1243			    (char *)Malloc((unsigned)strlen(token) + 1);
1244			strcpy(ep->label, token);
1245			type = get_token(fp, token);
1246			if (type != LEFTPAREN){
1247			    print_error("Expected \"(\"", token, type);
1248			    free_node(np);
1249			    return 0;
1250			}
1251			type = get_token(fp, token);
1252			if (type != NUMBER){
1253			    print_error("Expected integer", token, type);
1254			    free_node(np);
1255			    return 0;
1256			}
1257			ep->value = atoi(token);
1258			type = get_token(fp, token);
1259			if (type != RIGHTPAREN){
1260			    print_error("Expected \")\"", token, type);
1261			    free_node(np);
1262			    return 0;
1263			}
1264		    }
1265		}
1266		if (type == ENDOFFILE){
1267		    print_error("Expected \"}\"", token, type);
1268		    free_node(np);
1269		    return 0;
1270		}
1271		nexttype = get_token(fp, nexttoken);
1272	    } else if (nexttype == LEFTPAREN){
1273		/* ignore the "constrained integer" for now */
1274		nexttype = get_token(fp, nexttoken);
1275		nexttype = get_token(fp, nexttoken);
1276		nexttype = get_token(fp, nexttoken);
1277	    }
1278	    break;
1279	case BITSTRING:
1280	    strcpy(syntax, token);
1281	    if (nexttype == LEFTBRACKET) {
1282		/* if there is an enumeration list, parse it */
1283		while((type = get_token(fp, token)) != ENDOFFILE){
1284		    if (type == RIGHTBRACKET)
1285			break;
1286		    if (type == LABEL){
1287			/* this is an enumerated label */
1288			if (np->enums == 0){
1289			    ep = np->enums = (struct enum_list *)
1290					Malloc(sizeof(struct enum_list));
1291			} else {
1292			    ep->next = (struct enum_list *)
1293					Malloc(sizeof(struct enum_list));
1294			    ep = ep->next;
1295			}
1296			ep->next = 0;
1297			/* a reasonable approximation for the length */
1298			ep->label =
1299			    (char *)Malloc((unsigned)strlen(token) + 1);
1300			strcpy(ep->label, token);
1301			type = get_token(fp, token);
1302			if (type != LEFTPAREN){
1303			    print_error("Expected \"(\"", token, type);
1304			    free_node(np);
1305			    return 0;
1306			}
1307			type = get_token(fp, token);
1308			if (type != NUMBER){
1309			    print_error("Expected integer", token, type);
1310			    free_node(np);
1311			    return 0;
1312			}
1313			ep->value = atoi(token);
1314			type = get_token(fp, token);
1315			if (type != RIGHTPAREN){
1316			    print_error("Expected \")\"", token, type);
1317			    free_node(np);
1318			    return 0;
1319			}
1320		    }
1321		}
1322		if (type == ENDOFFILE){
1323		    print_error("Expected \"}\"", token, type);
1324		    free_node(np);
1325		    return 0;
1326		}
1327		nexttype = get_token(fp, nexttoken);
1328	    } else if (nexttype == LEFTPAREN){
1329		/* ignore the "constrained integer" for now */
1330		nexttype = get_token(fp, nexttoken);
1331		nexttype = get_token(fp, nexttoken);
1332		nexttype = get_token(fp, nexttoken);
1333	    }
1334	    break;
1335	case OCTETSTR:
1336	    strcpy(syntax, token);
1337            /* ignore the "constrained octet string" for now */
1338            if (nexttype == LEFTPAREN) {
1339                nexttype = get_token(fp, nexttoken);
1340                if (nexttype == SIZE) {
1341                    nexttype = get_token(fp, nexttoken);
1342                    if (nexttype == LEFTPAREN) {
1343                        nexttype = get_token(fp, nexttoken); /* 0..255 */
1344                        np->oct_str_len = number_value;
1345                        number_value = 0;
1346                        nexttype = get_token(fp, nexttoken); /* ) */
1347                        nexttype = get_token(fp, nexttoken); /* ) */
1348                        if (nexttype == RIGHTPAREN)
1349                        {
1350                            nexttype = get_token(fp, nexttoken);
1351                            break;
1352                        }
1353                    }
1354                }
1355                print_error("Bad syntax", token, type);
1356                free_node(np);
1357                return 0;
1358            }
1359	    break;
1360	case PARSE_OBJID:
1361	case NETADDR:
1362	case IPADDR:
1363	case PARSE_COUNTER:
1364	case PARSE_GUAGE:
1365	case PARSE_TIMETICKS:
1366	case PARSE_OPAQUE:
1367	case NUL:
1368	case LABEL:
1369	case NSAPADDRESS:
1370	case PARSE_COUNTER64:
1371	    strcpy(syntax, token);
1372	    break;
1373	default:
1374	    print_error("Bad syntax", token, type);
1375	    free_node(np);
1376	    return 0;
1377    }
1378    if (nexttype == UNITS){
1379	type = get_token(fp, token);
1380	if (type != QUOTESTRING) {
1381	    print_error("Bad DESCRIPTION", token, type);
1382	    free_node(np);
1383	    return 0;
1384	}
1385	nexttype = get_token(fp, nexttoken);
1386    }
1387    if (nexttype != ACCESS){
1388	print_error("Should be ACCESS", nexttoken, nexttype);
1389	free_node(np);
1390	return 0;
1391    }
1392    type = get_token(fp, token);
1393    if (type != READONLY && type != READWRITE && type != WRITEONLY
1394	&& type != NOACCESS && type != READCREATE){
1395	print_error("Bad access type", nexttoken, nexttype);
1396	free_node(np);
1397	return 0;
1398    }
1399
1400/*
1401-- Olivier Reisacher 95/2/14
1402*/
1403	switch(type)
1404	{
1405		case READONLY:
1406			np->access = READ_FLAG;
1407			break;
1408
1409		case READWRITE:
1410			np->access = READ_FLAG | WRITE_FLAG;
1411			break;
1412
1413		case WRITEONLY:
1414			np->access = WRITE_FLAG;
1415			break;
1416
1417		case NOACCESS:
1418			np->access = 0;
1419			break;
1420
1421		case READCREATE:
1422			np->access = READ_FLAG | CREATE_FLAG;
1423			break;
1424	}
1425
1426    type = get_token(fp, token);
1427    if (type != STATUS){
1428	print_error("Should be STATUS", token, nexttype);
1429	free_node(np);
1430	return 0;
1431    }
1432    type = get_token(fp, token);
1433    if (type != MANDATORY && type != CURRENT && type != OPTIONAL && type != OBSOLETE && type != DEPRECATED){
1434	print_error("Bad status", token, type);
1435	free_node(np);
1436	return 0;
1437    }
1438    /*
1439     * Optional parts of the OBJECT-TYPE macro
1440     */
1441    type = get_token(fp, token);
1442    while (type != EQUALS) {
1443      switch (type) {
1444        case DESCRIPTION:
1445          type = get_token(fp, token);
1446          if (type != QUOTESTRING) {
1447              print_error("Bad DESCRIPTION", token, type);
1448              free_node(np);
1449              return 0;
1450          }
1451#ifdef TEST
1452printf("Description== \"%.50s\"\n", quoted_string_buffer);
1453#endif
1454	  np->description = quoted_string_buffer;
1455	  quoted_string_buffer = (char *)malloc(MAXQUOTESTR);
1456	  if (!quoted_string_buffer){
1457	    fprintf(stderr, "malloc failed.  Exiting\n");
1458	    exit(1);
1459	  }
1460          break;
1461
1462	case REFERENCE:
1463	  type = get_token(fp, token);
1464	  if (type != QUOTESTRING) {
1465	      print_error("Bad DESCRIPTION", token, type);
1466	      free_node(np);
1467	      return 0;
1468	  }
1469	  break;
1470/*
1471-- Olivier Reisacher 95/2/14
1472*/
1473       	case INDEX:
1474		np->type = PSEUDO_TOKEN_ENTRY;
1475
1476		type = get_token(fp, token);
1477		if(type != LEFTBRACKET)
1478		{
1479			print_error("{ expected in INDEX clause",token,type);
1480			free_node(np);
1481			return 0;
1482		}
1483		type = get_token(fp, token);
1484		while(type != RIGHTBRACKET)
1485		{
1486			if(type != LABEL)
1487			{
1488				print_error("LABEL expected in INDEX clause",token,type);
1489				free_node(np);
1490				return 0;
1491			}
1492
1493
1494			(np->n_indexs)++;
1495		    	if(np->indexs == NULL)
1496			{
1497				ip = np->indexs = (struct index_list *)
1498					Malloc(sizeof(struct index_list));
1499			}
1500			else
1501			{
1502				ip->next = (struct index_list *)
1503					Malloc(sizeof(struct enum_list));
1504				ip = ip->next;
1505			}
1506			ip->next = 0;
1507			ip->tp = NULL;
1508
1509			ip->label =
1510				(char *)Malloc((unsigned)strlen(token) + 1);
1511			strcpy(ip->label, token);
1512
1513			type = get_token(fp, token);
1514
1515			switch(type)
1516			{
1517				case COMMA:
1518					type = get_token(fp, token);
1519					break;
1520
1521				case RIGHTBRACKET:
1522					break;
1523
1524				default:
1525					print_error(", or } expected in INDEX clause",token,type);
1526					free_node(np);
1527					return 0;
1528
1529			}
1530		}
1531		break;
1532
1533        case DEFVAL:
1534	case AUGMENTS:
1535	case NUM_ENTRIES:
1536          if (tossObjectIdentifier(fp) != PARSE_OBJID) {
1537              print_error("Bad Object Identifier", token, type);
1538              free_node(np);
1539              return 0;
1540          }
1541          break;
1542
1543        default:
1544          print_error("Bad format of optional clauses", token,type);
1545          free_node(np);
1546          return 0;
1547
1548      }
1549      type = get_token(fp, token);
1550    }
1551    if (type != EQUALS){
1552	print_error("Bad format", token, type);
1553	free_node(np);
1554	return 0;
1555    }
1556    length = getoid(fp, oid, 32);
1557    if (length > 1 && length <= 32){
1558	/* just take the last pair in the oid list */
1559	if (oid[length - 2].label)
1560	    strncpy(np->parent, oid[length - 2].label, MAXLABEL);
1561	strcpy(np->label, name);
1562	if (oid[length - 1].subid != -1)
1563	    np->subid = oid[length - 1].subid;
1564	else
1565	    print_error("Warning: This entry is pretty silly", np->label, type);
1566    } else {
1567	print_error("No end to oid", (char *)NULL, type);
1568	free_node(np);
1569	np = 0;
1570    }
1571    /* free oid array */
1572    for(count = 0; count < length; count++){
1573	if (oid[count].label)
1574	    free(oid[count].label);
1575	oid[count].label = 0;
1576    }
1577    return np;
1578}
1579
1580
1581/*
1582 * Parses an OBJECT GROUP macro.
1583 * Returns 0 on error.
1584 */
1585static struct node *
1586parse_objectgroup(fp, name)
1587    register FILE *fp;
1588    char *name;
1589{
1590    register int type;
1591    char token[MAXTOKEN];
1592    int count, length;
1593    struct subid oid[32];
1594    register struct node *np;
1595
1596    np = (struct node *)Malloc(sizeof(struct node));
1597    np->type = 0;
1598    np->next = 0;
1599    np->enums = 0;
1600    np->description = NULL;        /* default to an empty description */
1601
1602/*
1603-- Olivier Reisacher 95/2/14
1604*/
1605	np->access = 0;
1606	np->n_indexs = 0;
1607	np->indexs = 0;
1608
1609    type = get_token(fp, token);
1610    while (type != EQUALS) {
1611      switch (type) {
1612        case DESCRIPTION:
1613          type = get_token(fp, token);
1614          if (type != QUOTESTRING) {
1615              print_error("Bad DESCRIPTION", token, type);
1616              free_node(np);
1617              return 0;
1618          }
1619#ifdef TEST
1620printf("Description== \"%.50s\"\n", quoted_string_buffer);
1621#endif
1622	  np->description = quoted_string_buffer;
1623	  quoted_string_buffer = (char *)malloc(MAXQUOTESTR);
1624	  if (!quoted_string_buffer){
1625	    fprintf(stderr, "malloc failed.  Exiting\n");
1626	    exit(1);
1627	  }
1628          break;
1629
1630        default:
1631	  /* NOTHING */
1632	  break;
1633      }
1634      type = get_token(fp, token);
1635    }
1636    length = getoid(fp, oid, 32);
1637    if (length > 1 && length <= 32){
1638	/* just take the last pair in the oid list */
1639	if (oid[length - 2].label)
1640	    strncpy(np->parent, oid[length - 2].label, MAXLABEL);
1641	strcpy(np->label, name);
1642	if (oid[length - 1].subid != -1)
1643	    np->subid = oid[length - 1].subid;
1644	else
1645	    print_error("Warning: This entry is pretty silly", np->label, type);
1646    } else {
1647	print_error("No end to oid", (char *)NULL, type);
1648	free_node(np);
1649	np = 0;
1650    }
1651    /* free oid array */
1652    for(count = 0; count < length; count++){
1653	if (oid[count].label)
1654	    free(oid[count].label);
1655	oid[count].label = 0;
1656    }
1657    return np;
1658}
1659
1660/*
1661 * Parses a NOTIFICATION-TYPE macro.
1662 * Returns 0 on error.
1663 */
1664static struct node *
1665parse_notificationDefinition(fp, name)
1666    register FILE *fp;
1667    char *name;
1668{
1669    register int type;
1670    char token[MAXTOKEN];
1671    int count, length;
1672    struct subid oid[32];
1673    register struct node *np;
1674
1675    np = (struct node *)Malloc(sizeof(struct node));
1676    np->type = 0;
1677    np->next = 0;
1678    np->enums = 0;
1679    np->description = NULL;        /* default to an empty description */
1680/*
1681-- Olivier Reisacher 95/2/14
1682*/
1683	np->access = 0;
1684	np->n_indexs = 0;
1685	np->indexs = 0;
1686
1687    type = get_token(fp, token);
1688    while (type != EQUALS) {
1689      switch (type) {
1690        case DESCRIPTION:
1691          type = get_token(fp, token);
1692          if (type != QUOTESTRING) {
1693              print_error("Bad DESCRIPTION", token, type);
1694              free_node(np);
1695              return 0;
1696          }
1697#ifdef TEST
1698printf("Description== \"%.50s\"\n", quoted_string_buffer);
1699#endif
1700	  np->description = quoted_string_buffer;
1701	  quoted_string_buffer = (char *)malloc(MAXQUOTESTR);
1702	  if (!quoted_string_buffer){
1703	    fprintf(stderr, "malloc failed.  Exiting\n");
1704	    exit(1);
1705	  }
1706          break;
1707
1708        default:
1709	  /* NOTHING */
1710	  break;
1711      }
1712      type = get_token(fp, token);
1713    }
1714    length = getoid(fp, oid, 32);
1715    if (length > 1 && length <= 32){
1716	/* just take the last pair in the oid list */
1717	if (oid[length - 2].label)
1718	    strncpy(np->parent, oid[length - 2].label, MAXLABEL);
1719	strcpy(np->label, name);
1720	if (oid[length - 1].subid != -1)
1721	    np->subid = oid[length - 1].subid;
1722	else
1723	    print_error("Warning: This entry is pretty silly", np->label, type);
1724    } else {
1725	print_error("No end to oid", (char *)NULL, type);
1726	free_node(np);
1727	np = 0;
1728    }
1729    /* free oid array */
1730    for(count = 0; count < length; count++){
1731	if (oid[count].label)
1732	    free(oid[count].label);
1733	oid[count].label = 0;
1734    }
1735    return np;
1736}
1737
1738/*
1739 * Parses a compliance macro
1740 * Returns 0 on error.
1741 */
1742static struct node *
1743parse_compliance(fp, name)
1744    register FILE *fp;
1745    char *name;
1746{
1747    register int type;
1748    char token[MAXTOKEN];
1749    int count, length;
1750    struct subid oid[32];
1751    register struct node *np;
1752
1753    np = (struct node *)Malloc(sizeof(struct node));
1754    np->type = 0;
1755    np->next = 0;
1756    np->enums = 0;
1757    np->description = NULL;        /* default to an empty description */
1758/*
1759-- Olivier Reisacher 95/2/14
1760*/
1761	np->access = 0;
1762	np->n_indexs = 0;
1763	np->indexs = 0;
1764
1765    type = get_token(fp, token);
1766    while (type != EQUALS) {
1767	type = get_token(fp, token);
1768    }
1769    length = getoid(fp, oid, 32);
1770    if (length > 1 && length <= 32){
1771	/* just take the last pair in the oid list */
1772	if (oid[length - 2].label)
1773	    strncpy(np->parent, oid[length - 2].label, MAXLABEL);
1774	strcpy(np->label, name);
1775	if (oid[length - 1].subid != -1)
1776	    np->subid = oid[length - 1].subid;
1777	else
1778	    print_error("Warning: This entry is pretty silly", np->label, type);
1779    } else {
1780	print_error("No end to oid", (char *)NULL, type);
1781	free_node(np);
1782	np = 0;
1783    }
1784    /* free oid array */
1785    for(count = 0; count < length; count++){
1786	if (oid[count].label)
1787	    free(oid[count].label);
1788	oid[count].label = 0;
1789    }
1790    return np;
1791}
1792
1793
1794
1795/*
1796 * Parses a module identity macro
1797 * Returns 0 on error.
1798 */
1799static struct node *
1800parse_moduleIdentity(fp, name)
1801    register FILE *fp;
1802    char *name;
1803{
1804    register int type;
1805    char token[MAXTOKEN];
1806    int count, length;
1807    struct subid oid[32];
1808    register struct node *np;
1809
1810    np = (struct node *)Malloc(sizeof(struct node));
1811    np->type = 0;
1812    np->next = 0;
1813    np->enums = 0;
1814    np->description = NULL;        /* default to an empty description */
1815/*
1816-- Olivier Reisacher 95/2/14
1817*/
1818	np->n_indexs = 0;
1819	np->indexs = 0;
1820	np->access = 0;
1821
1822    type = get_token(fp, token);
1823    while (type != EQUALS) {
1824	type = get_token(fp, token);
1825    }
1826    length = getoid(fp, oid, 32);
1827    if (length > 1 && length <= 32){
1828	/* just take the last pair in the oid list */
1829	if (oid[length - 2].label)
1830	    strncpy(np->parent, oid[length - 2].label, MAXLABEL);
1831	strcpy(np->label, name);
1832	if (oid[length - 1].subid != -1)
1833	    np->subid = oid[length - 1].subid;
1834	else
1835	    print_error("Warning: This entry is pretty silly", np->label, type);
1836    } else {
1837	print_error("No end to oid", (char *)NULL, type);
1838	free_node(np);
1839	np = 0;
1840    }
1841    /* free oid array */
1842    for(count = 0; count < length; count++){
1843	if (oid[count].label)
1844	    free(oid[count].label);
1845	oid[count].label = 0;
1846    }
1847    return np;
1848}
1849
1850int parse_mib_header(fp, name)
1851    register FILE *fp;
1852    char *name;
1853{
1854    int type = DEFINITIONS;
1855    char token[MAXTOKEN];
1856
1857#ifdef TRACE_PROC
1858printf("parse_mib_header() invoked\n");
1859#endif
1860
1861    /* This probably isn't good enough.  If there is no
1862       imports clause we can't go around waiting (forever) for a semicolon.
1863       We need to check for semi following an EXPORTS clause or an IMPORTS
1864       clause of both.  Look for BEGIN; in my initial MIBs to see those
1865       that I needed to hack to get to parse because they didn't have
1866       an IMPORTS or and EXPORTS clause.
1867       */
1868    while(type != SEMI){
1869	type = get_token(fp, token);
1870    }
1871    return 1;
1872}
1873
1874
1875/*
1876-- Olivier Reisacher 95/2/14
1877*/
1878void parse_init()
1879{
1880	hash_init();
1881	memset((void *) tclist, 0, 64 * sizeof(struct tc));
1882}
1883
1884
1885/*
1886 * Parses a mib file and returns a linked list of nodes found in the file.
1887 * Returns NULL on error.
1888 */
1889/*
1890-- Olivier Reisacher 95/2/14
1891static struct node *
1892*/
1893struct node *
1894parse(fp)
1895    FILE *fp;
1896{
1897    char token[MAXTOKEN];
1898    char name[MAXTOKEN];
1899    int	type = 1;
1900#define BETWEEN_MIBS  	      1
1901#define IN_MIB                2
1902    int state = BETWEEN_MIBS;
1903    struct node *np, *root = NULL;
1904
1905#ifdef TRACE_PROC
1906printf("parse() invoked\n");
1907#endif
1908
1909/*
1910-- Olivier Reisacher 95/2/14
1911    hash_init();
1912*/
1913    Line = 1;
1914
1915    quoted_string_buffer = (char *)malloc(MAXQUOTESTR);  /* free this later */
1916    if (!quoted_string_buffer){
1917      fprintf(stderr, "malloc failed.  Exiting\n");
1918      exit(1);
1919    }
1920
1921/*
1922-- Olivier Reisacher 95/2/14
1923    bzero(tclist, 64 * sizeof(struct tc));
1924*/
1925
1926    while(type != ENDOFFILE){
1927	type = get_token(fp, token);
1928skipget:
1929	if (type == END){
1930	    if (state != IN_MIB){
1931		print_error("Error, end before start of MIB.", (char *)NULL, type);
1932		return NULL;
1933	    }
1934	    state = BETWEEN_MIBS;
1935	    continue;
1936	} else if (type != LABEL){
1937	    if (type == ENDOFFILE){
1938		return root;
1939	    }
1940	    print_error(token, "is a reserved word", type);
1941	    return NULL;
1942	}
1943	strncpy(name, token, MAXTOKEN);
1944	type = get_token(fp, token);
1945	if (type == DEFINITIONS){
1946	    if (state != BETWEEN_MIBS){
1947		print_error("Error, nested MIBS.", (char *)NULL, type);
1948		return NULL;
1949	    }
1950	    state = IN_MIB;
1951	    if (!parse_mib_header(fp, name)){
1952		print_error("Bad parse of module header", (char *)NULL, type);
1953		return NULL;
1954	    }
1955       } else if (type == OBJTYPE){
1956	    if (root == NULL){
1957		/* first link in chain */
1958		np = root = parse_objecttype(fp, name);
1959		if (np == NULL){
1960		    print_error("Bad parse of object type", (char *)NULL,
1961				type);
1962		    return NULL;
1963		}
1964	    } else {
1965		np->next = parse_objecttype(fp, name);
1966		if (np->next == NULL){
1967		    print_error("Bad parse of objecttype", (char *)NULL,
1968				type);
1969		    return NULL;
1970		}
1971	    }
1972	    /* now find end of chain */
1973	    while(np->next)
1974		np = np->next;
1975	} else if (type == OBJGROUP){
1976	    if (root == NULL){
1977		/* first link in chain */
1978		np = root = parse_objectgroup(fp, name);
1979		if (np == NULL){
1980		    print_error("Bad parse of object group", (char *)NULL,
1981				type);
1982		    return NULL;
1983		}
1984	    } else {
1985		np->next = parse_objectgroup(fp, name);
1986		if (np->next == NULL){
1987		    print_error("Bad parse of objectgroup", (char *)NULL,
1988				type);
1989		    return NULL;
1990		}
1991	    }
1992	    /* now find end of chain */
1993	    while(np->next)
1994		np = np->next;
1995	} else if (type == NOTIFTYPE){
1996	    if (root == NULL){
1997		/* first link in chain */
1998		np = root = parse_notificationDefinition(fp, name);
1999		if (np == NULL){
2000		    print_error("Bad parse of notification definition",
2001				(char *)NULL, type);
2002		    return NULL;
2003		}
2004	    } else {
2005		np->next = parse_notificationDefinition(fp, name);
2006		if (np->next == NULL){
2007		    print_error("Bad parse of notification definition",
2008				(char *)NULL, type);
2009		    return NULL;
2010		}
2011	    }
2012	    /* now find end of chain */
2013	    while(np->next)
2014		np = np->next;
2015	} else if (type == COMPLIANCE){
2016	    if (root == NULL){
2017		/* first link in chain */
2018		np = root = parse_compliance(fp, name);
2019		if (np == NULL){
2020		    print_error("Bad parse of module compliance", (char *)NULL,
2021				type);
2022		    return NULL;
2023		}
2024	    } else {
2025		np->next = parse_compliance(fp, name);
2026		if (np->next == NULL){
2027		    print_error("Bad parse of module compliance", (char *)NULL,
2028				type);
2029		    return NULL;
2030		}
2031	    }
2032	    /* now find end of chain */
2033	    while(np->next)
2034		np = np->next;
2035	} else if (type == MODULEIDENTITY){
2036	    if (root == NULL){
2037		/* first link in chain */
2038		np = root = parse_moduleIdentity(fp, name);
2039		if (np == NULL){
2040		    print_error("Bad parse of module identity", (char *)NULL,
2041				type);
2042		    return NULL;
2043		}
2044	    } else {
2045		np->next = parse_moduleIdentity(fp, name);
2046		if (np->next == NULL){
2047		    print_error("Bad parse of module identity", (char *)NULL,
2048				type);
2049		    return NULL;
2050		}
2051	    }
2052	    /* now find end of chain */
2053	    while(np->next)
2054		np = np->next;
2055	} else if (type == PARSE_OBJID){
2056	    if (root == NULL){
2057		/* first link in chain */
2058		np = root = parse_objectid(fp, name);
2059		if (np == NULL){
2060		    print_error("Bad parse of object id", (char *)NULL, type);
2061		    return NULL;
2062		}
2063	    } else {
2064		np->next = parse_objectid(fp, name);
2065		if (np->next == NULL){
2066		    print_error("Bad parse of object type", (char *)NULL,
2067				type);
2068		    return NULL;
2069		}
2070	    }
2071	    /* now find end of chain */
2072	    while(np->next)
2073		np = np->next;
2074	} else if (type == EQUALS){
2075	    if (!parse_asntype(fp, name, &type, token)){
2076		print_error("Bad parse of ASN type definition.", (char*)NULL, EQUALS);
2077		return NULL;
2078	    }
2079	    goto skipget;
2080	} else if (type == TRAPTYPE){ /* Jerry Yeung 6-6-96 */
2081	    if(!parse_traptype(fp,name)){
2082		 print_error("Bad parse of TRAP type",(char*)NULL,type);
2083		 return NULL;
2084	    }
2085	} else if (type == ENDOFFILE){
2086	    break;
2087	} else {
2088	    print_error("Bad operator", (char *)NULL, type);
2089	    return NULL;
2090	}
2091    }
2092#ifdef TEST
2093{
2094    struct enum_list *ep;
2095
2096    for(np = root; np; np = np->next){
2097	printf("%s ::= { %s %d } (%d)\n", np->label, np->parent, np->subid,
2098		np->type);
2099	if (np->enums){
2100	    printf("Enums: \n");
2101	    for(ep = np->enums; ep; ep = ep->next){
2102		printf("%s(%d)\n", ep->label, ep->value);
2103	    }
2104	}
2105    }
2106}
2107#endif /* TEST */
2108    return root;
2109}
2110
2111/*
2112 * Parses a token from the file.  The type of the token parsed is returned,
2113 * and the text is placed in the string pointed to by token.
2114 */
2115static int
2116get_token(fp, token)
2117    register FILE *fp;
2118    register char *token;
2119{
2120    static char last = ' ';
2121    register int ch;
2122    register char *cp = token;
2123    register int hash = 0;
2124    register struct tok *tp;
2125
2126    *cp = 0;
2127    ch = last;
2128    /* skip all white space */
2129    while(isspace(ch) && ch != -1){
2130	ch = getc(fp);
2131	if (ch == '\n')
2132	    Line++;
2133    }
2134    if (ch == -1) {
2135#ifdef TRACE_GET_TOKEN
2136print_error("TRACE", token, ENDOFFILE);
2137#endif
2138	return ENDOFFILE;
2139    } else if (ch == '"') {
2140	return parseQuoteString(fp, token);
2141    }
2142
2143    /*
2144     * Accumulate characters until end of token is found.  Then attempt to
2145     * match this token as a reserved word.  If a match is found, return the
2146     * type.  Else it is a label.
2147     */
2148    do {
2149	if (ch == '\n')
2150	    Line++;
2151	if (isspace(ch) || ch == '(' || ch == ')' || ch == '{' || ch == '}' ||
2152	    ch == ',' || ch == ';'){
2153	    if (!isspace(ch) && *token == 0){
2154		hash += ch;
2155		*cp++ = ch;
2156		last = ' ';
2157	    } else {
2158		last = ch;
2159	    }
2160	    *cp = '\0';
2161
2162	    for (tp = buckets[BUCKET(hash)]; tp; tp = tp->next) {
2163		if ((tp->hash == hash) && (strcmp(tp->name, token) == 0))
2164			break;
2165	    }
2166	    if (tp){
2167		if (tp->token == CONTINUE)
2168		    continue;
2169#ifdef TRACE_GET_TOKEN
2170print_error("TRACE", token, tp->token);
2171#endif
2172		return (tp->token);
2173	    }
2174
2175	    if (token[0] == '-' && token[1] == '-'){
2176		/* strip comment */
2177		if (ch != '\n'){
2178		    while ((ch = getc(fp)) != -1)
2179			if (ch == '\n'){
2180			    Line++;
2181			    break;
2182			}
2183		}
2184		if (ch == -1)
2185{
2186#ifdef TRACE_GET_TOKEN
2187print_error("TRACE", token, ENDOFFILE);
2188#endif
2189		    return ENDOFFILE;
2190}
2191		last = ch;
2192		return get_token(fp, token);
2193	    }
2194	    for(cp = token; *cp; cp++)
2195		if (!isdigit(*cp))
2196{
2197#ifdef TRACE_GET_TOKEN
2198print_error("TRACE", token, LABEL);
2199#endif
2200		    return LABEL;
2201}
2202#ifdef TRACE_GET_TOKEN
2203print_error("TRACE", token, NUMBER);
2204#endif
2205            number_value = atoi(token);   /* octet string size */
2206	    return NUMBER;
2207	} else {
2208	    hash += ch;
2209	    *cp++ = ch;
2210	    if (ch == '\n')
2211		Line++;
2212	}
2213
2214    } while ((ch = getc(fp)) != -1);
2215#ifdef TRACE_GET_TOKEN
2216print_error("TRACE", token, ENDOFFILE);
2217#endif
2218    return ENDOFFILE;
2219}
2220
2221struct tree *
2222read_mib(filename)
2223    char *filename;
2224{
2225    FILE *fp;
2226    struct node *nodes;
2227    struct tree *tree;
2228
2229    fp = fopen(filename, "r");
2230    if (fp == NULL)
2231	return NULL;
2232    nodes = parse(fp);
2233    if (!nodes){
2234	fprintf(stderr, "Mib table is bad.  Exiting\n");
2235	exit(1);
2236    }
2237    tree = build_tree(nodes);
2238    fclose(fp);
2239    return tree;
2240}
2241
2242
2243#ifdef TEST
2244main(argc, argv)
2245    int argc;
2246    char *argv[];
2247{
2248    FILE *fp;
2249    struct node *nodes;
2250    struct tree *tp;
2251
2252    fp = fopen("mib.txt", "r");
2253    if (fp == NULL){
2254	fprintf(stderr, "open failed\n");
2255	return 1;
2256    }
2257    nodes = parse(fp);
2258    if (!nodes){
2259      fprintf(stderr, "Mib table is bad. \n");
2260      return (1);
2261    }
2262    tp = build_tree(nodes);
2263    print_subtree(tp, 0);
2264    fclose(fp);
2265}
2266
2267#endif /* TEST */
2268
2269static int
2270parseQuoteString(fp, token)
2271    register FILE *fp;
2272    register char *token;
2273{
2274    register int ch;
2275    register char *ptr, *ptr1;
2276    register int len = 0;
2277
2278    ch = ' ';
2279    *token = '\0';                      /* make the token empty */
2280
2281    ptr = quoted_string_buffer;
2282    if (ptr)
2283	*ptr = 0;
2284
2285
2286    while(ch != -1) {
2287        ch = getc(fp);
2288	if (ch != '"') {
2289		if (ptr) {
2290			* ptr ++ = ch;
2291			len ++;
2292			if (len % MAXQUOTESTR == 0) {
2293				ptr1 = (char *) malloc (len + MAXQUOTESTR);
2294				if (!ptr1){
2295				  fprintf(stderr, "malloc failed.  Exiting\n");
2296				  exit(1);
2297				}
2298				memcpy (ptr1, quoted_string_buffer, len);
2299				free (quoted_string_buffer);
2300				quoted_string_buffer = ptr1;
2301				ptr = ptr1 + len;
2302			}
2303		}
2304	} else {
2305		if (ptr)
2306			* ptr = 0;
2307	}
2308	if (ch == '\n')
2309	    Line++;
2310	else if (ch == '"') {
2311#ifdef TRACE_GET_TOKEN
2312print_error("TRACE", token, QUOTESTRING);
2313#endif
2314            return QUOTESTRING;
2315        }
2316
2317    }
2318
2319#ifdef TRACE_GET_TOKEN
2320print_error("TRACE", token, NULL);
2321#endif
2322    return NULL;
2323}
2324
2325/*
2326 * This routine parses a string like  { blah blah blah } and returns PARSE_OBJID if
2327 * it is well formed, and NULL if not.
2328 */
2329static int
2330tossObjectIdentifier(fp)
2331    register FILE *fp;
2332{
2333    register int ch;
2334
2335        ch = getc(fp);
2336/*    ch = last; = ' '? */
2337    /* skip all white space */
2338    while(isspace(ch) && ch != -1){
2339        ch = getc(fp);
2340        if (ch == '\n')
2341            Line++;
2342    }
2343    if (ch != '{')
2344        return NULL;
2345
2346    while(ch != -1) {
2347        ch = getc(fp);
2348
2349        if (ch == '\n')
2350            Line++;
2351        else if (ch == '}')
2352            return PARSE_OBJID;
2353    }
2354
2355/*    last = ch;*/
2356    return NULL;
2357}
2358