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