]> git.sur5r.net Git - openldap/blob - contrib/whois++/command.c
Protoized, moved extern definitions to .h files, fixed related bugs.
[openldap] / contrib / whois++ / command.c
1 #if !defined(lint)
2 static char copyright[] = "Copyright 1992 The University of Adelaide";
3 #endif
4
5 /*
6  *                      C O M M A N D
7  *
8  * Author:      Mark R. Prior
9  *              Communications and Systems Branch
10  *              Information Technology Division
11  *              The University of Adelaide
12  * E-mail:      mrp@itd.adelaide.edu.au
13  * Date:        October 1992
14  * Version:     1.8
15  * Description:
16  *              Interpret the command sent by the client
17  *
18  * Redistribution and use in source and binary forms are permitted
19  * provided that the above copyright notice and this paragraph are
20  * duplicated in all such forms and that any documentation,
21  * advertising materials, and other materials related to such
22  * distribution and use acknowledge that the software was developed
23  * by the University of Adelaide. The name of the University may not
24  * be used to endorse or promote products derived from this software
25  * without specific prior written permission.
26  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
28  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  */
30
31 #include "whois++.h"
32 #include <stdlib.h>
33 extern int getdtablesize (void);
34
35 #define isspecial(c)    ( (c) == ',' || (c) == ';' || (c) == ':' || (c) == '=' )
36
37 static  char    **component = NULL;
38 static  int     numberOfComponents;
39 static  int     components = 10;
40
41 static int
42 getToken( char *token )
43 {
44         static char     *buffer = NULL;
45         static int      idx;
46         char            ch;
47         fd_set          readfds;
48         struct timeval  timeout;
49         int             i, status, tablesize;
50
51         if ( buffer == NULL ) {
52                 tablesize = getdtablesize();
53
54 #ifdef FD_SETSIZE
55                 if ( tablesize > FD_SETSIZE ) {
56                         tablesize = FD_SETSIZE;
57                 }
58 #endif  /* FD_SETSIZE */
59
60                 timeout.tv_sec = 60;
61                 timeout.tv_usec = 0;
62                 FD_ZERO( &readfds );
63                 FD_SET( fileno( stdin ), &readfds );
64
65                 if ( (status = select( tablesize, &readfds, 0, 0, &timeout )) <= 0 ) {
66                         if ( status < 0 )
67                                 printFormatted( lineLength, TRUE, stdout,
68                                         "select: %s", strerror( errno ) );
69                         else
70                                 printFormatted( lineLength, TRUE, stdout,
71                                         "Connection timed out waiting for input." );
72                         exit( 1 );
73                 }
74 /**/            /*
75                  * We really should determine how many characters are
76                  * waiting for us and then malloc that amount rather than
77                  * just guessing!
78                  */
79                 if ( ( buffer = (char *)malloc(BUFSIZ) ) == NULL
80                         || fgets( buffer, BUFSIZ, stdin ) == NULL ) {
81                         *token = '\0';
82                         return EOF;
83                 }
84                 idx = 0;
85                 i = strlen( buffer );
86                 while ( i-- > 0 && ( buffer[i] == '\r' || buffer[i] == '\n' ) )
87                         buffer[i] = '\0';
88                 if ( log )
89                         syslog( LOG_INFO, "Whois++ Query: %s", buffer );
90         }
91         while ( buffer[idx] != '\0' && isspace( buffer[idx] ) )
92                 idx++;
93         token[0] = buffer[idx++];
94         token[1] = '\0';
95         switch ( token[0] ) {
96         case '\0':
97                 strcpy( token, "<end of line>" );
98                 free( buffer );
99                 buffer = NULL;
100                 return EOF;
101
102         case '^':
103                 return TEMPLATE;
104
105         case '!':
106                 return HANDLE;
107
108         case '.':
109                 return ATTRIBUTE;
110
111         case '#':
112                 return VALUE;
113
114         case '*':
115                 return SEARCH_ALL;
116
117         case '?':
118                 return HELP;
119
120         case ':':
121                 return COLON;
122
123         case ';':
124                 return SEMICOLON;
125
126         case ',':
127                 return COMMA;
128
129         case '=':
130                 return EQUALS;
131
132         case '"':
133                 i = 0;
134                 do {
135                         ch = buffer[idx++];
136                         if ( ch == '\\' && buffer[idx] != '\0' )
137                                 token[i++] = buffer[idx++];
138                         else
139                                 token[i++] = ch;
140                 } while ( ch != '\0' && ch != '"' );
141                 if ( ch == '\0' ) {
142                         printFormatted( lineLength, TRUE, stdout,
143                                 "Trailing \" missing" );
144                         idx--;
145                 }
146                 token[--i] = '\0';
147                 return SEARCH;
148
149         default:
150                 i = 1;
151                 do {
152                         ch = buffer[idx++];
153                         if ( ch == '\\' && buffer[idx] != '\0' )
154                                 token[i++] = buffer[idx++];
155                         else
156                                 token[i++] = ch;
157                 } while ( ch != '\0' && !isspace( ch ) && !isspecial( ch ) );
158                 token[--i] = '\0';
159                 idx--;
160 /**/            /*
161                  * The following is a brute force lookup, once the names
162                  * have settled down this should change to a hash table,
163                  * or something similar.
164                  */
165                 if ( EQ( token, "help" ) )
166                         return HELP;
167                 else if ( EQ( token, "list" ) )
168                         return LIST;
169                 else if ( EQ( token, "show" ) )
170                         return SHOW;
171                 else if ( EQ( token, "constraints" ) )
172                         return CONSTRAINTS;
173                 else if ( EQ( token, "describe" ) )
174                         return DESCRIBE;
175                 else if ( EQ( token, "version" ) )
176                         return VERSION;
177                 else if ( EQ( token, "template" ) )
178                         return TEMPLATE;
179                 else if ( EQ( token, "handle" ) )
180                         return HANDLE;
181                 else if ( EQ( token, "attribute" ) )
182                         return ATTRIBUTE;
183                 else if ( EQ( token, "value" ) )
184                         return VALUE;
185                 else if ( EQ( token, "full" ) )
186                         return FULL;
187                 else if ( EQ( token, "abridged" ) )
188                         return ABRIDGED;
189                 else if ( EQ( token, "summary" ) )
190                         return SUMMARY;
191                 else if ( EQ( token, "format" ) )
192                         return FORMAT;
193                 else if ( EQ( token, "hold" ) )
194                         return HOLD;
195                 else if ( EQ( token, "maxhits" ) )
196                         return MAXHITS;
197                 else if ( EQ( token, "match" ) )
198                         return MATCH;
199                 else if ( EQ( token, "linelength" ) )
200                         return LINE_LENGTH;
201                 else if ( EQ( token, "command" ) )
202                         return COMMAND;
203                 else if ( EQ( token, "trace" ) )
204                         return TRACE;
205                 else
206                         return SEARCH;
207         }
208 }
209
210 static int
211 term(
212         int     token,
213         char    *value,
214         char    *attribute,
215         int     *specifier,
216         int     *soundex
217 )
218 {
219         char    buffer[BUFSIZ], temp[BUFSIZ];
220         int     iterations;
221
222         *soundex = FALSE;
223         switch ( token ) {
224         case ATTRIBUTE: /* . */
225         case VALUE:     /* # */
226         case HANDLE:    /* ! */
227         case TEMPLATE:  /* ^ */
228         case SEARCH_ALL:/* * */
229                 *specifier = token;
230                 if ( strlen( value ) > 1 ) {
231                         /* fullname used, so expect an equals sign */
232                         if ( getToken( buffer ) != EQUALS ) {
233                                 printFormatted( lineLength, TRUE, stdout,
234                                         "\"=\" expected" );
235                                 return ERROR;
236                         } else
237                                 token = getToken( value );
238                 } else 
239                         token = getToken( value );
240                 if ( token != COMMA && token != SEMICOLON && token != EQUALS
241                         && token != COLON && token != EOF ) {
242                         token = getToken( buffer );
243                         break;
244                 }
245
246         case COMMA:
247         case SEMICOLON:
248         case EQUALS:
249         case COLON:
250         case EOF:
251                 printFormatted( lineLength, TRUE, stdout,
252                         "Expected search string but got \"%s\"", buffer );
253                 return ERROR;
254
255         default:
256                 *specifier = SEARCH_ALL;
257                 if ( ( token = getToken( buffer ) ) == EQUALS ) {
258                         strcpy( attribute, value );
259                         token = getToken( value );
260                         if ( token == COMMA || token == SEMICOLON
261                                 || token == COLON || token == EOF ) {
262                                 printFormatted( lineLength, TRUE, stdout,
263                                         "Syntax error, string expected." );
264                                 return ERROR;
265                         }
266                         token = getToken( buffer );
267                 }
268         }
269
270         while ( token != COMMA && token != SEMICOLON && token != COLON
271                 && token != EOF ) {
272                 if ( *value != '\0' )
273                         strcat( value, " " );
274                 strcat( value, buffer );
275                 token = getToken( buffer );
276         }
277         iterations = 2;
278         while ( token == COMMA ) {
279                 token = getToken( buffer );
280                 switch ( token ) {
281                 case MATCH:
282                         iterations = 0;
283                         if ( ( token = getToken( buffer ) ) != EQUALS ) {
284                                 printFormatted( lineLength, TRUE, stdout,
285                                         "\"=\" expected" );
286                         } else
287                                 token = getToken( buffer );
288                         if ( EQ( buffer, "exact" ) )
289                                 *soundex = FALSE;
290                         else if ( EQ( buffer, "fuzzy" ) )
291                                 *soundex = TRUE;
292                         else
293                                 printFormatted( lineLength, TRUE, stdout,
294                                         "Unrecognised search type" );
295                         token = getToken( buffer );
296                         break;
297
298                 default:
299                         if ( iterations == 0 ) {
300                                 /* obviously an unrecognised constraint */
301                                 printFormatted( lineLength, TRUE, stdout,
302                                         "Constraint \"%s\" not supported",
303                                         buffer );
304                                 while ( ( token = getToken( buffer ) ) != EOF
305                                         && token != COMMA && token != COLON
306                                         && token != SEMICOLON )
307                                         ;
308                         } else {
309                                 strcpy( temp, buffer );
310                                 token = getToken( buffer );
311                                 if ( token == EQUALS ) {
312                                         iterations = 0;
313                                         printFormatted( lineLength, TRUE, stdout,
314                                                 "Constraint \"%s\" not supported",
315                                                 buffer );
316                                 }
317                                 while ( token != EOF && token != SEMICOLON
318                                         && token != COLON && token != COMMA ) {
319                                         if ( iterations > 0 ) {
320                                                 strcat( temp, " " );
321                                                 strcat( temp, buffer );
322                                         }
323                                         token = getToken( buffer );
324                                 }
325                                 if ( iterations > 0 ) {
326                                         printFormatted( lineLength, TRUE, stdout,
327                                                 "Assuming \"%s\" part of query and not an unrecognised constraint.", temp );
328                                         strcat( value, "," );
329                                         strcat( value, temp );
330                                 }
331                         }
332                         break;
333
334                 }
335                 iterations--;
336         }
337         if ( *value == '\0' ) {
338                 printFormatted( lineLength, TRUE, stdout,
339                         "Value not specified" );
340                 return ERROR;
341         }
342         if ( *specifier == NULL )
343                 *specifier = SEARCH_ALL;
344         return token;
345 }
346
347 static int
348 processTerm(
349         int     specifier,
350         int     soundex,
351         char    *buffer,
352         char    *attribute,
353         char    *value
354 )
355 {
356         char    *s, *t;
357         char    query[BUFSIZ];
358         char    **reallocResult;
359
360         switch ( specifier ) {
361         case SEARCH_ALL:
362                 if ( numberOfComponents+3 >= components ) {
363                         components += 10;
364                         reallocResult = (char **)realloc(component, sizeof(char **)*components);
365                         if ( reallocResult == NULL ) {
366                                 printFormatted( lineLength, TRUE, stdout,
367                                         "Realloc failed" );
368                                 return ERROR;
369                         } else
370                                 component = reallocResult;
371                 }
372                 if ( attribute != NULL && *attribute != '\0' ) {
373                         /* The user obviously knows what they are doing */
374                         sprintf( query, "(%s%s%s)", attribute,
375                                 (soundex)?"~=":"=", buffer );
376                 } else {
377                         if ( ( s = strchr( buffer, ',' ) ) != NULL ) {
378                                 *s++ = '\0';
379                                 while ( *s && isspace( *s ) )
380                                         s++;
381                                 sprintf( query, "(sn%s%s)",
382                                         (soundex)?"~=":"=", buffer );
383                                 component[numberOfComponents++] = strdup( query );
384                                 /* let's just make sure there is no title */
385                                 if ( ( t = strrchr( s, ',' ) ) != NULL ) {
386                                         *t++ = '\0';
387                                         while ( *t && isspace( *t ) )
388                                                 t++;
389                                         sprintf( query, "(personalTitle%s%s)",
390                                                 (soundex)?"~=":"=", t );
391                                         component[numberOfComponents++] = strdup( query );
392                                 }
393                                 sprintf( query, "%s %s", s, buffer );
394                                 strcpy( buffer, query );
395                         } else if ( strncasecmp( buffer, "first ", 6 ) == 0 ) {
396                                 sprintf( query, "%s *", &buffer[6] );
397                                 strcpy( buffer, query );
398                         }
399                         if ( ( s = strchr( buffer, '@' ) ) != NULL ) {
400                                 *s++ = '\0';
401                                 if ( *buffer == '\0' ) /* no username */
402                                         sprintf( query, "(mail=*@%s)", s );
403                                 else if ( *s == '\0' ) /* no host */
404                                         sprintf( query, "(|(mail=%s@*)(userid=%s))",
405                                                 buffer, buffer );
406                                 else
407                                         sprintf( query, "(mail=%s@%s)",
408                                                 buffer, s );
409                                 if ( soundex )
410                                         printFormatted( lineLength, TRUE, stdout,
411                                                 "Fuzzy matching not supported on e-mail address queries" );
412                         } else if ( strchr( buffer, ' ' ) == NULL ) {
413                                 sprintf( query,
414                                         "(|(sn%s%s)(userid%s%s)(l%s%s)(ou%s%s)\
415 (&(cn%s%s)(!(objectClass=person))))",
416                                         (soundex)?"~=":"=", buffer,
417                                         (soundex)?"~=":"=", buffer,
418                                         (soundex)?"~=":"=", buffer,
419                                         (soundex)?"~=":"=", buffer,
420                                         (soundex)?"~=":"=", buffer );
421                         } else {
422 #if defined(UOFA)
423                                 sprintf( query, "(|(l%s%s)(ou%s%s)(preferredName%s%s)",
424                                         (soundex)?"~=":"=", buffer,
425                                         (soundex)?"~=":"=", buffer,
426                                         (soundex)?"~=":"=", buffer );
427 #else
428                                 sprintf( query, "(|(l%s%s)(ou%s%s)",
429                                         (soundex)?"~=":"=", buffer,
430                                         (soundex)?"~=":"=", buffer );
431 #endif
432                                 /*
433                                  * If LDAP and/or Quipu didn't strip spaces
434                                  * then this would be different but as it does
435                                  * this is easy :-) but it also means we might
436                                  * get false hits.
437                                  */
438                                 if ( soundex ) {
439                                         strcat( query, "(cn~=" );
440                                         strcat( query, buffer );
441                                 } else {
442                                         strcat( query, "(cn=*" );
443                                         strcat( query, strtok( buffer, " " ) );
444                                         while ( ( s = strtok( NULL, " " ) ) != NULL ) {
445                                                 strcat( query, " * " );
446                                                 strcat( query, s );
447                                         }
448                                 }
449                                 strcat( query, "))" );
450                         }
451                 }
452                 component[numberOfComponents++] = strdup( query );
453                 break;
454
455         case ATTRIBUTE:
456                 if ( numberOfComponents+1 >= components ) {
457                         components += 10;
458                         reallocResult = (char **)realloc(component, sizeof(char **)*components);
459                         if ( reallocResult == NULL ) {
460                                 printFormatted( lineLength, TRUE, stdout,
461                                         "Realloc failed" );
462                                 return ERROR;
463                         } else
464                                 component = reallocResult;
465                 }
466                 if ( *value != '\0' ) {
467                         sprintf( query, "(%s%s%s)", buffer,
468                                 (soundex)?"~=":"=", value );
469                         component[numberOfComponents++] = strdup( query );
470                         *value = '\0';
471                 } else {
472                         if ( *attribute != '\0' ) {
473                                 sprintf( query, "(%s%s*)", attribute,
474                                         (soundex)?"~=":"=" );
475                                 component[numberOfComponents++] = strdup( query );
476                         }
477                         strcpy( attribute, buffer );
478                 }
479                 break;
480
481         case TEMPLATE:
482                 if ( numberOfComponents+1 >= components ) {
483                         components += 10;
484                         reallocResult = (char **)realloc(component, sizeof(char **)*components);
485                         if ( reallocResult == NULL ) {
486                                 printFormatted( lineLength, TRUE, stdout,
487                                         "Realloc failed" );
488                                 return ERROR;
489                         } else
490                                 component = reallocResult;
491                 }
492                 sprintf( query, "(objectClass%s%s)",
493                         (soundex)?"~=":"=", templateToObjectClass( buffer ) );
494                 component[numberOfComponents++] = strdup( query );
495                 break;
496
497         case VALUE:
498                 if ( *attribute != '\0' ) {
499                         if ( numberOfComponents+1 >= components ) {
500                                 components += 10;
501                                 reallocResult = (char **)realloc(component, sizeof(char **)*components);
502                                 if ( reallocResult == NULL ) {
503                                         printFormatted( lineLength, TRUE, stdout,
504                                                 "Realloc failed" );
505                                         return ERROR;
506                                 } else
507                                         component = reallocResult;
508                         }
509                         sprintf( query, "(%s%s%s)", attribute,
510                                 (soundex)?"~=":"=", buffer );
511                         component[numberOfComponents++] = strdup( query );
512                         *attribute = '\0';
513                 } else {
514                         if ( *value != '\0' )
515                                 printFormatted( lineLength, TRUE, stdout,
516                                         "Ignoring old value (%s)", value );
517                         strcpy( value, buffer );
518                 }
519                 break;
520
521         case HANDLE:
522                 if ( numberOfComponents+1 >= components ) {
523                         components += 10;
524                         reallocResult = (char **)realloc(component, sizeof(char **)*components);
525                         if ( reallocResult == NULL ) {
526                                 printFormatted( lineLength, TRUE, stdout,
527                                         "Realloc failed" );
528                                 return ERROR;
529                         } else
530                                 component = reallocResult;
531                 }
532                 component[numberOfComponents++] = strdup( buffer );
533                 return READ;
534
535         }
536         return SEARCH;
537 }
538
539 int
540 parseCommand( char *query )
541 {
542         /*
543          * This procedure reads the string sent by the user and breaks it
544          * down into command to execute.
545          */
546         char    buffer[BUFSIZ], attribute[BUFSIZ], objectClass[BUFSIZ],
547                 value[BUFSIZ];
548         char    **reallocResult;
549         int     command, specificName, length, token, i, j, specifier, soundex;
550         int     trace = FALSE;
551
552         switch ( command = getToken( buffer ) ) {
553         case COMMAND:
554         case CONSTRAINTS:
555         case DESCRIBE:
556         case VERSION:
557                 /* <command> */
558                 token = getToken( buffer );
559                 break;
560
561         case HELP:
562         case LIST:
563                 /* <command> [ <string> ] */
564                 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
565                         strcpy( query, buffer );
566                         token = getToken( buffer );
567                 } else
568                         *query = '\0';
569                 break;
570
571         case SHOW:
572                 /* "show" <string> */
573                 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
574                         strcpy( query, buffer );
575                         token = getToken( buffer );
576                 } else {
577                         printFormatted( lineLength, TRUE, stdout,
578                                 "Show must have a parameter" );
579                         return ERROR;
580                 }
581                 break;
582
583         default:
584                 /* <term> [ ";" <term> ] */
585                 *attribute = '\0';
586                 *value = '\0';
587                 soundex = FALSE;
588                 numberOfComponents = 0;
589                 if ( ( component = (char **)malloc(sizeof(char **)*components) ) == NULL ) {
590                         printFormatted( lineLength, TRUE, stdout,
591                                 "Malloc failed" );
592                         return ERROR;
593                 }
594                 if ( ( token = term( command, buffer, attribute, &specifier,
595                         &soundex ) ) != ERROR )
596                         command = processTerm( specifier, soundex, buffer,
597                                 attribute, value );
598                 else
599                         return ERROR;
600                 if ( token == SEMICOLON ) {
601                         if ( command == READ ) {
602                                 printFormatted( lineLength, TRUE, stdout,
603                                         "Multiple components on a Handle query not supported." );
604                                 return ERROR;
605                         }
606                         do {
607                                 soundex = FALSE;
608                                 token = getToken( buffer );
609                                 token = term( token, buffer, attribute,
610                                         &specifier, &soundex );
611                                 command = processTerm( specifier, soundex,
612                                         buffer, attribute, value );
613                                 if ( command == READ ) {
614                                         printFormatted( lineLength, TRUE, stdout,
615                                                 "Multiple components on a Handle query not supported." );
616                                         return ERROR;
617                                 } else if ( command == ERROR )
618                                         return ERROR;
619                         } while ( token == SEMICOLON );
620                 }
621                 /*
622                  * Need to tidy up outstanding single value or attribute terms
623                  */
624                 if ( *attribute != '\0' ) {
625                         if ( numberOfComponents+1 >= components ) {
626                                 components += 10;
627                                 reallocResult = (char **)realloc(component, sizeof(char **)*components);
628                                 if ( reallocResult == NULL ) {
629                                         printFormatted( lineLength, TRUE, stdout,
630                                                 "Realloc failed" );
631                                         return ERROR;
632                                 } else
633                                         component = reallocResult;
634                         }
635                         sprintf( query, "(%s%s*)", attribute,
636                                 (soundex)?"~=":"=" );
637                         component[numberOfComponents++] = strdup( query );
638                 }
639                 if ( *value != '\0' )
640                         if ( processTerm( SEARCH_ALL, soundex, value, NULL, NULL ) == ERROR )
641                                 return ERROR;
642                 if ( numberOfComponents == 0 ) {
643                         printFormatted( lineLength, TRUE, stdout,
644                                 "NULL query." );
645                         return ERROR;
646                 } else if ( numberOfComponents == 1 )
647                         strcpy( query, component[0] );
648                 else {
649                         strcpy( query, "(&" );
650                         for ( i = 0; i < numberOfComponents; i++ )
651                                 strcat( query, component[i] );
652                         strcat( query, ")" );
653                 }
654                 free( component );
655                 break;
656
657         }
658         if ( token == COLON ) { /* global constraints */
659                 do {
660                         token = getToken( buffer );
661                         switch ( token ) {
662                         case FORMAT:
663                                 if ( ( token = getToken( buffer ) ) != EQUALS ) {
664                                         printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
665                                 } else
666                                         token = getToken( buffer );
667                                 switch ( token ) {
668                                 case FULL:
669                                 case ABRIDGED:
670                                 case HANDLE:
671                                 case SUMMARY:
672                                         if ( outputFormat != NULL )
673                                                 printFormatted( lineLength, TRUE, stdout, "Only one response format can be specified." );
674                                         else
675                                                 outputFormat = token;
676                                         break;
677
678                                 default:
679                                         printFormatted( lineLength, TRUE, stdout, "Unrecognised format specifier" );
680                                 }
681                                 token = getToken( buffer );
682                                 break;
683
684                         case HOLD:
685                                 holdConnection = TRUE;
686                                 token = getToken( buffer );
687                                 break;
688
689                         case MAXHITS:
690                                 if ( ( token = getToken( buffer ) ) != EQUALS ) {
691                                         printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
692                                 } else
693                                         token = getToken( buffer );
694                                 if ( (maxHits = atoi( buffer )) < 1 
695                                         || maxHits > maximumSize ) {
696                                         printFormatted( lineLength, TRUE, stdout, "Invalid maxhits value, defaulting to %s", maximumSize );
697                                         maxHits = maximumSize;
698                                 }
699                                 token = getToken( buffer );
700                                 break;
701
702                         case LANGUAGE:
703                                 if ( ( token = getToken( buffer ) ) != EQUALS ) {
704                                         printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
705                                 } else
706                                         token = getToken( buffer );
707 /**/                            /* need to save this value and lookup locale */
708                                 printFormatted( lineLength, TRUE, stdout,
709                                         "Language not currently implemented" );
710                                 token = getToken( buffer );
711                                 break;
712
713                         case LINE_LENGTH:
714                                 if ( ( token = getToken( buffer ) ) != EQUALS ) {
715                                         printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
716                                 } else
717                                         token = getToken( buffer );
718                                 lineLength = atoi( buffer );
719                                 if ( lineLength < MIN_LINE_LENGTH
720                                         || lineLength > MAX_LINE_LENGTH ) {
721                                         printFormatted( lineLength, TRUE, stdout, "Invalid line length, using default %d", DEFAULT_LINE_LENGTH );
722                                         lineLength = DEFAULT_LINE_LENGTH;
723                                 }
724                                 token = getToken( buffer );
725                                 break;
726
727                         case TRACE:
728                                 trace = TRUE;
729                                 token = getToken( buffer );
730                                 break;
731
732                         default:
733                                 printFormatted( lineLength, TRUE, stdout, "Unrecognised global constraint \"%s\"", buffer );
734                                 while ( ( token = getToken( buffer ) ) != EOF
735                                         && token != COMMA )
736                                         ;
737                                 break;
738
739                         }
740                 } while ( token == COMMA );
741         }
742         if ( token != EOF ) {
743                 printFormatted( lineLength, TRUE, stdout,
744                         "Data following \"%s\" ignored.", buffer );
745                 while ( ( token = getToken( buffer ) ) != EOF )
746                         ;
747         }
748         if ( trace && ( command == READ || command == SEARCH ) )
749                 switch (command) {
750                 case READ:
751                         printFormatted( lineLength, TRUE, stdout,
752                                 "Attempting to read \"%s\"", query );
753                         break;
754
755                 case SEARCH:
756                         printFormatted( lineLength, TRUE, stdout,
757                                 "Searching using LDAP query %s", query );
758                         break;
759
760                 }
761         return command;
762 }