]> git.sur5r.net Git - openldap/blob - libraries/libldap/disptmpl.c
be418309e982a74b6d594fa808fd69e5ac825a4d
[openldap] / libraries / libldap / disptmpl.c
1 /*
2  * Copyright (c) 1993, 1994 Regents of the University of Michigan.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of Michigan at Ann Arbor. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  *
12  * disptmpl.c:  display template library routines for LDAP clients
13  * 7 March 1994 by Mark C Smith
14  */
15
16 #include <stdio.h>
17 #include <ctype.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #ifdef MACOS
21 #include "macos.h"
22 #else /* MACOS */
23 #ifdef DOS
24 #include <malloc.h>
25 #include "msdos.h"
26 #else /* DOS */
27 #include <sys/types.h>
28 #include <sys/file.h>
29 #ifndef VMS
30 #include <unistd.h>
31 #endif /* VMS */
32 #endif /* DOS */
33 #endif /* MACOS */
34
35 #include "lber.h"
36 #include "ldap.h"
37 #include "disptmpl.h"
38
39 #ifndef NEEDPROTOS
40 static void free_disptmpl();
41 static int read_next_tmpl();
42 int next_line_tokens();
43 void free_strarray();
44 #else /* !NEEDPROTOS */
45 static void free_disptmpl( struct ldap_disptmpl *tmpl );
46 static int read_next_tmpl( char **bufp, long *blenp,
47         struct ldap_disptmpl **tmplp, int dtversion );
48 int next_line_tokens( char **bufp, long *blenp, char ***toksp );
49 void free_strarray( char **sap );
50 #endif /* !NEEDPROTOS */
51
52 static char             *tmploptions[] = {
53     "addable", "modrdn",
54     "altview",
55     NULL
56 };
57
58
59 static unsigned long    tmploptvals[] = {
60     LDAP_DTMPL_OPT_ADDABLE, LDAP_DTMPL_OPT_ALLOWMODRDN,
61     LDAP_DTMPL_OPT_ALTVIEW,
62 };
63
64
65 static char             *itemtypes[] = {
66     "cis",                      "mls",                  "dn",
67     "bool",                     "jpeg",                 "jpegbtn",
68     "fax",                      "faxbtn",               "audiobtn",
69     "time",                     "date",                 "url",
70     "searchact",                "linkact",              "adddnact",
71     "addact",                   "verifyact",            "mail",
72     NULL
73 };
74
75 static unsigned long    itemsynids[] = {
76     LDAP_SYN_CASEIGNORESTR,     LDAP_SYN_MULTILINESTR,  LDAP_SYN_DN,
77     LDAP_SYN_BOOLEAN,           LDAP_SYN_JPEGIMAGE,     LDAP_SYN_JPEGBUTTON,
78     LDAP_SYN_FAXIMAGE,          LDAP_SYN_FAXBUTTON,     LDAP_SYN_AUDIOBUTTON,
79     LDAP_SYN_TIME,              LDAP_SYN_DATE,          LDAP_SYN_LABELEDURL,
80     LDAP_SYN_SEARCHACTION,      LDAP_SYN_LINKACTION,    LDAP_SYN_ADDDNACTION,
81     LDAP_SYN_ADDDNACTION,       LDAP_SYN_VERIFYDNACTION,LDAP_SYN_RFC822ADDR,
82 };
83
84
85 static char             *itemoptions[] = {
86     "ro",                               "sort",
87     "1val",                             "hide",
88     "required",                         "hideiffalse",
89     NULL
90 };
91
92
93 static unsigned long    itemoptvals[] = {
94     LDAP_DITEM_OPT_READONLY,            LDAP_DITEM_OPT_SORTVALUES,
95     LDAP_DITEM_OPT_SINGLEVALUED,        LDAP_DITEM_OPT_HIDEIFEMPTY,
96     LDAP_DITEM_OPT_VALUEREQUIRED,       LDAP_DITEM_OPT_HIDEIFFALSE,
97 };
98
99
100 #define ADDEF_CONSTANT  "constant"
101 #define ADDEF_ADDERSDN  "addersdn"
102
103
104 int
105 ldap_init_templates( char *file, struct ldap_disptmpl **tmpllistp )
106 {
107     FILE        *fp;
108     char        *buf;
109     long        rlen, len;
110     int         rc, eof;
111
112     *tmpllistp = NULLDISPTMPL;
113
114     if (( fp = fopen( file, "r" )) == NULL ) {
115         return( LDAP_TMPL_ERR_FILE );
116     }
117
118     if ( fseek( fp, 0L, SEEK_END ) != 0 ) {     /* move to end to get len */
119         fclose( fp );
120         return( LDAP_TMPL_ERR_FILE );
121     }
122
123     len = ftell( fp );
124
125     if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {     /* back to start of file */
126         fclose( fp );
127         return( LDAP_TMPL_ERR_FILE );
128     }
129
130     if (( buf = malloc( (size_t)len )) == NULL ) {
131         fclose( fp );
132         return( LDAP_TMPL_ERR_MEM );
133     }
134
135     rlen = fread( buf, 1, (size_t)len, fp );
136     eof = feof( fp );
137     fclose( fp );
138
139     if ( rlen != len && !eof ) {        /* error:  didn't get the whole file */
140         free( buf );
141         return( LDAP_TMPL_ERR_FILE );
142     }
143
144     rc = ldap_init_templates_buf( buf, rlen, tmpllistp );
145     free( buf );
146
147     return( rc );
148 }
149
150
151 int
152 ldap_init_templates_buf( char *buf, long buflen,
153         struct ldap_disptmpl **tmpllistp )
154 {
155     int                         rc, version;
156     char                        **toks;
157     struct ldap_disptmpl        *prevtmpl, *tmpl;
158
159     *tmpllistp = prevtmpl = NULLDISPTMPL;
160
161     if ( next_line_tokens( &buf, &buflen, &toks ) != 2 ||
162             strcasecmp( toks[ 0 ], "version" ) != 0 ) {
163         free_strarray( toks );
164         return( LDAP_TMPL_ERR_SYNTAX );
165     }
166     version = atoi( toks[ 1 ] );
167     free_strarray( toks );
168     if ( version != LDAP_TEMPLATE_VERSION ) {
169         return( LDAP_TMPL_ERR_VERSION );
170     }
171
172     while ( buflen > 0 && ( rc = read_next_tmpl( &buf, &buflen, &tmpl,
173             version )) == 0 && tmpl != NULLDISPTMPL ) {
174         if ( prevtmpl == NULLDISPTMPL ) {
175             *tmpllistp = tmpl;
176         } else {
177             prevtmpl->dt_next = tmpl;
178         }
179         prevtmpl = tmpl;
180     }
181
182     if ( rc != 0 ) {
183         ldap_free_templates( *tmpllistp );
184     }
185
186     return( rc );
187 }
188             
189
190
191 void
192 ldap_free_templates( struct ldap_disptmpl *tmpllist )
193 {
194     struct ldap_disptmpl        *tp, *nexttp;
195
196     if ( tmpllist != NULL ) {
197         for ( tp = tmpllist; tp != NULL; tp = nexttp ) {
198             nexttp = tp->dt_next;
199             free_disptmpl( tp );
200         }
201     }
202 }
203
204
205 static void
206 free_disptmpl( struct ldap_disptmpl *tmpl )
207 {
208     if ( tmpl != NULL ) {
209         if ( tmpl->dt_name != NULL ) {
210             free(  tmpl->dt_name );
211         }
212
213         if ( tmpl->dt_pluralname != NULL ) {
214             free( tmpl->dt_pluralname );
215         }
216
217         if ( tmpl->dt_iconname != NULL ) {
218             free( tmpl->dt_iconname );
219         }
220
221         if ( tmpl->dt_authattrname != NULL ) {
222             free( tmpl->dt_authattrname );
223         }
224
225         if ( tmpl->dt_defrdnattrname != NULL ) {
226             free( tmpl->dt_defrdnattrname );
227         }
228
229         if ( tmpl->dt_defaddlocation != NULL ) {
230             free( tmpl->dt_defaddlocation );
231         }
232
233         if (  tmpl->dt_oclist != NULL ) {
234             struct ldap_oclist  *ocp, *nextocp;
235
236             for ( ocp = tmpl->dt_oclist; ocp != NULL; ocp = nextocp ) {
237                 nextocp = ocp->oc_next;
238                 free_strarray( ocp->oc_objclasses );
239                 free( ocp );
240             }
241         }
242
243         if (  tmpl->dt_adddeflist != NULL ) {
244             struct ldap_adddeflist      *adp, *nextadp;
245
246             for ( adp = tmpl->dt_adddeflist; adp != NULL; adp = nextadp ) {
247                 nextadp = adp->ad_next;
248                 if( adp->ad_attrname != NULL ) {
249                     free( adp->ad_attrname );
250                 }
251                 if( adp->ad_value != NULL ) {
252                     free( adp->ad_value );
253                 }
254                 free( adp );
255             }
256         }
257
258         if (  tmpl->dt_items != NULL ) {
259             struct ldap_tmplitem        *rowp, *nextrowp, *colp, *nextcolp;
260
261             for ( rowp = tmpl->dt_items; rowp != NULL; rowp = nextrowp ) {
262                 nextrowp = rowp->ti_next_in_col;
263                 for ( colp = rowp; colp != NULL; colp = nextcolp ) {
264                     nextcolp = colp->ti_next_in_row;
265                     if ( colp->ti_attrname != NULL ) {
266                         free( colp->ti_attrname );
267                     }
268                     if ( colp->ti_label != NULL ) {
269                         free( colp->ti_label );
270                     }
271                     if ( colp->ti_args != NULL ) {
272                         free_strarray( colp->ti_args );
273                     }
274                     free( colp );
275                 }
276             }
277         }
278
279         free( tmpl );
280     }
281 }
282
283
284 struct ldap_disptmpl *
285 ldap_first_disptmpl( struct ldap_disptmpl *tmpllist )
286 {
287     return( tmpllist );
288 }
289
290
291 struct ldap_disptmpl *
292 ldap_next_disptmpl( struct ldap_disptmpl *tmpllist,
293         struct ldap_disptmpl *tmpl )
294 {
295     return( tmpl == NULLDISPTMPL ? tmpl : tmpl->dt_next );
296 }
297
298
299 struct ldap_disptmpl *
300 ldap_name2template( char *name, struct ldap_disptmpl *tmpllist )
301 {
302     struct ldap_disptmpl        *dtp;
303
304     for ( dtp = ldap_first_disptmpl( tmpllist ); dtp != NULLDISPTMPL;
305             dtp = ldap_next_disptmpl( tmpllist, dtp )) {
306         if ( strcasecmp( name, dtp->dt_name ) == 0 ) {
307             return( dtp );
308         }
309     }
310
311     return( NULLDISPTMPL );
312 }
313
314
315 struct ldap_disptmpl *
316 ldap_oc2template( char **oclist, struct ldap_disptmpl *tmpllist )
317 {
318     struct ldap_disptmpl        *dtp;
319     struct ldap_oclist          *oclp;
320     int                         i, j, needcnt, matchcnt;
321
322     if ( tmpllist == NULL || oclist == NULL || oclist[ 0 ] == NULL ) {
323         return( NULLDISPTMPL );
324     }
325
326     for ( dtp = ldap_first_disptmpl( tmpllist ); dtp != NULLDISPTMPL;
327                 dtp = ldap_next_disptmpl( tmpllist, dtp )) {
328         for ( oclp = dtp->dt_oclist; oclp != NULLOCLIST;
329                 oclp = oclp->oc_next ) {
330             needcnt = matchcnt = 0;
331             for ( i = 0; oclp->oc_objclasses[ i ] != NULL; ++i ) {
332                 for ( j = 0; oclist[ j ] != NULL; ++j ) {
333                     if ( strcasecmp( oclist[ j ], oclp->oc_objclasses[ i ] )
334                             == 0 ) {
335                         ++matchcnt;
336                     }
337                 }
338                 ++needcnt;
339             }
340
341             if ( matchcnt == needcnt ) {
342                 return( dtp );
343             }
344         }
345     }
346
347     return( NULLDISPTMPL );
348 }
349
350
351 struct ldap_tmplitem *
352 ldap_first_tmplrow( struct ldap_disptmpl *tmpl )
353 {
354     return( tmpl->dt_items );
355 }
356
357
358 struct ldap_tmplitem *
359 ldap_next_tmplrow( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row )
360 {
361     return( row == NULLTMPLITEM ? row : row->ti_next_in_col );
362 }
363
364
365 struct ldap_tmplitem *
366 ldap_first_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row )
367 {
368     return( row );
369 }
370
371
372 struct ldap_tmplitem *
373 ldap_next_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row,
374         struct ldap_tmplitem *col )
375 {
376     return( col == NULLTMPLITEM ? col : col->ti_next_in_row );
377 }
378
379
380 char **
381 ldap_tmplattrs( struct ldap_disptmpl *tmpl, char **includeattrs,
382         int exclude, unsigned long syntaxmask )
383 {
384 /*
385  * this routine should filter out duplicate attributes...
386  */
387     struct ldap_tmplitem        *tirowp, *ticolp;
388     int                 i, attrcnt, memerr;
389     char                **attrs;
390
391     attrcnt = 0;
392     memerr = 0;
393
394     if (( attrs = (char **)malloc( sizeof( char * ))) == NULL ) {
395         return( NULL );
396     }
397
398     if ( includeattrs != NULL ) {
399         for ( i = 0; !memerr && includeattrs[ i ] != NULL; ++i ) {
400             if (( attrs = (char **)realloc( attrs, ( attrcnt + 2 ) *
401                     sizeof( char * ))) == NULL || ( attrs[ attrcnt++ ] =
402                     strdup( includeattrs[ i ] )) == NULL ) {
403                 memerr = 1;
404             } else {
405                 attrs[ attrcnt ] = NULL;
406             }
407         }
408     }
409
410     for ( tirowp = ldap_first_tmplrow( tmpl );
411             !memerr && tirowp != NULLTMPLITEM;
412             tirowp = ldap_next_tmplrow( tmpl, tirowp )) {
413         for ( ticolp = ldap_first_tmplcol( tmpl, tirowp );
414                 ticolp != NULLTMPLITEM;
415                 ticolp = ldap_next_tmplcol( tmpl, tirowp, ticolp )) {
416
417             if ( syntaxmask != 0 ) {
418                 if (( exclude &&
419                         ( syntaxmask & ticolp->ti_syntaxid ) != 0 ) ||
420                         ( !exclude &&
421                         ( syntaxmask & ticolp->ti_syntaxid ) == 0 )) {
422                     continue;
423                 }
424             }
425
426             if ( ticolp->ti_attrname != NULL ) {
427                 if (( attrs = (char **)realloc( attrs, ( attrcnt + 2 ) *
428                         sizeof( char * ))) == NULL || ( attrs[ attrcnt++ ] =
429                         strdup( ticolp->ti_attrname )) == NULL ) {
430                     memerr = 1;
431                 } else {
432                     attrs[ attrcnt ] = NULL;
433                 }
434             }
435         }
436     }
437
438     if ( memerr || attrcnt == 0 ) {
439         for ( i = 0; i < attrcnt; ++i ) {
440             if ( attrs[ i ] != NULL ) {
441                 free( attrs[ i ] );
442             }
443         }
444
445         free( (char *)attrs );
446         return( NULL );
447     }
448
449     return( attrs );
450 }
451
452
453 static int
454 read_next_tmpl( char **bufp, long *blenp, struct ldap_disptmpl **tmplp,
455         int dtversion )
456 {
457     int                         i, j, tokcnt, samerow, adsource;
458     char                        **toks, *itemopts;
459     struct ldap_disptmpl        *tmpl;
460     struct ldap_oclist          *ocp, *prevocp;
461     struct ldap_adddeflist      *adp, *prevadp;
462     struct ldap_tmplitem        *rowp, *ip, *previp;
463
464     *tmplp = NULL;
465
466     /*
467      * template name comes first
468      */
469     if (( tokcnt = next_line_tokens( bufp, blenp, &toks )) != 1 ) {
470         free_strarray( toks );
471         return( tokcnt == 0 ? 0 : LDAP_TMPL_ERR_SYNTAX );
472     }
473
474     if (( tmpl = (struct ldap_disptmpl *)calloc( 1,
475             sizeof( struct ldap_disptmpl ))) == NULL ) {
476         free_strarray( toks );
477         return(  LDAP_TMPL_ERR_MEM );
478     }
479     tmpl->dt_name = toks[ 0 ];
480     free( (char *)toks );
481
482     /*
483      * template plural name comes next
484      */
485     if (( tokcnt = next_line_tokens( bufp, blenp, &toks )) != 1 ) {
486         free_strarray( toks );
487         free_disptmpl( tmpl );
488         return( LDAP_TMPL_ERR_SYNTAX );
489     }
490     tmpl->dt_pluralname = toks[ 0 ];
491     free( (char *)toks );
492
493     /*
494      * template icon name is next
495      */
496     if (( tokcnt = next_line_tokens( bufp, blenp, &toks )) != 1 ) {
497         free_strarray( toks );
498         free_disptmpl( tmpl );
499         return( LDAP_TMPL_ERR_SYNTAX );
500     }
501     tmpl->dt_iconname = toks[ 0 ];
502     free( (char *)toks );
503
504     /*
505      * template options come next
506      */
507     if (( tokcnt = next_line_tokens( bufp, blenp, &toks )) < 1 ) {
508         free_strarray( toks );
509         free_disptmpl( tmpl );
510         return( LDAP_TMPL_ERR_SYNTAX );
511     }
512     for ( i = 0; toks[ i ] != NULL; ++i ) {
513         for ( j = 0; tmploptions[ j ] != NULL; ++j ) {
514             if ( strcasecmp( toks[ i ], tmploptions[ j ] ) == 0 ) {
515                 tmpl->dt_options |= tmploptvals[ j ];
516             }
517         }
518     }
519     free_strarray( toks );
520
521     /*
522      * object class list is next
523      */
524     while (( tokcnt = next_line_tokens( bufp, blenp, &toks )) > 0 ) {
525         if (( ocp = (struct ldap_oclist *)calloc( 1,
526                 sizeof( struct ldap_oclist ))) == NULL ) {
527             free_strarray( toks );
528             free_disptmpl( tmpl );
529             return( LDAP_TMPL_ERR_MEM );
530         }
531         ocp->oc_objclasses = toks;
532         if ( tmpl->dt_oclist == NULL ) {
533             tmpl->dt_oclist = ocp;
534         } else {
535             prevocp->oc_next = ocp;
536         }
537         prevocp = ocp;
538     }
539     if ( tokcnt < 0 ) {
540         free_disptmpl( tmpl );
541         return( LDAP_TMPL_ERR_SYNTAX );
542     }
543
544     /*
545      * read name of attribute to authenticate as
546      */
547     if (( tokcnt = next_line_tokens( bufp, blenp, &toks )) != 1 ) {
548         free_strarray( toks );
549         free_disptmpl( tmpl );
550         return( LDAP_TMPL_ERR_SYNTAX );
551     }
552     if ( toks[ 0 ][ 0 ] != '\0' ) {
553         tmpl->dt_authattrname = toks[ 0 ];
554     } else {
555         free( toks[ 0 ] );
556     }
557     free( (char *)toks );
558
559     /*
560      * read default attribute to use for RDN
561      */
562     if (( tokcnt = next_line_tokens( bufp, blenp, &toks )) != 1 ) {
563         free_strarray( toks );
564         free_disptmpl( tmpl );
565         return( LDAP_TMPL_ERR_SYNTAX );
566     }
567     tmpl->dt_defrdnattrname = toks[ 0 ];
568     free( (char *)toks );
569
570     /*
571      * read default location for new entries
572      */
573     if (( tokcnt = next_line_tokens( bufp, blenp, &toks )) != 1 ) {
574         free_strarray( toks );
575         free_disptmpl( tmpl );
576         return( LDAP_TMPL_ERR_SYNTAX );
577     }
578     if ( toks[ 0 ][ 0 ] != '\0' ) {
579         tmpl->dt_defaddlocation = toks[ 0 ];
580     } else {
581         free( toks[ 0 ] );
582     }
583     free( (char *)toks );
584
585     /*
586      * read list of rules used to define default values for new entries
587      */
588     while (( tokcnt = next_line_tokens( bufp, blenp, &toks )) > 0 ) {
589         if ( strcasecmp( ADDEF_CONSTANT, toks[ 0 ] ) == 0 ) {
590             adsource = LDAP_ADSRC_CONSTANTVALUE;
591         } else if ( strcasecmp( ADDEF_ADDERSDN, toks[ 0 ] ) == 0 ) {
592             adsource = LDAP_ADSRC_ADDERSDN;
593         } else {
594             adsource = 0;
595         }
596         if ( adsource == 0 || tokcnt < 2 ||
597                 ( adsource == LDAP_ADSRC_CONSTANTVALUE && tokcnt != 3 ) ||
598                 ( adsource == LDAP_ADSRC_ADDERSDN && tokcnt != 2 )) {
599             free_strarray( toks );
600             free_disptmpl( tmpl );
601             return( LDAP_TMPL_ERR_SYNTAX );
602         }
603                 
604         if (( adp = (struct ldap_adddeflist *)calloc( 1,
605                 sizeof( struct ldap_adddeflist ))) == NULL ) {
606             free_strarray( toks );
607             free_disptmpl( tmpl );
608             return( LDAP_TMPL_ERR_MEM );
609         }
610         adp->ad_source = adsource;
611         adp->ad_attrname = toks[ 1 ];
612         if ( adsource == LDAP_ADSRC_CONSTANTVALUE ) {
613             adp->ad_value = toks[ 2 ];
614         }
615         free( toks[ 0 ] );
616         free( (char *)toks );
617
618         if ( tmpl->dt_adddeflist == NULL ) {
619             tmpl->dt_adddeflist = adp;
620         } else {
621             prevadp->ad_next = adp;
622         }
623         prevadp = adp;
624     }
625
626     /*
627      * item list is next
628      */
629     samerow = 0;
630     while (( tokcnt = next_line_tokens( bufp, blenp, &toks )) > 0 ) {
631         if ( strcasecmp( toks[ 0 ], "item" ) == 0 ) {
632             if ( tokcnt < 4 ) {
633                 free_strarray( toks );
634                 free_disptmpl( tmpl );
635                 return( LDAP_TMPL_ERR_SYNTAX );
636             }
637
638             if (( ip = (struct ldap_tmplitem *)calloc( 1,
639                     sizeof( struct ldap_tmplitem ))) == NULL ) {
640                 free_strarray( toks );
641                 free_disptmpl( tmpl );
642                 return( LDAP_TMPL_ERR_MEM );
643             }
644
645             /*
646              * find syntaxid from config file string
647              */
648             while (( itemopts = strrchr( toks[ 1 ], ',' )) != NULL ) {
649                 *itemopts++ = '\0';
650                 for ( i = 0; itemoptions[ i ] != NULL; ++i ) {
651                     if ( strcasecmp( itemopts, itemoptions[ i ] ) == 0 ) {
652                         break;
653                     }
654                 }
655                 if ( itemoptions[ i ] == NULL ) {
656                     free_strarray( toks );
657                     free_disptmpl( tmpl );
658                     return( LDAP_TMPL_ERR_SYNTAX );
659                 }
660                 ip->ti_options |= itemoptvals[ i ];
661             }
662
663             for ( i = 0; itemtypes[ i ] != NULL; ++i ) {
664                 if ( strcasecmp( toks[ 1 ], itemtypes[ i ] ) == 0 ) {
665                     break;
666                 }
667             }
668             if ( itemtypes[ i ] == NULL ) {
669                 free_strarray( toks );
670                 free_disptmpl( tmpl );
671                 return( LDAP_TMPL_ERR_SYNTAX );
672             }
673
674             free( toks[ 0 ] );
675             free( toks[ 1 ] );
676             ip->ti_syntaxid = itemsynids[ i ];
677             ip->ti_label = toks[ 2 ];
678             if ( toks[ 3 ][ 0 ] == '\0' ) {
679                 ip->ti_attrname = NULL;
680                 free( toks[ 3 ] );
681             } else {
682                 ip->ti_attrname = toks[ 3 ];
683             }
684             if ( toks[ 4 ] != NULL ) {  /* extra args. */
685                 for ( i = 0; toks[ i + 4 ] != NULL; ++i ) {
686                     ;
687                 }
688                 if (( ip->ti_args = (char **) calloc( i + 1, sizeof( char * )))
689                         == NULL ) {
690                     free_disptmpl( tmpl );
691                     return( LDAP_TMPL_ERR_MEM );
692                 }
693                 for ( i = 0; toks[ i + 4 ] != NULL; ++i ) {
694                     ip->ti_args[ i ] = toks[ i + 4 ];
695                 }
696             }
697             free( (char *)toks );
698
699             if ( tmpl->dt_items == NULL ) {
700                 tmpl->dt_items = rowp = ip;
701             } else if ( samerow ) {
702                 previp->ti_next_in_row = ip;
703             } else {
704                 rowp->ti_next_in_col = ip;
705                 rowp = ip;
706             }
707             previp = ip;
708             samerow = 0;
709         } else if ( strcasecmp( toks[ 0 ], "samerow" ) == 0 ) {
710             free_strarray( toks );
711             samerow = 1;
712         } else {
713             free_strarray( toks );
714             free_disptmpl( tmpl );
715             return( LDAP_TMPL_ERR_SYNTAX );
716         }
717     }
718     if ( tokcnt < 0 ) {
719         free_disptmpl( tmpl );
720         return( LDAP_TMPL_ERR_SYNTAX );
721     }
722
723     *tmplp = tmpl;
724     return( 0 );
725 }