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