dtprintinfo: Coverity 88650
[oweals/cde.git] / cde / programs / dtpdm / PdmOid.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: PdmOid.c /main/12 1997/11/20 16:37:12 bill $ */
24 /*
25  * dtpdm/PdmOid.c 
26  */
27 /*
28  * (c) Copyright 1996 Digital Equipment Corporation.
29  * (c) Copyright 1996 Hewlett-Packard Company.
30  * (c) Copyright 1996 International Business Machines Corp.
31  * (c) Copyright 1996 Sun Microsystems, Inc.
32  * (c) Copyright 1996 Novell, Inc. 
33  * (c) Copyright 1996 FUJITSU LIMITED.
34  * (c) Copyright 1996 Hitachi.
35  */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include "PdmOid.h"
40
41 #include <Dt/DtNlUtils.h>
42
43 #include "PdmMsgs.h"
44
45 /*
46  * PdmOidNotify value strings
47  */
48 #define NOTIFY_EMAIL_STR "{{event-report-job-completed} electronic-mail}"
49 #define NOTIFY_NONE_STR  "{}"
50
51 /*
52  * entry type for the object identifier string map
53  */
54 typedef struct _PdmOidStringMapEntry
55 {
56     const char* string;
57     int length;
58     int msg_set;
59     int msg_number;
60     const char* default_message;
61     
62 } PdmOidStringMapEntry;
63
64 /*
65  * include the auto-generated static PdmOidStringMap
66  */
67 #include "PdmOidStrs.h"
68
69 /*
70  * PdmOid static function declarations
71  */
72 static PdmOid PdmOidParse(const char* value_string,
73                           const char** ptr_return);
74 /*
75  * PdmOidList static function declarations
76  */
77 static PdmOidList* PdmOidListParse(const char* value_string,
78                                    const char** ptr_return, int i);
79
80
81 /*
82  * PdmOidMediumSourceSize static function declarations
83  */
84 static PdmOidMediumSS* MediumSSParse(const char* value_string,
85                                      const char** ptr_return, int i);
86 static PdmOidMediumContinuousSize* MediumContinuousSizeParse(const char*,
87                                                              const char**);
88 static void MediumContinuousSizeDelete(PdmOidMediumContinuousSize* me);
89 static PdmOidMediumDiscreteSizeList* MediumDiscreteSizeListParse(const char*,
90                                                                  const char**,
91                                                                  int i);
92 static void MediumDiscreteSizeListDelete(PdmOidMediumDiscreteSizeList* list);
93
94 static Boolean ParseArea(const char* value_string,
95                          const char** ptr_return,
96                          PdmOidArea* area_return);
97 static Boolean ParseUnsignedRange(const char* value_string,
98                                   const char** ptr_return,
99                                   PdmOidUnsignedRange* range_return);
100
101 /*
102  * PdmOidTrayMediumList static function declarations
103  */
104 static PdmOidTrayMediumList* TrayMediumListParse(const char* value_string,
105                                                  const char** ptr_return,
106                                                  int i);
107
108 /*
109  * PdmOidDocumentFormat
110  */
111 static char* PdmOidDocumentFormatNext(const char* value_string,
112                                       const char** ptr_return);
113 /*
114  * misc. parsing static function declarations
115  */
116 static Boolean ParseBooleanValue(const char* value_string,
117                                  const char** ptr_return,
118                                  Boolean* boolean_return);
119 static Boolean ParseUnsignedValue(const char* value_string,
120                                   const char** ptr_return,
121                                   unsigned long* unsigned_return);
122 static Boolean ParseRealValue(const char* value_string,
123                               const char** ptr_return,
124                               float* real_return);
125 static Boolean ParseSeqEnd(
126                            const char* value_string,
127                            const char** ptr_return);
128 static Boolean ParseSeqStart(
129                              const char* value_string,
130                              const char** ptr_return);
131 static Boolean ParseUnspecifiedValue(
132                                      const char* value_string,
133                                      const char** ptr_return);
134 static int SpanToken(
135                              const char* string);
136 static int SpanWhitespace(
137                           const char* string);
138
139
140 /*
141  * ------------------------------------------------------------------------
142  * Name: PdmOidString
143  *
144  * Description:
145  *
146  *
147  * Return value:
148  *
149  */
150 const char*
151 PdmOidString(PdmOid pdm_oid)
152 {
153     /*
154      * PdmOid enum values are index values into the string map
155      */
156     return PdmOidStringMap[pdm_oid].string;
157 }
158
159 /*
160  * ------------------------------------------------------------------------
161  * Name: PdmOidMsgSet
162  *
163  * Description:
164  *
165  *
166  * Return value:
167  *
168  */
169 int
170 PdmOidMsgSet(PdmOid pdm_oid)
171 {
172     /*
173      * PdmOid enum values are index values into the string map
174      */
175     return PdmOidStringMap[pdm_oid].msg_set;
176 }
177
178 /*
179  * ------------------------------------------------------------------------
180  * Name: PdmOidMsgNum
181  *
182  * Description:
183  *
184  *
185  * Return value:
186  *
187  */
188 int
189 PdmOidMsgNum(PdmOid pdm_oid)
190 {
191     /*
192      * PdmOid enum values are index values into the string map
193      */
194     return PdmOidStringMap[pdm_oid].msg_number;
195 }
196
197 /*
198  * ------------------------------------------------------------------------
199  * Name: PdmOidDefaultMsg
200  *
201  * Description:
202  *
203  *
204  * Return value:
205  *
206  */
207 const char*
208 PdmOidDefaultMsg(PdmOid pdm_oid)
209 {
210     if(PdmOidStringMap[pdm_oid].default_message == (const char*)NULL)
211         return PdmOidStringMap[pdm_oid].string;
212     else
213         return PdmOidStringMap[pdm_oid].default_message;
214 }
215
216 /*
217  * ------------------------------------------------------------------------
218  * Name: PdmOidStringLength
219  *
220  * Description:
221  *
222  *
223  * Return value:
224  *
225  */
226 int
227 PdmOidStringLength(PdmOid pdm_oid)
228 {
229     /*
230      * PdmOid enum values are index values into the string map
231      */
232     return PdmOidStringMap[pdm_oid].length;
233 }
234
235 /*
236  * ------------------------------------------------------------------------
237  * Name: PdmOidFromString
238  *
239  * Description:
240  *
241  *
242  * Return value:
243  *
244  */
245 PdmOid
246 PdmOidFromString(const char* value)
247 {
248     if(value == (const char*)NULL)
249         return pdmoid_none;
250     else
251         return PdmOidParse(value, (const char**)NULL);
252 }
253
254 /*
255  * ------------------------------------------------------------------------
256  * Name: PdmOidParse
257  *
258  * Description:
259  *
260  *     Parse the next whitespace-delimited string from 'value_string'
261  *     updating 'ptr_return' to point to the next unparsed location in
262  *     'value_string'. 'ptr_return' can be NULL.
263  *
264  * Return value:
265  *
266  *     The corresponding PdmOid for the parsed name string.
267  *     A return value of pdmoid_none is returned if the parsed name
268  *     was not a valid oid or if no name was found.
269  *
270  */
271 static PdmOid
272 PdmOidParse(const char* value_string,
273             const char** ptr_return)
274 {
275     const char* ptr;
276     int length;
277     int i;
278
279     /*
280      * skip leading whitespace
281      */
282     ptr = value_string + SpanWhitespace(value_string);
283     /*
284      * get the whitespace-delimited token length
285      */
286     length = SpanToken(ptr);
287     /*
288      * match the oid string in the map
289      */
290     for(i = 0; i < PdmOidStringMapCount; i++)
291         if(length == PdmOidStringMap[i].length)
292             if(strncmp(ptr, PdmOidStringMap[i].string, length) == 0)
293                 break;
294     if(i == PdmOidStringMapCount)
295         i =  pdmoid_none;
296     /*
297      * update the return pointer and return
298      */
299     if(ptr_return != (const char**)NULL)
300         *ptr_return = ptr+length;
301     return i;
302 }
303
304 /*
305  * ------------------------------------------------------------------------
306  * Name: PdmOidListNew
307  *
308  * Description:
309  *
310  *
311  * Return value:
312  *
313  *     
314  *
315  */
316 PdmOidList*
317 PdmOidListNew(const char* value_string)
318 {
319     if(value_string == (const char*)NULL)
320         return (PdmOidList*)NULL;
321     else
322     {
323         const char* ptr;
324         return PdmOidListParse(value_string, &ptr, 0);
325     }
326 }
327
328
329 /*
330  * ------------------------------------------------------------------------
331  * Name: PdmOidListParse
332  *
333  * Description:
334  *
335  *     'ptr_return' *cannot* be NULL.
336  *
337  * Return value:
338  *
339  *     
340  *
341  */
342 static PdmOidList*
343 PdmOidListParse(const char* value_string,
344                 const char** ptr_return, int i)
345 {
346     PdmOid oid = pdmoid_none;
347     PdmOidList* list;
348     /*
349      * parse the next valid oid out of the value string
350      */
351     ptr_return = &value_string;
352     while(**ptr_return != '\0' &&
353           (oid = PdmOidParse(*ptr_return, ptr_return)) == pdmoid_none);
354     if(oid == pdmoid_none)
355     {
356         /*
357          * end of value string; allocate the list structure
358          */
359         list = (PdmOidList*)XtCalloc(1, sizeof(PdmOidList));
360         list->count = i;
361         list->list = (PdmOid*)XtCalloc(i, sizeof(PdmOid));
362     }
363     else
364     {
365         /*
366          * recurse
367          */
368         list = PdmOidListParse(*ptr_return, ptr_return, i+1);
369         /*
370          * set the oid in the list
371          */
372         list->list[i] = oid;
373     }
374     /*
375      * return
376      */
377     return list;
378 }
379
380 /*
381  * ------------------------------------------------------------------------
382  * Name: PdmOidListHasOid
383  *
384  * Description:
385  *
386  *     
387  *
388  * Return value:
389  *
390  *     
391  *
392  */
393 Boolean
394 PdmOidListHasOid(const PdmOidList* list, PdmOid oid)
395 {
396     int i;
397     if(list != (PdmOidList*)NULL)
398         for(i = 0; i < list->count; i++)
399             if(list->list[i] == oid)
400                 return 1;
401     return 0;
402 }
403
404 /*
405  * ------------------------------------------------------------------------
406  * Name: PdmOidListGetIndex
407  *
408  * Description:
409  *
410  *     
411  *
412  * Return value:
413  *
414  *     
415  *
416  */
417 int
418 PdmOidListGetIndex(const PdmOidList* list, PdmOid oid)
419 {
420     int i;
421     if(list != (PdmOidList*)NULL)
422         for(i = 0; i < list->count; i++)
423             if(list->list[i] == oid)
424                 return i;
425     return -1;
426 }
427
428 /*
429  * ------------------------------------------------------------------------
430  * Name: PdmOidListDelete
431  *
432  * Description:
433  *
434  *     
435  *
436  * Return value:
437  *
438  *     
439  *
440  */
441 void
442 PdmOidListDelete(PdmOidList* list)
443 {
444     if(list != (PdmOidList*)NULL)
445     {
446         XtFree((char*)list->list);
447         XtFree((char*)list);
448     }
449 }
450
451 /*
452  * ------------------------------------------------------------------------
453  * Name: PdmOidLinkedListNew
454  *
455  * Description:
456  *
457  *
458  * Return value:
459  *
460  */
461 PdmOidLinkedList*
462 PdmOidLinkedListNew(void)
463 {
464     return (PdmOidLinkedList*)XtCalloc(1, sizeof(PdmOidLinkedList));
465 }
466
467 /*
468  * ------------------------------------------------------------------------
469  * Name: PdmOidLinkedListDelete
470  *
471  * Description:
472  *
473  *
474  * Return value:
475  *
476  */
477 void
478 PdmOidLinkedListDelete(PdmOidLinkedList* me)
479 {
480     if(me != (PdmOidLinkedList*)NULL)
481     {
482         while(me->head)
483         {
484             me->current = me->head;
485             me->head = me->current->next;
486             XtFree((char*)me->current);
487         }
488         XtFree((char*)me);
489     }
490 }
491
492 /*
493  * ------------------------------------------------------------------------
494  * Name: PdmOidLinkedListGetOid
495  *
496  * Description:
497  *
498  *
499  * Return value:
500  *
501  */
502 PdmOid
503 PdmOidLinkedListGetOid(PdmOidLinkedList* me, int i)
504 {
505     if(i < 0 || i >= me->count) return pdmoid_none;
506     me->current = me->head;
507     while(i--) me->current = me->current->next;
508     return me->current->oid;
509 }
510
511 /*
512  * ------------------------------------------------------------------------
513  * Name: PdmOidLinkedListAddOid
514  *
515  * Description:
516  *
517  *
518  * Return value:
519  *
520  */
521 void
522 PdmOidLinkedListAddOid(PdmOidLinkedList* me, PdmOid oid)
523 {
524     me->current = (PdmOidNode)XtCalloc(1, sizeof(struct PdmOidNodeStruct));
525     me->current->oid = oid;
526     ++me->count;
527     if(me->tail)
528     {
529         me->tail->next = me->current;
530         me->tail = me->current;
531     }
532     else
533         me->head = me->tail = me->current;
534 }
535
536 /*
537  * ------------------------------------------------------------------------
538  * Name: PdmOidLinkedListGetIndex
539  *
540  * Description:
541  *
542  *
543  * Return value:
544  *
545  */
546 int
547 PdmOidLinkedListGetIndex(PdmOidLinkedList* me, PdmOid oid)
548 {
549     int i = 0;
550     me->current = me->head;
551     while(me->current)
552         if(me->current->oid == oid)
553             return i;
554         else
555         {
556             ++i;
557             me->current = me->current->next;
558         }
559     return -1;
560 }
561
562 /*
563  * ------------------------------------------------------------------------
564  * Name: PdmOidLinkedListHasOid
565  *
566  * Description:
567  *
568  *
569  * Return value:
570  *
571  */
572 Boolean
573 PdmOidLinkedListHasOid(PdmOidLinkedList* me,
574                        PdmOid oid)
575 {
576     me->current = me->head;
577     while(me->current)
578         if(me->current->oid == oid)
579             return True;
580         else
581             me->current = me->current->next;
582     return False;
583 }
584                        
585 /*
586  * ------------------------------------------------------------------------
587  * Name: PdmOidLinkedListFirstOid
588  *
589  * Description:
590  *
591  *
592  * Return value:
593  *
594  */
595 PdmOid
596 PdmOidLinkedListFirstOid(PdmOidLinkedList* me)
597 {
598     if(me->current = me->head)
599         return me->current->oid;
600     else
601         return pdmoid_none;
602 }
603
604 /*
605  * ------------------------------------------------------------------------
606  * Name: PdmOidLinkedListNextOid
607  *
608  * Description:
609  *
610  *
611  * Return value:
612  *
613  */
614 PdmOid
615 PdmOidLinkedListNextOid(PdmOidLinkedList* me)
616 {
617     if(me->current ? me->current = me->current->next : False)
618         return me->current->oid;
619     else
620         return pdmoid_none;
621 }
622
623 /*
624  * ------------------------------------------------------------------------
625  * Name: PdmOidLocalString
626  *
627  * Description:
628  *
629  *
630  * Return value:
631  *
632  */
633 const char*
634 PdmOidLocalString(PdmOid pdm_oid)
635 {
636     /*
637      * PdmOid enum values are index values into the string map
638      */
639     if(PdmOidStringMap[pdm_oid].msg_set != -1)
640     {
641         return DTPDM_GETMESSAGE(PdmOidStringMap[pdm_oid].msg_set,
642                                 PdmOidStringMap[pdm_oid].msg_number,
643                                 PdmOidStringMap[pdm_oid].string);
644     }
645     else
646         return PdmOidStringMap[pdm_oid].string;
647 }
648
649 /*
650  * ------------------------------------------------------------------------
651  * Name: PdmOidMediumSSNew
652  *
653  * Description:
654  *
655  *     
656  *
657  * Return value:
658  *
659  *     
660  *
661  */
662 PdmOidMediumSS*
663 PdmOidMediumSSNew(const char* value_string)
664 {
665     if(value_string == (const char*)NULL)
666         return (PdmOidMediumSS*)NULL;
667     else
668     {
669         const char* ptr;
670         return MediumSSParse(value_string, &ptr, 0);
671     }
672 }
673
674 /*
675  * ------------------------------------------------------------------------
676  * Name: MediumSSParse
677  *
678  * Description:
679  *
680  *     'ptr_return' *cannot* be NULL.
681  *     
682  *
683  * Return value:
684  *
685  *     
686  *
687  */
688 static PdmOidMediumSS*
689 MediumSSParse(const char* value_string,
690                     const char** ptr_return, int i)
691 {
692     PdmOidMediumSS* medium_ss;
693     PdmOidMediumSourceSize mss;
694     /*
695      * check for the start of a new MediumSourceSize sequence
696      */
697     if(ParseSeqStart(value_string, ptr_return))
698     {
699         /*
700          * check for an unspecified tray value
701          */
702         if(ParseUnspecifiedValue(*ptr_return, ptr_return))
703             mss.input_tray = pdmoid_unspecified;
704         else
705         {
706             /*
707              * parse out the input tray
708              */
709             mss.input_tray = PdmOidParse(*ptr_return, ptr_return);
710         }
711         /*
712          * attempt to parse a Continuous MediumSize sequence
713          */
714         mss.ms.continuous_size =
715             MediumContinuousSizeParse(*ptr_return, ptr_return);
716         if(mss.ms.continuous_size != (PdmOidMediumContinuousSize*)NULL)
717         {
718             mss.mstag = PdmOidMediumSS_CONTINUOUS;
719         }
720         else
721         {
722             /*
723              * not continuous, try Discrete MediumSize
724              */
725             mss.ms.discrete =
726                 MediumDiscreteSizeListParse(*ptr_return, ptr_return, 0);
727             if(mss.ms.discrete == (PdmOidMediumDiscreteSizeList*)NULL)
728             {
729                 /*
730                  * syntax error (MediumDiscreteSizeListParse reports error)
731                  */
732                 return NULL;
733             }
734             mss.mstag = PdmOidMediumSS_DISCRETE;
735         }
736         /*
737          * parse out the MediumSourceSize sequence end
738          */
739         if(!ParseSeqEnd(*ptr_return, ptr_return))
740         {
741             /*
742              * syntax error
743              */
744             fprintf(stderr, "%s\n", PDM_MSG_WARN_MSS);
745             return NULL;
746         }
747         /*
748          * recurse to parse the next MediumSourceSize sequence
749          */
750         medium_ss = MediumSSParse(*ptr_return, ptr_return, i+1);
751         if(medium_ss == (PdmOidMediumSS*)NULL)
752         {
753             /*
754              * syntax error - clean up and return
755              */
756             switch(mss.mstag)
757             {
758             case PdmOidMediumSS_CONTINUOUS:
759                 MediumContinuousSizeDelete(mss.ms.continuous_size);
760                 break;
761             case PdmOidMediumSS_DISCRETE:
762                 MediumDiscreteSizeListDelete(mss.ms.discrete);
763                 break;
764             }
765             return NULL;
766         }
767         /*
768          * copy the current MediumSourceSize into the array
769          */
770         memmove((medium_ss->mss)+i, &mss, sizeof(PdmOidMediumSourceSize));
771     }
772     else
773     {
774         /*
775          * MediumSourceSize sequence start not found
776          */
777         if(**ptr_return == '\0')
778         {
779             /*
780              * end of value string; allocate the MediumSS structure
781              */
782             medium_ss = (PdmOidMediumSS*)XtCalloc(1, sizeof(PdmOidMediumSS));
783             medium_ss->count = i;
784             medium_ss->mss = (PdmOidMediumSourceSize*)
785                 XtCalloc(i, sizeof(PdmOidMediumSourceSize));
786         }
787         else
788         {
789             /*
790              * syntax error
791              */
792             fprintf(stderr, "%s\n", PDM_MSG_WARN_MSS);
793             return NULL;
794         }
795     }
796     return medium_ss;
797 }
798
799 /*
800  * ------------------------------------------------------------------------
801  * Name: PdmOidMediumSSDelete
802  *
803  * Description:
804  *
805  *     
806  *
807  * Return value:
808  *
809  *     
810  *
811  */
812 void
813 PdmOidMediumSSDelete(PdmOidMediumSS* me)
814 {
815     if(me != (PdmOidMediumSS*)NULL)
816     {
817         int i;
818         for(i = 0; i < me->count; i++)
819         {
820             switch((me->mss)[i].mstag)
821             {
822             case PdmOidMediumSS_CONTINUOUS:
823                 MediumContinuousSizeDelete((me->mss)[i].ms.continuous_size);
824                 break;
825             case PdmOidMediumSS_DISCRETE:
826                 MediumDiscreteSizeListDelete((me->mss)[i].ms.discrete);
827                 break;
828             }
829         }
830         XtFree((char*)me);
831     }
832 }
833
834 /*
835  * ------------------------------------------------------------------------
836  * Name: PdmOidMediumSSGetAllSizes
837  *
838  * Description:
839  *
840  *     
841  *
842  * Return value:
843  *
844  *     
845  *
846  */
847 PdmOidLinkedList*
848 PdmOidMediumSSGetAllSizes(PdmOidMediumSS* me)
849 {
850     PdmOidLinkedList* all_sizes;
851     int i_mss, i_ds;
852     PdmOidMediumDiscreteSizeList* ds_list;
853     PdmOid page_size;
854     
855     if(me == (PdmOidMediumSS*)NULL)
856         return (PdmOidLinkedList*)NULL;
857
858     all_sizes = PdmOidLinkedListNew();
859     /*
860      * loop through the MediumSourceSizes
861      */
862     for(i_mss = 0; i_mss < me->count; i_mss++)
863     {
864         switch((me->mss)[i_mss].mstag)
865         {
866         case PdmOidMediumSS_DISCRETE:
867             /*
868              * add unique discrete sizes to the size list
869              */
870             ds_list =  (me->mss)[i_mss].ms.discrete;
871             for(i_ds = 0; i_ds < ds_list->count; i_ds++)
872             {
873                 page_size = (ds_list->list)[i_ds].page_size;
874                 if(page_size == pdmoid_none)
875                     continue;
876                 if(!PdmOidLinkedListHasOid(all_sizes, page_size))
877                 {
878                     PdmOidLinkedListAddOid(all_sizes, page_size);
879                 }
880             }
881             break;
882
883         case PdmOidMediumSS_CONTINUOUS:
884             /*
885              * unsupported
886              */
887             break;
888         }
889     }
890     /*
891      * return
892      */
893     return all_sizes;
894 }
895
896 /*
897  * ------------------------------------------------------------------------
898  * Name: PdmOidMediumSSGetTraysSizes
899  *
900  * Description:
901  *
902  *     
903  *
904  * Return value:
905  *
906  *     
907  *
908  */
909 void
910 PdmOidMediumSSGetTraysSizes(PdmOidMediumSS* me,
911                             PdmOidTrayMediumList* trays_medium,
912                             PdmOidList** trays,
913                             PdmOidList** sizes)
914 {
915     int i_mss, i_ds, i_itm, i_its;
916     PdmOidMediumDiscreteSizeList* ds_list;
917     int tray_count;
918     PdmOid current_tray, current_medium;
919     PdmOidMediumDiscreteSizeList* unspecified_tray_ds;
920     PdmOidMediumDiscreteSizeList* tray_ds;
921
922     if(me == (PdmOidMediumSS*)NULL
923        ||
924        trays_medium == (PdmOidTrayMediumList*)NULL)
925     {
926         if(trays != (PdmOidList**)NULL) *trays = (PdmOidList*)NULL;
927         if(sizes != (PdmOidList**)NULL) *sizes = (PdmOidList*)NULL;
928         return;
929     }
930     /*
931      * allocate the trays and sizes lists
932      */
933     tray_count = PdmOidTrayMediumListCount(trays_medium);
934     if(tray_count == 0)
935     {
936         if(trays != (PdmOidList**)NULL) *trays = (PdmOidList*)NULL;
937         if(sizes != (PdmOidList**)NULL) *sizes = (PdmOidList*)NULL;
938         return;
939     }
940     if(trays != (PdmOidList**)NULL)
941     {
942         *trays = (PdmOidList*)XtCalloc(1, sizeof(PdmOidList));
943         (*trays)->list = (PdmOid*)XtCalloc(tray_count, sizeof(PdmOid));
944     }
945     if(sizes != (PdmOidList**)NULL)
946     {
947         *sizes = (PdmOidList*)XtCalloc(1, sizeof(PdmOidList));
948         (*sizes)->list = (PdmOid*)XtCalloc(tray_count, sizeof(PdmOid));
949     }
950     /*
951      * loop through the input trays medium list
952      */
953     i_its = 0;
954     for(i_itm = 0; i_itm < tray_count; i_itm++)
955     {
956         current_tray = PdmOidTrayMediumListTray(trays_medium, i_itm);
957         if(current_tray == pdmoid_none)
958             continue;
959         current_medium = PdmOidTrayMediumListMedium(trays_medium, i_itm);
960         if(current_medium == pdmoid_none)
961             continue;
962         /*
963          * loop through the MediumSourceSizes, looking for an appropriate
964          * discrete sizes spec for the current tray
965          */
966         unspecified_tray_ds = (PdmOidMediumDiscreteSizeList*)NULL;
967         tray_ds = (PdmOidMediumDiscreteSizeList*)NULL;
968         for(i_mss = 0;
969             i_mss < me->count
970             && tray_ds == (PdmOidMediumDiscreteSizeList*)NULL;
971             i_mss++)
972         {
973             switch((me->mss)[i_mss].mstag)
974             {
975             case PdmOidMediumSS_DISCRETE:
976                 if((me->mss)[i_mss].input_tray == current_tray)
977                     tray_ds = (me->mss)[i_mss].ms.discrete;
978                 else if ((me->mss)[i_mss].input_tray == pdmoid_unspecified)
979                     unspecified_tray_ds = (me->mss)[i_mss].ms.discrete;
980                 break;
981                    
982             case PdmOidMediumSS_CONTINUOUS:
983                 /*
984                  * unsupported
985                  */
986                 break;
987             }
988         }
989         /*
990          * if the tray was not matched, use the unspecifed tray size list
991          */
992         if(tray_ds == (PdmOidMediumDiscreteSizeList*)NULL)
993             if(unspecified_tray_ds == (PdmOidMediumDiscreteSizeList*)NULL)
994             {
995                 /*
996                  * not even an unspecified tray, skip this
997                  * input-trays-medium entry.
998                  */
999                 continue;
1000             }
1001             else
1002                 tray_ds = unspecified_tray_ds;
1003         /*
1004          * loop through the discrete sizes list, looking for a size that
1005          * matches the medium for the current input tray
1006          */
1007         for(i_ds = 0; i_ds < tray_ds->count; i_ds++)
1008         {
1009             /*
1010              * check to see if the current input tray's medium size
1011              * matches the current discrete size
1012              *
1013              * Note: in the CDEnext SI, medium identifiers coincide with
1014              *       medium-size identifiers. If the DP-Medium object is
1015              *       ever implemented, this check would need to be
1016              *       changed so that the input tray's medium size is
1017              *       obtained from the indicated Medium object, and not
1018              *       inferred from the medium identifier itself.
1019              */
1020             if((tray_ds->list)[i_ds].page_size == current_medium)
1021             {
1022                 /*
1023                  * the current input tray's medium size matches the
1024                  * current discrete size; add the tray and medium size
1025                  * Oids to the return lists.
1026                  */
1027                 if(trays != (PdmOidList**)NULL)
1028                     (*trays)->list[i_its] = current_tray;
1029                 if(sizes != (PdmOidList**)NULL)
1030                     (*sizes)->list[i_its] = (tray_ds->list)[i_ds].page_size;
1031                 ++i_its;
1032                 break;
1033             }
1034         }
1035     }
1036     if(trays != (PdmOidList**)NULL) (*trays)->count = i_its;
1037     if(sizes != (PdmOidList**)NULL) (*sizes)->count = i_its;
1038 }
1039
1040 /*
1041  * ------------------------------------------------------------------------
1042  * Name: MediumContinuousSizeParse
1043  *
1044  * Description:
1045  *
1046  *     'ptr_return' *cannot* be NULL.
1047  *     
1048  *
1049  * Return value:
1050  *
1051  *     
1052  *
1053  */
1054 static PdmOidMediumContinuousSize*
1055 MediumContinuousSizeParse(const char* value_string,
1056                                 const char** ptr_return)
1057 {
1058     const char* first_nonws_ptr;
1059     PdmOidMediumContinuousSize* mcs = NULL;
1060     /*
1061      * skip leading whitespace
1062      */
1063     first_nonws_ptr = value_string + SpanWhitespace(value_string);
1064     /*
1065      * parse out the MediumSize sequence start char
1066      */
1067     if(!ParseSeqStart(first_nonws_ptr, ptr_return))
1068         goto MediumContinuousSizeParse_error;
1069     /*
1070      * peek ahead to see if it looks like we actually have a continuous
1071      * size spec (looking for the sequence start char on the 1st range spec)
1072      */
1073     if(!ParseSeqStart(*ptr_return, (const char**)NULL))
1074         goto MediumContinuousSizeParse_error;
1075     /*
1076      * Ok, let's go for it
1077      */
1078     mcs = (PdmOidMediumContinuousSize*)
1079         XtCalloc(1, sizeof(PdmOidMediumContinuousSize));
1080     /*
1081      * "range across the feed direction"
1082      */
1083     if(!ParseUnsignedRange(*ptr_return, ptr_return, &mcs->range_across_feed))
1084         goto MediumContinuousSizeParse_error;
1085     /*
1086      * "increment across the feed direction" (optional, default 0)
1087      */
1088     if(!ParseUnspecifiedValue(*ptr_return, ptr_return))
1089         if(!ParseUnsignedValue(*ptr_return, ptr_return,
1090                                &mcs->increment_across_feed))
1091             goto MediumContinuousSizeParse_error;
1092     /*
1093      * "range in the feed direction"
1094      */
1095     if(!ParseUnsignedRange(*ptr_return, ptr_return, &mcs->range_in_feed))
1096         goto MediumContinuousSizeParse_error;
1097     /*
1098      * "increment in the feed direction" (optional, default 0)
1099      */
1100     if(!ParseUnspecifiedValue(*ptr_return, ptr_return))
1101         if(!ParseUnsignedValue(*ptr_return, ptr_return,
1102                                &mcs->increment_in_feed))
1103             goto MediumContinuousSizeParse_error;
1104     /*
1105      * "long edge feeds" flag (default TRUE)
1106      */
1107     if(ParseUnspecifiedValue(*ptr_return, ptr_return))
1108         mcs->long_edge_feeds = True;
1109     else
1110         if(!ParseBooleanValue(*ptr_return, ptr_return, &mcs->long_edge_feeds))
1111             goto MediumContinuousSizeParse_error;
1112     /*
1113      * "generic assured reproduction area"
1114      */
1115     if(!ParseArea(*ptr_return, ptr_return, &mcs->assured_reproduction_area))
1116         goto MediumContinuousSizeParse_error;
1117     /*
1118      * parse out the MediumSize sequence end character
1119      */
1120     if(!ParseSeqEnd(*ptr_return, ptr_return))
1121         goto MediumContinuousSizeParse_error;
1122     /*
1123      * return
1124      */
1125     return mcs;
1126     
1127
1128  MediumContinuousSizeParse_error:
1129     /*
1130      * syntax error - don't log since this function may be called
1131      * as a lookahead
1132      */
1133     *ptr_return = first_nonws_ptr;
1134     XtFree((char*)mcs);
1135     return NULL;
1136 }
1137
1138 /*
1139  * ------------------------------------------------------------------------
1140  * Name: MediumContinuousSizeDelete
1141  *
1142  * Description:
1143  *
1144  *     'ptr_return' *cannot* be NULL.
1145  *     
1146  *
1147  * Return value:
1148  *
1149  *     
1150  *
1151  */
1152 static void
1153 MediumContinuousSizeDelete(PdmOidMediumContinuousSize* me)
1154 {
1155     XtFree((char*)me);
1156 }
1157
1158 /*
1159  * ------------------------------------------------------------------------
1160  * Name: MediumDiscreteSizeListParse
1161  *
1162  * Description:
1163  *
1164  *     'ptr_return' *cannot* be NULL.
1165  *
1166  * Return value:
1167  *
1168  *     
1169  *
1170  */
1171 static PdmOidMediumDiscreteSizeList*
1172 MediumDiscreteSizeListParse(const char* value_string,
1173                             const char** ptr_return, int i)
1174 {
1175     PdmOidMediumDiscreteSizeList* list;
1176     PdmOidMediumDiscreteSize mds;
1177     /*
1178      * check for the start of a new MediumSize sequence
1179      */
1180     if(ParseSeqStart(value_string, ptr_return))
1181     {
1182         /*
1183          * "page size"
1184          */
1185         mds.page_size = PdmOidParse(*ptr_return, ptr_return);
1186         /*
1187          * "long edge feeds" flag (default TRUE)
1188          */
1189         if(ParseUnspecifiedValue(*ptr_return, ptr_return))
1190             mds.long_edge_feeds = True;
1191         else
1192             if(!ParseBooleanValue(*ptr_return, ptr_return,
1193                                   &mds.long_edge_feeds))
1194             {
1195                 /*
1196                  * syntax error
1197                  */
1198                 fprintf(stderr, "%s\n", PDM_MSG_WARN_MSS);
1199                 return NULL;
1200             }
1201         /*
1202          * "assured reproduction area"
1203          */
1204         if(!ParseArea(*ptr_return, ptr_return,
1205                       &mds.assured_reproduction_area))
1206         {
1207             /*
1208              * syntax error
1209              */
1210             fprintf(stderr, "%s\n", PDM_MSG_WARN_MSS);
1211             return NULL;
1212         }
1213         /*
1214          * parse out the MediumSize sequence end character
1215          */
1216         if(!ParseSeqEnd(*ptr_return, ptr_return))
1217         {
1218             fprintf(stderr, "%s\n", PDM_MSG_WARN_MSS);
1219             return NULL;
1220         }
1221         /*
1222          * recurse to parse the next Discrete MediumSize sequence
1223          */
1224         list = MediumDiscreteSizeListParse(*ptr_return, ptr_return, i+1);
1225         if(list != (PdmOidMediumDiscreteSizeList*)NULL)
1226         {
1227             /*
1228              * copy the current discrete MediumSize into the list
1229              */
1230             memmove((list->list)+i, &mds, sizeof(PdmOidMediumDiscreteSize));
1231         }
1232     }
1233     else
1234     {
1235         /*
1236          * MediumSize sequence start not found; end of the discrete sizes
1237          * list
1238          */
1239         list = (PdmOidMediumDiscreteSizeList*)
1240             XtCalloc(1, sizeof(PdmOidMediumDiscreteSizeList));
1241         list->count = i;
1242         list->list = (PdmOidMediumDiscreteSize*)
1243             XtCalloc(i, sizeof(PdmOidMediumDiscreteSize));
1244     }
1245     return list;
1246 }
1247
1248 /*
1249  * ------------------------------------------------------------------------
1250  * Name: MediumDiscreteSizeListDelete
1251  *
1252  * Description:
1253  *
1254  *     
1255  *
1256  * Return value:
1257  *
1258  *     
1259  *
1260  */
1261 static void
1262 MediumDiscreteSizeListDelete(PdmOidMediumDiscreteSizeList* list)
1263 {
1264     if(list != (PdmOidMediumDiscreteSizeList*)NULL)
1265     {
1266         XtFree((char*)list->list);
1267         XtFree((char*)list);
1268     }
1269 }
1270
1271 /*
1272  * ------------------------------------------------------------------------
1273  * Name: PdmOidTrayMediumListNew
1274  *
1275  * Description:
1276  *
1277  *
1278  * Return value:
1279  *
1280  *     
1281  *
1282  */
1283 PdmOidTrayMediumList*
1284 PdmOidTrayMediumListNew(const char* value_string)
1285 {
1286     if(value_string == (const char*)NULL)
1287         return (PdmOidTrayMediumList*)NULL;
1288     else
1289     {
1290         const char* ptr;
1291         return TrayMediumListParse(value_string, &ptr, 0);
1292     }
1293 }
1294
1295 /*
1296  * ------------------------------------------------------------------------
1297  * Name: TrayMediumListParse
1298  *
1299  * Description:
1300  *
1301  *     'ptr_return' *cannot* be NULL.
1302  *
1303  * Return value:
1304  *
1305  *     
1306  *
1307  */
1308 static PdmOidTrayMediumList*
1309 TrayMediumListParse(const char* value_string,
1310                     const char** ptr_return, int i)
1311 {
1312     PdmOidTrayMedium tm;
1313     PdmOidTrayMediumList* list;
1314     /*
1315      * check for the start of a new InputTrayMedium sequence
1316      */
1317     if(ParseSeqStart(value_string, ptr_return))
1318     {
1319         /*
1320          * "input tray"
1321          */
1322         tm.input_tray = PdmOidParse(*ptr_return, ptr_return);
1323         /*
1324          * "medium"
1325          */
1326         tm.medium = PdmOidParse(*ptr_return, ptr_return);
1327         /*
1328          * parse out the InputTrayMedium sequence end character
1329          */
1330         if(!ParseSeqEnd(*ptr_return, ptr_return))
1331         {
1332             fprintf(stderr, "%s\n", PDM_MSG_WARN_ITM);
1333             return NULL;
1334         }
1335         /*
1336          * recurse to parse the next InputTrayMedium sequence
1337          */
1338         list = TrayMediumListParse(*ptr_return, ptr_return, i+1);
1339         if(list != (PdmOidTrayMediumList*)NULL)
1340         {
1341             /*
1342              * copy the current InputTrayMedium into the list
1343              */
1344             memmove((list->list)+i, &tm, sizeof(PdmOidTrayMedium));
1345         }
1346     }
1347     else
1348     {
1349         /*
1350          * InputTrayMedium sequence start not found
1351          */
1352         if(**ptr_return == '\0')
1353         {
1354             /*
1355              * end of the list
1356              */
1357             list = (PdmOidTrayMediumList*)
1358                 XtCalloc(1, sizeof(PdmOidTrayMediumList));
1359             list->count = i;
1360             if (i == 0)
1361                 list->list = NULL;
1362             else
1363                 list->list = (PdmOidTrayMedium*)
1364                     XtCalloc(i, sizeof(PdmOidTrayMedium));
1365         }
1366         else
1367         {
1368             /*
1369              * syntax error
1370              */
1371             fprintf(stderr, "%s\n", PDM_MSG_WARN_ITM);
1372             return NULL;
1373         }
1374     }
1375     /*
1376      * return
1377      */
1378     return list;
1379 }
1380
1381 /*
1382  * ------------------------------------------------------------------------
1383  * Name: PdmOidTrayMediumListDelete
1384  *
1385  * Description:
1386  *
1387  *     
1388  *
1389  * Return value:
1390  *
1391  *     
1392  *
1393  */
1394 void
1395 PdmOidTrayMediumListDelete(PdmOidTrayMediumList* list)
1396 {
1397     if(list != (PdmOidTrayMediumList*)NULL)
1398     {
1399         XtFree((char*)list->list);
1400         XtFree((char*)list);
1401     }
1402 }
1403
1404 /*
1405  * ------------------------------------------------------------------------
1406  * Name: ParseArea
1407  *
1408  * Description:
1409  *
1410  *     Skips leading whitespace and parses out and returns a PdmOidArea.
1411  *
1412  * Return value:
1413  *
1414  *     True if the PdmOidArea was successfully parsed. ptr_return is
1415  *     updated to point to location where the parsing ended.
1416  *
1417  *     False if a PdmOidArea was not found; ptr_return is updated
1418  *     to point to the first non-whitespace char in value_string.
1419  *
1420  */
1421 static Boolean
1422 ParseArea(const char* value_string,
1423                const char** ptr_return,
1424                PdmOidArea* area_return)
1425 {
1426     const char* first_nonws_ptr;
1427     const char* ptr;
1428     /*
1429      * skip leading whitespace
1430      */
1431     first_nonws_ptr = value_string + SpanWhitespace(value_string);
1432     /*
1433      * parse out the area sequence start
1434      */
1435     if(!ParseSeqStart(first_nonws_ptr, &ptr))
1436         goto ParseArea_error;
1437     /*
1438      * parse the minimum x value
1439      */
1440     if(!ParseRealValue(ptr, &ptr,
1441                            area_return ? &area_return->minimum_x : NULL))
1442         goto ParseArea_error;
1443     /*
1444      * parse the maximum x value
1445      */
1446     if(!ParseRealValue(ptr, &ptr,
1447                            area_return ? &area_return->maximum_x : NULL))
1448         goto ParseArea_error;
1449     /*
1450      * parse the minimum y value
1451      */
1452     if(!ParseRealValue(ptr, &ptr,
1453                            area_return ? &area_return->minimum_y : NULL))
1454         goto ParseArea_error;
1455     /*
1456      * parse the maximum y value
1457      */
1458     if(!ParseRealValue(ptr, &ptr,
1459                            area_return ? &area_return->maximum_y : NULL))
1460         goto ParseArea_error;
1461     /*
1462      * parse out the area sequence end
1463      */
1464     if(!ParseSeqEnd(ptr, &ptr))
1465         goto ParseArea_error;
1466     /*
1467      * update the return pointer
1468      */
1469     if(ptr_return != (const char**)NULL)
1470         *ptr_return = ptr;
1471     /*
1472      * return
1473      */
1474     return True;
1475     
1476
1477  ParseArea_error:
1478     /*
1479      * syntax error
1480      */
1481     if(ptr_return != (const char**)NULL)
1482         *ptr_return = first_nonws_ptr;
1483     return False;
1484 }
1485
1486 /*
1487  * ------------------------------------------------------------------------
1488  * Name: ParseUnsignedRange
1489  *
1490  * Description:
1491  *
1492  *     Skips leading whitespace and parses out and returns a
1493  *     PdmOidUnsignedRange.
1494  *
1495  * Return value:
1496  *
1497  *     True if the PdmOidUnsignedRange was successfully
1498  *     parsed. ptr_return is updated to point to location where the
1499  *     parsing ended.
1500  *
1501  *     False if a PdmOidUnsignedRange was not found; ptr_return is
1502  *     updated to point to the first non-whitespace char in value_string.
1503  *
1504  */
1505 static Boolean
1506 ParseUnsignedRange(const char* value_string,
1507                    const char** ptr_return,
1508                    PdmOidUnsignedRange* range_return)
1509 {
1510     const char* first_nonws_ptr;
1511     const char* ptr;
1512     /*
1513      * skip leading whitespace
1514      */
1515     first_nonws_ptr = value_string + SpanWhitespace(value_string);
1516     /*
1517      * parse out the range sequence start
1518      */
1519     if(!ParseSeqStart(first_nonws_ptr, &ptr))
1520         goto ParseUnsignedRange_error;
1521     /*
1522      * parse the lower bound
1523      */
1524     if(!ParseUnsignedValue(ptr, &ptr,
1525                            range_return ? &range_return->lower_bound : NULL))
1526         goto ParseUnsignedRange_error;
1527     /*
1528      * parse the upper bound
1529      */
1530     if(!ParseUnsignedValue(ptr, &ptr,
1531                            range_return ? &range_return->upper_bound : NULL))
1532         goto ParseUnsignedRange_error;
1533     /*
1534      * parse out the range sequence end
1535      */
1536     if(!ParseSeqEnd(ptr, &ptr))
1537         goto ParseUnsignedRange_error;
1538     /*
1539      * update the return pointer
1540      */
1541     if(ptr_return != (const char**)NULL)
1542         *ptr_return = ptr;
1543     /*
1544      * return
1545      */
1546     return True;
1547     
1548
1549  ParseUnsignedRange_error:
1550     /*
1551      * syntax error
1552      */
1553     if(ptr_return != (const char**)NULL)
1554         *ptr_return = first_nonws_ptr;
1555     return False;
1556 }
1557
1558 /*
1559  * ------------------------------------------------------------------------
1560  * Name: PdmOidNotifyParse
1561  *
1562  * Description:
1563  *
1564  *
1565  * Return value:
1566  *
1567  *
1568  */
1569 PdmOidNotify PdmOidNotifyParse(const char* value_string)
1570 {
1571     const char* ptr = value_string;
1572
1573     if(value_string == (const char*)NULL)
1574         return PDMOID_NOTIFY_NONE;
1575     /*
1576      * look for an event handling profile sequence start
1577      */
1578     if(!ParseSeqStart(value_string, &ptr))
1579     {
1580         if('\0' == *ptr)
1581             /*
1582              * empty value is valid
1583              */
1584             return PDMOID_NOTIFY_NONE;
1585         else
1586             return PDMOID_NOTIFY_UNSUPPORTED;
1587     }
1588     /*
1589      * look for an event set sequence start
1590      */
1591     if(!ParseSeqStart(ptr, &ptr))
1592     {
1593         /*
1594          * check for an empty event handling profile
1595          */
1596         if(ParseSeqEnd(ptr, &ptr))
1597         {
1598             ptr += SpanWhitespace(ptr);
1599             if(*ptr == '\0')
1600                 /*
1601                  * valid empty event handling profile sequence
1602                  */
1603                 return PDMOID_NOTIFY_NONE;
1604         }
1605         return PDMOID_NOTIFY_UNSUPPORTED;
1606     }
1607     /*
1608      * the only event in the set should be report job completed
1609      */
1610     if(pdmoid_val_event_report_job_completed != PdmOidParse(ptr, &ptr))
1611         return PDMOID_NOTIFY_UNSUPPORTED;
1612     /*
1613      * event set sequence end
1614      */
1615     if(!ParseSeqEnd(ptr, &ptr))
1616         return PDMOID_NOTIFY_UNSUPPORTED;
1617     /*
1618      * delivery method of electronic mail
1619      */
1620     if(pdmoid_val_delivery_method_electronic_mail != PdmOidParse(ptr, &ptr))
1621         return PDMOID_NOTIFY_UNSUPPORTED;
1622     /*
1623      * event handling profile sequence end
1624      */
1625     if(!ParseSeqEnd(ptr, &ptr))
1626         return PDMOID_NOTIFY_UNSUPPORTED;
1627     /*
1628      * end of value
1629      */
1630     ptr += SpanWhitespace(ptr);
1631     if('\0' == *ptr)
1632         /*
1633          * valid supported notification profile
1634          */
1635         return PDMOID_NOTIFY_EMAIL;
1636     else
1637         return PDMOID_NOTIFY_UNSUPPORTED;
1638 }
1639
1640 /*
1641  * ------------------------------------------------------------------------
1642  * Name: PdmOidNotifyString
1643  *
1644  * Description:
1645  *
1646  *
1647  * Return value:
1648  *
1649  *
1650  */
1651 const char* PdmOidNotifyString(PdmOidNotify notify)
1652 {
1653     switch(notify)
1654     {
1655     case PDMOID_NOTIFY_UNSUPPORTED:
1656         return (const char*)NULL;
1657         break;
1658     case PDMOID_NOTIFY_NONE:
1659         return NOTIFY_NONE_STR;
1660         break;
1661     case PDMOID_NOTIFY_EMAIL:
1662         return NOTIFY_EMAIL_STR;
1663         break;
1664     }
1665 }
1666
1667 /*
1668  * ------------------------------------------------------------------------
1669  * Name: PdmOidDocumentFormatParse
1670  *
1671  * Description:
1672  *
1673  *
1674  * Return value:
1675  *
1676  *
1677  */
1678 char*
1679 PdmOidDocumentFormatParse(const char* value_string)
1680 {
1681     char* document_format;
1682     const char* ptr;
1683     
1684     /*
1685      * get the document format from the value string
1686      */
1687     document_format =
1688         PdmOidDocumentFormatNext(value_string, &ptr);
1689     if((char*)NULL != document_format)
1690     {
1691         /*
1692          * verify that the document format is the only value specified
1693          */
1694         ptr += SpanWhitespace(ptr);
1695         if('\0' == *ptr)
1696             /*
1697              * valid document-format value
1698              */
1699             return document_format;
1700         else
1701             /*
1702              * invalid; clean up
1703              */
1704             XtFree(document_format);
1705     }
1706     return (char*)NULL;
1707 }
1708
1709 /*
1710  * ------------------------------------------------------------------------
1711  * Name: PdmOidDocumentFormatDefault
1712  *
1713  * Description:
1714  *
1715  *
1716  * Return value:
1717  *
1718  *
1719  */
1720 char*
1721 PdmOidDocumentFormatDefault(const char* value_string)
1722 {
1723     /*
1724      * return the first document format from the value string
1725      */
1726     return PdmOidDocumentFormatNext(value_string, (const char**)NULL);
1727 }
1728
1729 /*
1730  * ------------------------------------------------------------------------
1731  * Name: PdmOidDocumentFormatNext
1732  *
1733  * Description:
1734  *
1735  *
1736  * Return value:
1737  *
1738  *
1739  */
1740 static char*
1741 PdmOidDocumentFormatNext(const char* value_string,
1742                          const char** ptr_return)
1743 {
1744     const char* ptr;
1745     const char* first_nonws_ptr;
1746     const char* document_format_start;
1747     char* document_format;
1748     int document_format_len;
1749     int token_len;
1750
1751     if((const char*)NULL == value_string)
1752         return (char*)NULL;
1753     /*
1754      * skip leading whitespace
1755      */
1756     ptr = value_string + SpanWhitespace(value_string);
1757     first_nonws_ptr = ptr;
1758     /*
1759      * sequence start
1760      */
1761     if(!ParseSeqStart(ptr, &ptr))
1762         goto PdmOidDocumentFormatNext_error;
1763     /*
1764      * skip whitepace to the start of the document format, and save the
1765      * location
1766      */
1767     ptr += SpanWhitespace(ptr);
1768     document_format_start = ptr;
1769     /*
1770      * document format
1771      */
1772     if(0 == (token_len = SpanToken(ptr)))
1773         goto PdmOidDocumentFormatNext_error;
1774     ptr += token_len;
1775     /*
1776      * optional variant
1777      */
1778     ptr += SpanWhitespace(ptr);
1779     if(0 != (token_len = SpanToken(ptr)))
1780     {
1781         ptr += token_len;
1782         /*
1783          * optional version
1784          */
1785         ptr += SpanWhitespace(ptr);
1786         ptr += SpanToken(ptr);
1787     }
1788     /*
1789      * determine the length of the document format, excluding the
1790      * sequence delimeters
1791      */
1792     document_format_len = ptr - document_format_start;
1793     /*
1794      * sequence end
1795      */
1796     if(!ParseSeqEnd(ptr, &ptr))
1797         goto PdmOidDocumentFormatNext_error;
1798     /*
1799      * update return pointer
1800      */
1801     if((const char**)NULL != ptr_return)
1802         *ptr_return = ptr;
1803     /*
1804      * build the return document format string, and return it
1805      */
1806     document_format = XtMalloc(document_format_len+1);
1807     strncpy(document_format, document_format_start, document_format_len);
1808     document_format[document_format_len] = '\0';
1809     return document_format;
1810
1811  PdmOidDocumentFormatNext_error:
1812     fprintf(stderr, "%s\n", PDM_MSG_WARN_DOC_FMT);
1813     if((const char**)NULL != ptr_return)
1814         *ptr_return = first_nonws_ptr;
1815     return (char*)NULL;
1816 }
1817
1818 /*
1819  * ------------------------------------------------------------------------
1820  * Name: ParseBooleanValue
1821  *
1822  * Description:
1823  *
1824  *
1825  * Return value:
1826  *
1827  *
1828  */
1829 static Boolean
1830 ParseBooleanValue(const char* value_string,
1831                   const char** ptr_return,
1832                   Boolean* boolean_return)
1833 {
1834     const char* ptr;
1835     int length;
1836     Boolean status;
1837     /*
1838      * skip leading whitespace
1839      */
1840     ptr = value_string + SpanWhitespace(value_string);
1841     /*
1842      * get the whitespace-delimited token length
1843      */
1844     length = SpanToken(ptr);
1845     /*
1846      * determine if true or false or bad
1847      */
1848     if(strncasecmp(ptr, "TRUE", length) == 0)
1849     {
1850         if(boolean_return != (Boolean*)NULL)
1851             *boolean_return = True;
1852         status = True;
1853     }
1854     else if(strncasecmp(ptr, "FALSE", length) == 0)
1855     {
1856         if(boolean_return != (Boolean*)NULL)
1857             *boolean_return = False;
1858         status = True;
1859     }
1860     else
1861     {
1862         /*
1863          * syntax error
1864          */
1865         status = False;
1866     }
1867     /*
1868      * update the return pointer and return
1869      */
1870     if(ptr_return != (const char**)NULL)
1871         *ptr_return = status ? ptr+length : ptr;
1872     return status;
1873 }
1874
1875 /*
1876  * ------------------------------------------------------------------------
1877  * Name: ParseUnsignedValue
1878  *
1879  * Description:
1880  *
1881  *     Skips leading whitespace and parses out and returns a unsigned number.
1882  *
1883  * Return value:
1884  *
1885  *     True if a unsigned number was successfully parsed. ptr_return is
1886  *     updated to point to location where the unsigned number parsing
1887  *     ended.
1888  *
1889  *     False if a unsigned number was not found; ptr_return is updated
1890  *     to point to the first non-whitespace char in value_string.
1891  *
1892  */
1893 static Boolean
1894 ParseUnsignedValue(const char* value_string,
1895                    const char** ptr_return,
1896                    unsigned long* unsigned_return)
1897 {
1898     unsigned long unsigned_value;
1899     Boolean status;
1900     const char* first_nonws_ptr;
1901     const char* ptr;
1902     /*
1903      * skip leading whitespace
1904      */
1905     first_nonws_ptr = value_string + SpanWhitespace(value_string);
1906     unsigned_value = strtoul(first_nonws_ptr, (char**)(&ptr), 0);
1907     if(ptr == first_nonws_ptr)
1908         status = False;
1909     else
1910         status = True;
1911     /*
1912      * update return parms
1913      */
1914     if(ptr_return != (const char**)NULL)
1915         *ptr_return = ptr;
1916     if(unsigned_return != (unsigned long*)NULL)
1917         *unsigned_return = unsigned_value;
1918     /*
1919      * return
1920      */
1921     return status;
1922 }
1923
1924 /*
1925  * ------------------------------------------------------------------------
1926  * Name: ParseRealValue
1927  *
1928  * Description:
1929  *
1930  *     Skips leading whitespace and parses out and returns a real number.
1931  *
1932  * Return value:
1933  *
1934  *     xTrue if a real number was successfully parsed. ptr_return is
1935  *     updated to point to location where the real number parsing
1936  *     ended.
1937  *
1938  *     xFalse if a real number was not found; ptr_return is updated
1939  *     to point to the first non-whitespace char in value_string.
1940  *
1941  */
1942 static Boolean
1943 ParseRealValue(const char* value_string,
1944                const char** ptr_return,
1945                float* real_return)
1946 {
1947     float real_value;
1948     Boolean status;
1949     const char* first_nonws_ptr;
1950     const char* ptr;
1951     /*
1952      * skip leading whitespace
1953      */
1954     first_nonws_ptr = value_string + SpanWhitespace(value_string);
1955     real_value = (float)strtod(first_nonws_ptr, (char**)(&ptr));
1956     if(ptr == first_nonws_ptr)
1957         status = False;
1958     else
1959         status = True;
1960     /*
1961      * update return parms
1962      */
1963     if(ptr_return != (const char**)NULL)
1964         *ptr_return = ptr;
1965     if(real_return != (float*)NULL)
1966         *real_return = real_value;
1967     /*
1968      * return
1969      */
1970     return status;
1971 }
1972
1973 /*
1974  * ------------------------------------------------------------------------
1975  * Name: ParseSeqEnd
1976  *
1977  * Description:
1978  *
1979  * Description:
1980  *
1981  *     Skips leading whitespace and parses out the sequence end
1982  *     character '}'.
1983  *
1984  * Return value:
1985  *
1986  *     True if the sequence end character was parsed; ptr_return is
1987  *     updated to point to the first char following the sequence end
1988  *     character.
1989  *
1990  *     False if the sequence end character was not found; ptr_return is
1991  *     updated to point to the first non-whitespace char in value_string.
1992  *
1993  */
1994 static Boolean
1995 ParseSeqEnd(const char* value_string,
1996             const char** ptr_return)
1997 {
1998     const char* ptr;
1999     Boolean status;
2000     /*
2001      * skip leading whitespace
2002      */
2003     ptr = value_string + SpanWhitespace(value_string);
2004     /*
2005      * parse out the sequence end character
2006      */
2007     if(*ptr == '}')
2008     {
2009         status = True;
2010         ++ptr;
2011     }
2012     else
2013         status = False;
2014     /*
2015      * update the return pointer
2016      */
2017     if(ptr_return != (const char**)NULL)
2018         *ptr_return = ptr;
2019     /*
2020      * return
2021      */
2022     return status;
2023 }
2024
2025 /*
2026  * ------------------------------------------------------------------------
2027  * Name: ParseSeqStart
2028  *
2029  * Description:
2030  *
2031  *     Skips leading whitespace and parses out the sequence start
2032  *     character '{'.
2033  *
2034  * Return value:
2035  *
2036  *     True if the sequence start character was parsed; ptr_return is
2037  *     updated to point to the first char following the sequence start
2038  *     character.
2039  *
2040  *     False if the sequence start character was not found; ptr_return is
2041  *     updated to point to the first non-whitespace char in value_string.
2042  *
2043  */
2044 static Boolean
2045 ParseSeqStart(const char* value_string,
2046               const char** ptr_return)
2047 {
2048     const char* ptr;
2049     Boolean status;
2050     /*
2051      * skip leading whitespace
2052      */
2053     ptr = value_string + SpanWhitespace(value_string);
2054     /*
2055      * parse out the sequence start character
2056      */
2057     if(*ptr == '{')
2058     {
2059         status = True;
2060         ++ptr;
2061     }
2062     else
2063         status = False;
2064     /*
2065      * update the return pointer
2066      */
2067     if(ptr_return != (const char**)NULL)
2068         *ptr_return = ptr;
2069     /*
2070      * return
2071      */
2072     return status;
2073 }
2074
2075 /*
2076  * ------------------------------------------------------------------------
2077  * Name: ParseUnspecifiedValue
2078  *
2079  * Description:
2080  *
2081  *     Skips leading whitespace and parses out an unspecified optional
2082  *     value (i.e. matching '' or "" - skips all data between the set of
2083  *     quotes).
2084  *
2085  * Return value:
2086  *
2087  *     True if an unspecified value was parsed; ptr_return is updated to
2088  *     point to the first char following the trailing quote.
2089  *
2090  *     False if an unspecified value was not found; ptr_return is updated
2091  *     to point to the first non-whitespace char in value_string.
2092  *
2093  */
2094 static Boolean
2095 ParseUnspecifiedValue(const char* value_string,
2096                       const char** ptr_return)
2097 {
2098     Boolean status;
2099     const char* ptr;
2100     /*
2101      * skip leading whitespace
2102      */
2103     ptr = value_string + SpanWhitespace(value_string);
2104     /*
2105      * parse out an unspecified optional value ('' or "")
2106      */
2107     if(*ptr == '\'' || *ptr == '"')
2108     {
2109         char delim[2];
2110
2111         if(ptr_return != (const char**)NULL)
2112         {
2113             delim[0] = *ptr;
2114             delim[1] = '\0';
2115             /*
2116              * skip over the matching delimiter
2117              */
2118             ++ptr;
2119             ptr += strcspn(ptr, delim);
2120             if(*ptr != '\0')
2121                 ++ptr;
2122         }
2123         status = True;
2124     }
2125     else
2126         status = False;
2127     /*
2128      * update the return pointer
2129      */
2130     if(ptr_return != (const char**)NULL)
2131         *ptr_return = ptr;
2132     /*
2133      * return
2134      */
2135     return status;
2136 }
2137
2138 /*
2139  * ------------------------------------------------------------------------
2140  * Name: SpanToken
2141  *
2142  * Description:
2143  *
2144  *     Returns the length of the initial segment of the passed string
2145  *     that consists entirely of non-whitespace and non-sequence
2146  *     delimiter characters.
2147  *
2148  *
2149  */
2150 static int
2151 SpanToken(const char* string)
2152 {
2153     const char* ptr;
2154     for(ptr = string;
2155         *ptr != '\0' && !DtIsspace((char*)ptr) && *ptr != '{' && *ptr != '}';
2156         ptr = DtNextChar((char*)ptr));
2157     return ptr - string;
2158 }
2159
2160 /*
2161  * ------------------------------------------------------------------------
2162  * Name: SpanWhitespace
2163  *
2164  * Description:
2165  *
2166  *     Returns the length of the initial segment of the passed string
2167  *     that consists entirely of whitespace characters.
2168  *
2169  *
2170  */
2171 static int
2172 SpanWhitespace(const char* string)
2173 {
2174     const char* ptr;
2175     for(ptr = string;
2176         *ptr != '\0' && DtIsspace((char*)ptr);
2177         ptr = DtNextChar((char*)ptr));
2178     return ptr - string;
2179 }