]> git.sur5r.net Git - openldap/blob - libraries/libldap/schema.c
Initial incomplete and broken version.
[openldap] / libraries / libldap / schema.c
1 /*
2  * Copyright 1999 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  *
5  * schema.c:  parsing routines used by servers and clients to process
6  *      schema definitions
7  */
8
9 #include "portable.h"
10
11 #include <ac/ctype.h>
12 #include <ac/string.h>
13 #ifdef HAVE_MALLOC_H
14 #include <malloc.h>
15 #endif
16 #include <lber.h>
17 #include <ldap.h>
18 #include <ldap_schema.h>
19 #include <stdio.h>
20
21 /*
22  * When pretty printing the entities we will be appending to a buffer.
23  * Since checking for overflow, realloc'ing and checking if no error
24  * is extremely boring, we will use a pretection layer that will let
25  * us blissfully ignore the error until the end.  This layer is
26  * implemented with the help of the next type.
27  */
28
29 typedef struct safe_string {
30         char * val;
31         int size;
32         int pos;
33         int at_whsp;
34 } safe_string;
35
36 static safe_string *
37 new_safe_string(int size)
38 {
39         safe_string * ss;
40         
41         ss = malloc(sizeof(safe_string));
42         if ( !ss )
43                 return(NULL);
44         ss->size = size;
45         ss->pos = 0;
46         ss->val = malloc(size);
47         ss->at_whsp = 0;
48         if ( !ss->val ) {
49                 free(ss);
50                 return(NULL);
51         }
52         return ss;
53 }
54
55 void
56 safe_string_free(safe_string * ss)
57 {
58         if ( !ss )
59                 return;
60         ldap_memfree(ss->val);
61         ldap_memfree(ss);
62 }
63
64 static char *
65 safe_string_val(safe_string * ss)
66 {
67         return(ss->val);
68 }
69
70 static int
71 append_to_safe_string(safe_string * ss, char * s)
72 {
73         int l = strlen(s);
74         char * temp;
75
76         /*
77          * Some runaway process is trying to append to a string that
78          * overflowed and we could not extend.
79          */
80         if ( !ss->val )
81                 return -1;
82
83         /* We always make sure there is at least one position available */
84         if ( ss->pos + l >= ss->size-1 ) {
85                 ss->size *= 2;
86                 temp = realloc(ss->val, ss->size);
87                 if ( !temp ) {
88                         /* Trouble, out of memory */
89                         free(ss->val);
90                         return -1;
91                 }
92                 ss->val = temp;
93         }
94         strncpy(&ss->val[ss->pos], s, l);
95         ss->pos += l;
96         if ( ss->pos > 0 && ss->val[ss->pos-1] == ' ' )
97                 ss->at_whsp = 1;
98         else
99                 ss->at_whsp = 0;
100
101         return 0;
102 }
103
104 static int
105 print_literal(safe_string *ss, char *s)
106 {
107         return(append_to_safe_string(ss,s));
108 }
109
110 static int
111 print_whsp(safe_string *ss)
112 {
113         if ( ss->at_whsp )
114                 return(append_to_safe_string(ss,""));
115         else
116                 return(append_to_safe_string(ss," "));
117 }
118
119 static int
120 print_numericoid(safe_string *ss, char *s)
121 {
122         return(append_to_safe_string(ss,s));
123 }
124
125 /* This one is identical to print_qdescr */
126 static int
127 print_qdstring(safe_string *ss, char *s)
128 {
129         print_whsp(ss);
130         print_literal(ss,"'");
131         append_to_safe_string(ss,s);
132         print_literal(ss,"'");
133         return(print_whsp(ss));
134 }
135
136 static int
137 print_qdescr(safe_string *ss, char *s)
138 {
139         print_whsp(ss);
140         print_literal(ss,"'");
141         append_to_safe_string(ss,s);
142         print_literal(ss,"'");
143         return(print_whsp(ss));
144 }
145
146 static int
147 print_qdescrlist(safe_string *ss, char **sa)
148 {
149         char **sp;
150         int ret = 0;
151         
152         for (sp=sa; *sp; sp++) {
153                 ret = print_qdescr(ss,*sp);
154         }
155         /* If the list was empty, we return zero that is potentially
156          * incorrect, but since we will still appending things, the
157          * overflow will be detected later.  Maybe FIX.
158          */
159         return(ret);
160 }
161
162 static int
163 print_qdescrs(safe_string *ss, char **sa)
164 {
165         /* The only way to represent an empty list is as a qdescrlist
166          * so, if the list is empty we treat it as a long list.
167          * Really, this is what the syntax mandates.
168          */
169         if ( !sa[0] || ( sa[0] && sa[1] ) ) {
170                 print_whsp(ss);
171                 print_literal(ss,"(");
172                 print_qdescrlist(ss,sa);
173                 print_literal(ss,")");
174                 return(print_whsp(ss));
175         } else {
176           return(print_qdescr(ss,*sa));
177         }
178 }
179
180 static int
181 print_woid(safe_string *ss, char *s)
182 {
183         print_whsp(ss);
184         append_to_safe_string(ss,s);
185         return print_whsp(ss);
186 }
187
188 static int
189 print_oidlist(safe_string *ss, char **sa)
190 {
191         char **sp;
192
193         for (sp=sa; *(sp+1); sp++) {
194                 print_woid(ss,*sp);
195                 print_literal(ss,"$");
196         }
197         return(print_woid(ss,*sp));
198 }
199
200 static int
201 print_oids(safe_string *ss, char **sa)
202 {
203         if ( sa[0] && sa[1] ) {
204                 print_literal(ss,"(");
205                 print_oidlist(ss,sa);
206                 print_whsp(ss);
207                 return(print_literal(ss,")"));
208         } else {
209                 return(print_woid(ss,*sa));
210         }
211 }
212
213 static int
214 print_noidlen(safe_string *ss, char *s, int l)
215 {
216         char buf[64];
217         int ret;
218
219         ret = print_numericoid(ss,s);
220         if ( l ) {
221                 sprintf(buf,"{%d}",l);
222                 ret = print_literal(ss,buf);
223         }
224         return(ret);
225 }
226
227 char *
228 ldap_objectclass2str( LDAP_OBJECT_CLASS * oc )
229 {
230         safe_string * ss;
231         char * retstring;
232         
233         ss = new_safe_string(256);
234         if ( !ss )
235                 return NULL;
236
237         print_literal(ss,"(");
238         print_whsp(ss);
239
240         print_numericoid(ss, oc->oc_oid);
241         print_whsp(ss);
242
243         if ( oc->oc_names ) {
244                 print_literal(ss,"NAME");
245                 print_qdescrs(ss,oc->oc_names);
246         }
247
248         if ( oc->oc_desc ) {
249                 print_literal(ss,"DESC");
250                 print_qdstring(ss,oc->oc_desc);
251         }
252
253         if ( oc->oc_obsolete ) {
254                 print_literal(ss, "OBSOLETE");
255                 print_whsp(ss);
256         }
257
258         if ( oc->oc_sup_oids ) {
259                 print_literal(ss,"SUP");
260                 print_oids(ss,oc->oc_sup_oids);
261         }
262
263         switch (oc->oc_kind) {
264         case 0:
265                 print_literal(ss,"ABSTRACT");
266                 break;
267         case 1:
268                 print_literal(ss,"STRUCTURAL");
269                 break;
270         case 2:
271                 print_literal(ss,"AUXILIARY");
272                 break;
273         default:
274                 print_literal(ss,"KIND-UNKNOWN");
275                 break;
276         }
277         print_whsp(ss);
278         
279         if ( oc->oc_at_oids_must ) {
280                 print_literal(ss,"MUST");
281                 print_whsp(ss);
282                 print_oids(ss,oc->oc_at_oids_must);
283                 print_whsp(ss);
284         }
285
286         if ( oc->oc_at_oids_may ) {
287                 print_literal(ss,"MAY");
288                 print_whsp(ss);
289                 print_oids(ss,oc->oc_at_oids_may);
290                 print_whsp(ss);
291         }
292
293         print_whsp(ss);
294         print_literal(ss,")");
295
296         retstring = safe_string_val(ss);
297         safe_string_free(ss);
298         return(retstring);
299 }
300
301 char *
302 ldap_attributetype2str( LDAP_ATTRIBUTE_TYPE * at )
303 {
304         safe_string * ss;
305         char * retstring;
306         
307         ss = new_safe_string(256);
308         if ( !ss )
309                 return NULL;
310
311         print_literal(ss,"(");
312         print_whsp(ss);
313
314         print_numericoid(ss, at->at_oid);
315         print_whsp(ss);
316
317         if ( at->at_names ) {
318                 print_literal(ss,"NAME");
319                 print_qdescrs(ss,at->at_names);
320         }
321
322         if ( at->at_desc ) {
323                 print_literal(ss,"DESC");
324                 print_qdstring(ss,at->at_desc);
325         }
326
327         if ( at->at_obsolete ) {
328                 print_literal(ss, "OBSOLETE");
329                 print_whsp(ss);
330         }
331
332         if ( at->at_sup_oid ) {
333                 print_literal(ss,"SUP");
334                 print_woid(ss,at->at_sup_oid);
335         }
336
337         if ( at->at_equality_oid ) {
338                 print_literal(ss,"EQUALITY");
339                 print_woid(ss,at->at_equality_oid);
340         }
341
342         if ( at->at_ordering_oid ) {
343                 print_literal(ss,"ORDERING");
344                 print_woid(ss,at->at_ordering_oid);
345         }
346
347         if ( at->at_substr_oid ) {
348                 print_literal(ss,"SUBSTR");
349                 print_woid(ss,at->at_substr_oid);
350         }
351
352         if ( at->at_syntax_oid ) {
353                 print_literal(ss,"SYNTAX");
354                 print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
355         }
356
357         if ( at->at_single_value ) {
358                 print_literal(ss,"SINGLE-VALUE");
359                 print_whsp(ss);
360         }
361
362         if ( at->at_collective ) {
363                 print_literal(ss,"COLLECTIVE");
364                 print_whsp(ss);
365         }
366
367         if ( at->at_no_user_mod ) {
368                 print_literal(ss,"NO-USER-MODIFICATION");
369                 print_whsp(ss);
370         }
371
372         if ( at->at_usage ) {
373                 print_literal(ss,"USAGE");
374                 print_whsp(ss);
375                 switch (at->at_usage) {
376                 case 1:
377                         print_literal(ss,"directoryOperation");
378                         break;
379                 case 2:
380                         print_literal(ss,"distributedOperation");
381                         break;
382                 case 3:
383                         print_literal(ss,"dSAOperation");
384                         break;
385                 default:
386                         print_literal(ss,"UNKNOWN");
387                         break;
388                 }
389         }
390         
391         print_whsp(ss);
392         print_literal(ss,")");
393
394         retstring = safe_string_val(ss);
395         safe_string_free(ss);
396         return(retstring);
397 }
398
399 /*
400  * This is ripped from servers/slapd/charray.c that should be promoted
401  * to -lldap or something so that it is used everywhere.
402  */
403 static void
404 charray_free( char **array )
405 {
406         char    **a;
407
408         if ( array == NULL ) {
409                 return;
410         }
411
412         for ( a = array; *a != NULL; a++ ) {
413                 if ( *a != NULL ) {
414                         free( *a );
415                 }
416         }
417         free( (char *) array );
418 }
419
420 #define TK_NOENDQUOTE   -2
421 #define TK_OUTOFMEM     -1
422 #define TK_EOS          0
423 #define TK_UNEXPCHAR    1
424 #define TK_BAREWORD     2
425 #define TK_QDSTRING     3
426 #define TK_LEFTPAREN    4
427 #define TK_RIGHTPAREN   5
428 #define TK_DOLLAR       6
429 #define TK_QDESCR       TK_QDSTRING
430
431 struct token {
432   int type;
433   char *sval;
434 };
435
436 static int
437 get_token(char ** sp, char ** token_val)
438 {
439         int kind;
440         char * p;
441         char * q;
442         char * res;
443
444         switch (**sp) {
445         case '\0':
446                 kind = TK_EOS;
447                 (*sp)++;
448                 break;
449         case '(':
450                 kind = TK_LEFTPAREN;
451                 (*sp)++;
452                 break;
453         case ')':
454                 kind = TK_RIGHTPAREN;
455                 (*sp)++;
456                 break;
457         case '$':
458                 kind = TK_DOLLAR;
459                 (*sp)++;
460                 break;
461         case '\'':
462                 kind = TK_QDSTRING;
463                 (*sp)++;
464                 p = *sp;
465                 while ( **sp != '\'' && **sp != '\0' )
466                         (*sp)++;
467                 if ( **sp == '\'' ) {
468                         q = *sp;
469                         res = malloc(q-p+1);
470                         if ( !res ) {
471                                 kind = TK_OUTOFMEM;
472                         } else {
473                                 strncpy(res,p,q-p);
474                                 res[q-p] = '\0';
475                                 *token_val = res;
476                         }
477                         (*sp)++;
478                 } else {
479                         kind = TK_NOENDQUOTE;
480                 }
481                 break;
482         default:
483                 kind = TK_BAREWORD;
484                 p = *sp;
485                 while ( !isspace(**sp) && **sp != '\0' )
486                         (*sp)++;
487                 q = *sp;
488                 res = malloc(q-p+1);
489                 if ( !res ) {
490                         kind = TK_OUTOFMEM;
491                 } else {
492                         strncpy(res,p,q-p);
493                         res[q-p] = '\0';
494                         *token_val = res;
495                 }
496                 break;
497 /*              kind = TK_UNEXPCHAR; */
498 /*              break; */
499         }
500         
501         return kind;
502 }
503
504 /* Gobble optional whitespace */
505 static void
506 parse_whsp(char **sp)
507 {
508         while (isspace(**sp))
509                 (*sp)++;
510 }
511
512 /* TBC:!!
513  * General note for all parsers: to guarantee the algorithm halts they
514  * must always advance the pointer even when an error is found.  For
515  * this one is not that important since an error here is fatal at the
516  * upper layers, but it is a simple strategy that will not get in
517  * endless loops.
518  */
519
520 /* Parse a sequence of dot-separated decimal strings */
521 static char *
522 parse_numericoid(char **sp, int *code)
523 {
524         char * res;
525         char * start = *sp;
526         int len;
527
528         /* Each iteration of this loops gets one decimal string */
529         while (**sp) {
530                 if ( !isdigit(**sp) ) {
531                         /* Initial char is not a digit or char after dot is not a digit */
532                         *code = SCHEMA_ERR_NODIGIT;
533                         return NULL;
534                 }
535                 (*sp)++;
536                 while ( isdigit(**sp) )
537                         (*sp)++;
538                 if ( **sp != '.' )
539                         break;
540                 /* Otherwise, gobble the dot and loop again */
541                 (*sp)++;
542         }
543         /* At this point, *sp points at the char past the numericoid. Perfect. */
544         len = *sp - start;
545         res = malloc(len+1);
546         if (!res) {
547           *code = SCHEMA_ERR_OUTOFMEM;
548           return(NULL);
549         }
550         strncpy(res,start,len);
551         res[len] = '\0';
552         return(res);
553 }
554
555 /* Parse a qdescr or a list of them enclosed in () */
556 static char **
557 parse_qdescrs(char **sp, int *code)
558 {
559         char ** res;
560         char ** res1;
561         int kind;
562         char * sval;
563         int size;
564         int pos;
565
566         parse_whsp(sp);
567         kind = get_token(sp,&sval);
568         if ( kind == TK_LEFTPAREN ) {
569                 /* Let's presume there will be at least 2 entries */
570                 size = 3;
571                 res = calloc(3,sizeof(char *));
572                 if ( !res ) {
573                         *code = SCHEMA_ERR_OUTOFMEM;
574                         return NULL;
575                 }
576                 pos = 0;
577                 while (1) {
578                         parse_whsp(sp);
579                         kind = get_token(sp,&sval);
580                         if ( kind == TK_RIGHTPAREN )
581                                 break;
582                         if ( kind == TK_QDESCR ) {
583                                 if ( pos == size-2 ) {
584                                         size++;
585                                         res1 = realloc(res,size*sizeof(char *));
586                                         if ( !res1 ) {
587                                                 charray_free(res);
588                                                 *code = SCHEMA_ERR_OUTOFMEM;
589                                                 return(NULL);
590                                         }
591                                         res = res1;
592                                 }
593                                 res[pos] = sval;
594                                 pos++;
595                                 parse_whsp(sp);
596                         } else {
597                                 charray_free(res);
598                                 *code = SCHEMA_ERR_UNEXPTOKEN;
599                                 return(NULL);
600                         }
601                 }
602                 res[pos] = NULL;
603                 parse_whsp(sp);
604                 return(res);
605         } else if ( kind == TK_QDESCR ) {
606                 res = calloc(2,sizeof(char *));
607                 if ( !res ) {
608                         *code = SCHEMA_ERR_OUTOFMEM;
609                         return NULL;
610                 }
611                 res[0] = sval;
612                 res[1] = NULL;
613                 parse_whsp(sp);
614                 return res;
615         } else {
616                 *code = SCHEMA_ERR_BADNAME;
617                 return NULL;
618         }
619 }
620
621 /* Parse a woid or a $-separated list of them enclosed in () */
622 static char **
623 parse_oids(char **sp, int *code)
624 {
625         char ** res;
626         char ** res1;
627         int kind;
628         char * sval;
629         int size;
630         int pos;
631
632         /*
633          * Strictly speaking, doing this here accepts whsp before the
634          * ( at the begining of an oidlist, but his is harmless.  Also,
635          * we are very liberal in what we accept as an OID.  Maybe
636          * refine later.
637          */
638         parse_whsp(sp);
639         kind = get_token(sp,&sval);
640         if ( kind == TK_LEFTPAREN ) {
641                 /* Let's presume there will be at least 2 entries */
642                 size = 3;
643                 res = calloc(3,sizeof(char *));
644                 if ( !res ) {
645                         *code = SCHEMA_ERR_OUTOFMEM;
646                         return NULL;
647                 }
648                 pos = 0;
649                 parse_whsp(sp);
650                 kind = get_token(sp,&sval);
651                 if ( kind == TK_BAREWORD ) {
652                         res[pos] = sval;
653                         pos++;
654                 } else {
655                         *code = SCHEMA_ERR_UNEXPTOKEN;
656                         charray_free(res);
657                         return NULL;
658                 }
659                 parse_whsp(sp);
660                 while (1) {
661                         kind = get_token(sp,&sval);
662                         if ( kind == TK_RIGHTPAREN )
663                                 break;
664                         if ( kind == TK_DOLLAR ) {
665                                 parse_whsp(sp);
666                                 kind = get_token(sp,&sval);
667                                 if ( kind == TK_BAREWORD ) {
668                                         if ( pos == size-2 ) {
669                                                 size++;
670                                                 res1 = realloc(res,size*sizeof(char *));
671                                                 if ( !res1 ) {
672                                                   charray_free(res);
673                                                   *code = SCHEMA_ERR_OUTOFMEM;
674                                                   return(NULL);
675                                                 }
676                                                 res = res1;
677                                         }
678                                         res[pos] = sval;
679                                         pos++;
680                                 } else {
681                                         *code = SCHEMA_ERR_UNEXPTOKEN;
682                                         charray_free(res);
683                                         return NULL;
684                                 }
685                                 parse_whsp(sp);
686                         } else {
687                                 *code = SCHEMA_ERR_UNEXPTOKEN;
688                                 charray_free(res);
689                                 return NULL;
690                         }
691                 }
692                 res[pos] = NULL;
693                 parse_whsp(sp);
694                 return(res);
695         } else if ( kind == TK_BAREWORD ) {
696                 res = calloc(2,sizeof(char *));
697                 if ( !res ) {
698                         *code = SCHEMA_ERR_OUTOFMEM;
699                         return NULL;
700                 }
701                 res[0] = sval;
702                 res[1] = NULL;
703                 parse_whsp(sp);
704                 return res;
705         } else {
706                 *code = SCHEMA_ERR_BADNAME;
707                 return NULL;
708         }
709 }
710
711 static void
712 free_oc(LDAP_OBJECT_CLASS * oc)
713 {
714         ldap_memfree(oc->oc_oid);
715         charray_free(oc->oc_names);
716         ldap_memfree(oc->oc_desc);
717         charray_free(oc->oc_sup_oids);
718         charray_free(oc->oc_at_oids_must);
719         charray_free(oc->oc_at_oids_may);
720         ldap_memfree(oc);
721 }
722
723 LDAP_OBJECT_CLASS *
724 ldap_str2objectclass( char * s, int * code, char ** errp )
725 {
726         int kind;
727         char * ss = s;
728         char * sval;
729         int seen_name = 0;
730         int seen_desc = 0;
731         int seen_obsolete = 0;
732         int seen_sup = 0;
733         int seen_kind = 0;
734         int seen_must = 0;
735         int seen_may = 0;
736         LDAP_OBJECT_CLASS * oc;
737
738         *errp = s;
739         oc = calloc(1,sizeof(LDAP_OBJECT_CLASS));
740
741         if ( !oc ) {
742                 *code = SCHEMA_ERR_OUTOFMEM;
743                 return NULL;
744         }
745
746         kind = get_token(&ss,&sval);
747         if ( kind != TK_LEFTPAREN ) {
748                 *code = SCHEMA_ERR_NOLEFTPAREN;
749                 free_oc(oc);
750                 return NULL;
751         }
752
753         parse_whsp(&ss);
754         oc->oc_oid = parse_numericoid(&ss,code);
755         if ( !oc->oc_oid ) {
756                 *errp = ss;
757                 free_oc(oc);
758                 return NULL;
759         }
760         parse_whsp(&ss);
761
762         /*
763          * Beyond this point we will be liberal an accept the items
764          * in any order.
765          */
766         while (1) {
767                 kind = get_token(&ss,&sval);
768                 switch (kind) {
769                 case TK_EOS:
770                         *code = SCHEMA_ERR_NORIGHTPAREN;
771                         *errp = ss;
772                         free_oc(oc);
773                         return NULL;
774                 case TK_RIGHTPAREN:
775                         return oc;
776                 case TK_BAREWORD:
777                         if ( !strcmp(sval,"NAME") ) {
778                                 if ( seen_name ) {
779                                         *code = SCHEMA_ERR_DUPOPT;
780                                         *errp = ss;
781                                         free_oc(oc);
782                                         return(NULL);
783                                 }
784                                 seen_name = 1;
785                                 oc->oc_names = parse_qdescrs(&ss,code);
786                                 if ( !oc->oc_names ) {
787                                         if ( *code != SCHEMA_ERR_OUTOFMEM )
788                                                 *code = SCHEMA_ERR_BADNAME;
789                                         *errp = ss;
790                                         free_oc(oc);
791                                         return NULL;
792                                 }
793                         } else if ( !strcmp(sval,"DESC") ) {
794                                 if ( seen_desc ) {
795                                         *code = SCHEMA_ERR_DUPOPT;
796                                         *errp = ss;
797                                         free_oc(oc);
798                                         return(NULL);
799                                 }
800                                 seen_desc = 1;
801                                 parse_whsp(&ss);
802                                 kind = get_token(&ss,&sval);
803                                 if ( kind != TK_QDSTRING ) {
804                                         *code = SCHEMA_ERR_UNEXPTOKEN;
805                                         *errp = ss;
806                                         free(oc);
807                                         return NULL;
808                                 }
809                                 oc->oc_desc = sval;
810                                 parse_whsp(&ss);
811                         } else if ( !strcmp(sval,"OBSOLETE") ) {
812                                 if ( seen_obsolete ) {
813                                         *code = SCHEMA_ERR_DUPOPT;
814                                         *errp = ss;
815                                         free_oc(oc);
816                                         return(NULL);
817                                 }
818                                 seen_obsolete = 1;
819                                 oc->oc_obsolete = 1;
820                                 parse_whsp(&ss);
821                         } else if ( !strcmp(sval,"SUP") ) {
822                                 if ( seen_sup ) {
823                                         *code = SCHEMA_ERR_DUPOPT;
824                                         *errp = ss;
825                                         free_oc(oc);
826                                         return(NULL);
827                                 }
828                                 seen_sup = 1;
829                                 /* Netscape DS is broken or I have not
830                                    understood the syntax. */
831                                 /* oc->oc_sup_oids = parse_oids(&ss,code); */
832                                 oc->oc_sup_oids = parse_qdescrs(&ss,code);
833                                 if ( !oc->oc_sup_oids ) {
834                                         *errp = ss;
835                                         free_oc(oc);
836                                         return NULL;
837                                 }
838                         } else if ( !strcmp(sval,"ABSTRACT") ) {
839                                 if ( seen_kind ) {
840                                         *code = SCHEMA_ERR_DUPOPT;
841                                         *errp = ss;
842                                         free_oc(oc);
843                                         return(NULL);
844                                 }
845                                 seen_kind = 1;
846                                 oc->oc_kind = 0;
847                                 parse_whsp(&ss);
848                         } else if ( !strcmp(sval,"STRUCTURAL") ) {
849                                 if ( seen_kind ) {
850                                         *code = SCHEMA_ERR_DUPOPT;
851                                         *errp = ss;
852                                         free_oc(oc);
853                                         return(NULL);
854                                 }
855                                 seen_kind = 1;
856                                 oc->oc_kind = 1;
857                                 parse_whsp(&ss);
858                         } else if ( !strcmp(sval,"AUXILIARY") ) {
859                                 if ( seen_kind ) {
860                                         *code = SCHEMA_ERR_DUPOPT;
861                                         *errp = ss;
862                                         free_oc(oc);
863                                         return(NULL);
864                                 }
865                                 seen_kind = 1;
866                                 oc->oc_kind = 2;
867                                 parse_whsp(&ss);
868                         } else if ( !strcmp(sval,"MUST") ) {
869                                 if ( seen_must ) {
870                                         *code = SCHEMA_ERR_DUPOPT;
871                                         *errp = ss;
872                                         free_oc(oc);
873                                         return(NULL);
874                                 }
875                                 seen_must = 1;
876                                 oc->oc_at_oids_must = parse_oids(&ss,code);
877                                 if ( !oc->oc_at_oids_must ) {
878                                         *errp = ss;
879                                         free_oc(oc);
880                                         return NULL;
881                                 }
882                                 parse_whsp(&ss);
883                         } else if ( !strcmp(sval,"MAY") ) {
884                                 if ( seen_may ) {
885                                         *code = SCHEMA_ERR_DUPOPT;
886                                         *errp = ss;
887                                         free_oc(oc);
888                                         return(NULL);
889                                 }
890                                 seen_may = 1;
891                                 oc->oc_at_oids_may = parse_oids(&ss,code);
892                                 if ( !oc->oc_at_oids_may ) {
893                                         *errp = ss;
894                                         free_oc(oc);
895                                         return NULL;
896                                 }
897                                 parse_whsp(&ss);
898                         } else {
899                                 *code = SCHEMA_ERR_UNEXPTOKEN;
900                                 *errp = ss;
901                                 free_oc(oc);
902                                 return NULL;
903                         }
904                         break;
905                 default:
906                         *code = SCHEMA_ERR_UNEXPTOKEN;
907                         *errp = ss;
908                         free_oc(oc);
909                         return NULL;
910                 }
911         }
912 }
913
914