1 /* dict.c --
2  * Created: Fri Mar 28 19:16:29 1997 by faith@dict.org
3  * Copyright 1997-2002 Rickard E. Faith (faith@dict.org)
4  * Copyright 2002-2008 Aleksey Cheusov (vle@gmx.net)
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 1, or (at your option) any
9  * later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include "dict.h"
22 #include "parse.h"
23 #include "md5.h"
24 #include <stdarg.h>
25 
26 extern int         yy_flex_debug;
27        lst_List    dict_Servers;
28        FILE        *dict_output;
29        FILE        *dict_error;
30        int         formatted;
31        int         flush;
32 
33 const char *host_connected    = NULL;
34 const char *service_connected = NULL;
35 
36 static int address_family = AF_UNSPEC;
37 
38 #define BUFFERSIZE  2048
39 #define PIPESIZE     256
40 #define DEF_STRAT    "."
41 #define DEF_DB       "*"
42 
43 #define CMD_PRINT     0
44 #define CMD_DEFPRINT  1
45 #define CMD_CONNECT   2
46 #define CMD_CLIENT    3
47 #define CMD_AUTH      4
48 #define CMD_INFO      5
49 #define CMD_SERVER    6
50 #define CMD_DBS       7
51 #define CMD_STRATS    8
52 #define CMD_HELP      9
53 #define CMD_MATCH    10
54 #define CMD_DEFINE   11
55 #define CMD_SPELL    12
56 #define CMD_WIND     13
57 #define CMD_CLOSE    14
58 #define CMD_OPTION_MIME 15
59 
60 struct cmd {
61    int        command;
62    int        sent;
63    int        flag;
64    const char *host;
65    const char *service;
66    const char *database;
67    const char *strategy;
68    const char *word;
69    const char *client;
70    const char *user;
71    const char *key;
72    const char *comment;
73 };
74 
75 lst_List      cmd_list;
76 unsigned long client_defines;
77 unsigned long client_bytes;
78 unsigned long client_pipesize = PIPESIZE;
79 char          *client_text    = NULL;
80 
81 int option_mime = 0;
82 
83 int ex_status = 0;
set_ex_status(int status)84 static void set_ex_status (int status)
85 {
86    if (!ex_status)
87       ex_status = status;
88 }
89 
90 #define EXST_NO_MATCH                20
91 #define EXST_APPROX_MATCHES          21
92 #define EXST_NO_DATABASES            22
93 #define EXST_NO_STRATEGIES           23
94 
95 #define EXST_UNEXPECTED              30
96 #define EXST_TEMPORARILY_UNAVAILABLE 31
97 #define EXST_SHUTTING_DOWN           32
98 #define EXST_SYNTAX_ERROR            33
99 #define EXST_ILLEGAL_PARAM           34
100 #define EXST_COMMAND_NOT_IMPLEMENTED 35
101 #define EXST_PARAM_NOT_IMPLEMENTED   36
102 #define EXST_ACCESS_DENIED           37
103 #define EXST_AUTH_DENIED             38
104 #define EXST_INVALID_DB              39
105 #define EXST_INVALID_STRATEGY        40
106 #define EXST_CONNECTION_FAILED       41
107 
108 struct def {
109    lst_List   data;
110    const char *word;
111    const char *db;
112    const char *dbname;
113 };
114 
115 struct reply {
116    int        s;
117    const char *host;
118    const char *service;
119    const char *user;
120    const char *key;
121    const char *msgid;
122    const char *word;
123    lst_List   data;
124    int        retcode;
125    int        count;		/* definitions found */
126    int        matches;		/* matches found */
127    int        match;		/* doing match found */
128    int        listed;		/* Databases or strategies listed */
129    struct def *defs;
130 } cmd_reply;
131 
cpy(const char * s)132 static const char *cpy( const char *s )
133 {
134    if (!s || !*s) return NULL;
135    return str_copy(s);
136 }
137 
client_crlf(char * d,const char * s)138 static void client_crlf( char *d, const char *s )
139 {
140    int flag = 0;
141 
142    while (*s) {
143       if (*s == '\n') {
144 	 *d++ = '\r';
145 	 *d++ = '\n';
146 	 ++s;
147 	 ++flag;
148       } else {
149 	 *d++ = *s++;
150 	 flag = 0;
151       }
152    }
153    if (!flag) {
154       *d++ = '\r';
155       *d++ = '\n';
156    }
157    *d = '\0';
158 }
159 
unexpected_status_code(int expected_status,int act_status,const char * message)160 static void unexpected_status_code (
161    int expected_status, int act_status,
162    const char *message)
163 {
164    switch (act_status){
165    case CODE_TEMPORARILY_UNAVAILABLE:
166       fprintf (stderr,
167 	       "%s\n",
168 	       (message ? message : "Server temporarily unavailable"));
169       exit (EXST_TEMPORARILY_UNAVAILABLE);
170 
171    case CODE_SHUTTING_DOWN:
172       fprintf (stderr,
173 	       "%s\n", (message ? message : "Server shutting down"));
174       exit (EXST_SHUTTING_DOWN);
175 
176    case CODE_SYNTAX_ERROR:
177       fprintf (stderr,
178 	       "%s\n", (message ? message : "Command not recognized"));
179       exit (EXST_SYNTAX_ERROR);
180 
181    case CODE_ILLEGAL_PARAM:
182       fprintf (stderr,
183 	       "%s\n", (message ? message : "Illegal parameters"));
184       exit (EXST_ILLEGAL_PARAM);
185 
186    case CODE_COMMAND_NOT_IMPLEMENTED:
187       fprintf (stderr,
188 	       "%s\n", (message ? message : "Command not implemented"));
189       exit (EXST_COMMAND_NOT_IMPLEMENTED);
190 
191    case CODE_PARAM_NOT_IMPLEMENTED:
192       fprintf (stderr,
193 	       "%s\n", (message ? message : "Parameter not implemented"));
194       exit (EXST_PARAM_NOT_IMPLEMENTED);
195 
196    case CODE_ACCESS_DENIED:
197       fprintf (stderr,
198 	       "%s\n", (message ? message : "Access denied"));
199       exit (EXST_ACCESS_DENIED);
200 
201    case CODE_AUTH_DENIED:
202       fprintf (stderr,
203 	       "%s\n", (message ? message : "Authentication denied"));
204       exit (EXST_AUTH_DENIED);
205 
206    case CODE_INVALID_DB:
207       fprintf (stderr,
208 	       "%s\n", (message ? message : "Invalid database"));
209       exit (EXST_INVALID_DB);
210 
211    case CODE_INVALID_STRATEGY:
212       fprintf (stderr,
213 	       "%s\n", (message ? message : "Invalid strategy"));
214       exit (EXST_INVALID_STRATEGY);
215 
216    case CODE_NO_MATCH:
217       fprintf (stderr,
218 	       "%s\n", (message ? message : "No matches found"));
219       exit (EXST_NO_MATCH);
220 
221    case CODE_NO_DATABASES:
222       fprintf (stderr,
223 	       "%s\n", (message ? message : "No databases"));
224       exit (EXST_NO_DATABASES);
225 
226    case CODE_NO_STRATEGIES:
227       fprintf (stderr,
228 	       "%s\n", (message ? message : "No strategies"));
229       exit (EXST_NO_STRATEGIES);
230 
231    default:
232       fprintf (stderr,
233 	       "Unexpected status code %d (%s), wanted %d\n",
234 	       act_status,
235 	       (message ? message : "no message"),
236 	       expected_status);
237 
238       exit (EXST_UNEXPECTED);
239    }
240 }
241 
client_open_pager(void)242 static void client_open_pager( void )
243 {
244    dict_output = stdout;
245    dict_error  = stderr;
246 }
247 
client_close_pager(void)248 static void client_close_pager( void )
249 {
250    fflush (dict_output);
251 }
252 
client_read_text(int s)253 static lst_List client_read_text( int s )
254 {
255    lst_List l = lst_create();
256    char     line[BUFFERSIZE];
257    int      len;
258 
259    while ((len = net_read(s, line, BUFFERSIZE - 1)) >= 0) {
260       line [len] = 0;
261 
262       client_bytes += len;
263       PRINTF(DBG_RAW,("* Text: %s\n",line));
264       if (line[0] == '.' && line[1] == '\0') break;
265       if (len >= 2 && line[0] == '.' && line[1] == '.')
266 	 lst_append( l, xstrdup(line + 1) );
267       else
268 	 lst_append( l, xstrdup(line) );
269    }
270    if (len < 0) {
271        client_close_pager();
272        err_fatal_errno( __func__, "Error reading from socket\n" );
273    }
274    return l;
275 }
276 
client_print_text(lst_List l,int print_host_port)277 static void client_print_text( lst_List l, int print_host_port )
278 {
279    lst_Position p;
280    const char   *e;
281 
282    if (!l) return;
283 
284    if (formatted && print_host_port){
285       fprintf (dict_output, "%s\t%s\n", host_connected, service_connected);
286    }
287 
288    LST_ITERATE(l,p,e) fprintf( dict_output, "  %s\n", e );
289 }
290 
client_print_definitions(const struct def * r)291 static void client_print_definitions (const struct def *r)
292 {
293    if (formatted){
294       fprintf (dict_output, "%s\t%s\t", host_connected, service_connected);
295 
296       if (r -> dbname && r -> db)
297       {
298 	 fprintf( dict_output, "%s\t%s\n", r -> db, r -> dbname);
299       } else if (r -> dbname) {
300 	 fprintf( dict_output, "%s\n", r -> dbname );
301       } else if (r -> db) {
302 	 fprintf( dict_output, "%s\n", r -> db );
303       } else {
304 	 fprintf( dict_output, "unknown\n" );
305       }
306    }else{
307       fprintf (dict_output, "\nFrom ");
308       if (r -> dbname && r -> db)
309       {
310 	 fprintf( dict_output, "%s [%s]",
311 		  r -> dbname,
312 		  r -> db);
313       } else if (r -> dbname) {
314 	 fprintf( dict_output, "%s", r -> dbname );
315       } else if (r -> db) {
316 	 fprintf( dict_output, "%s", r -> db );
317       } else {
318 	 fprintf( dict_output, "unknown" );
319       }
320 
321       fprintf( dict_output, ":\n\n" );
322    }
323 
324    client_print_text( r -> data, 0 );
325 }
326 
client_print_matches(lst_List l,int flag,const char * word)327 static void client_print_matches( lst_List l, int flag, const char *word )
328 {
329    lst_Position p;
330    const char   *e;
331    arg_List     a;
332    const char   *prev = NULL;
333    const char   *last;
334    const char   *db;
335    static int   first = 1;
336    int          pos = 0;
337    int          len;
338    int          count;
339    int          empty_line_found = 0;
340 
341    const char  *arg0 = NULL;
342    const char  *arg1 = NULL;
343 
344    count = 0;
345    if (l) {
346       last = NULL;
347       LST_ITERATE(l,p,e) {
348 	 if (last && !strcmp(last,e)) continue;
349 	 ++count;
350 	 last = e;
351       }
352    } else {
353        if (flag)
354            fprintf( dict_error, "No matches found for \"%s\"\n", word );
355        set_ex_status (EXST_NO_MATCH);
356        return;
357    }
358 
359    last = NULL;
360    LST_ITERATE(l,p,e) {
361       /* skip MIME header */
362       if (option_mime && !empty_line_found){
363 	 empty_line_found = (e [0] == 0 ||
364 			     (e [0] == '\r' && e [1] == 0));
365 	 continue;
366       }
367 
368       /* */
369       if (last && !strcmp(last,e)) continue;
370       last = e;
371       a = arg_argify( e, 0 );
372       if (arg_count(a) != 2)
373 	 err_internal( __func__,
374 		       "MATCH command didn't return 2 args: \"%s\"\n", e );
375 
376       arg0 = arg_get (a,0);
377       arg1 = arg_get (a,1);
378 
379       if (formatted){
380 	 fprintf (dict_output, "%s\t%s\t%s\t%s\n",
381 		  host_connected, service_connected, arg0, arg1);
382       }else{
383 	 if ((db = str_find(arg0)) != prev) {
384 	    if (!first) fprintf( dict_output, "\n" );
385 	    first = 0;
386 	    fprintf( dict_output, "%s:", db );
387 	    prev = db;
388 	    pos = 6 + strlen(db);
389 	 }
390 	 len = strlen(arg1);
391 	 if (pos + len + 4 > 70) {
392 	    fprintf( dict_output, "\n" );
393 	    pos = 0;
394 	 }
395 	 if (strchr (arg1,' ')) {
396 	    fprintf( dict_output, "  \"%s\"", arg1 );
397 	    pos += len + 4;
398 	 } else {
399 	    fprintf( dict_output, "  %s", arg1 );
400 	    pos += len + 2;
401 	 }
402       }
403 
404       arg_destroy(a);
405    }
406    fprintf( dict_output, "\n" );
407 }
408 
client_print_listed(lst_List l)409 static void client_print_listed( lst_List l )
410 {
411    lst_Position p;
412    const char   *e;
413    arg_List     a;
414    int          colWidth = 10; /* minimum size of first column */
415    int          colMax = 16; /* maximum size of unragged first column */
416    char         format[32];
417    int          empty_line_found = 0;
418    int len;
419 
420    if (!l) return;
421    LST_ITERATE(l,p,e) {
422       /* skip MIME header */
423       if (option_mime && !empty_line_found){
424 	 empty_line_found = (e [0] == 0 ||
425 			     (e [0] == '\r' && e [1] == 0));
426 	 continue;
427       }
428 
429       /* */
430       a = arg_argify( e, 0 );
431       if (arg_count(a) != 2)
432 	 err_internal( __func__,
433 		       "SHOW command didn't return 2 args: \"%s\"\n", e );
434 
435       len = strlen (arg_get (a,0));
436 
437       if (len > colWidth && len <= colMax)
438         colWidth = len;
439 
440       arg_destroy(a);
441    }
442 
443    snprintf( format, sizeof (format), " %%-%ds %%s\n", colWidth );
444 
445    empty_line_found = 0;
446 
447    LST_ITERATE(l,p,e) {
448       /* skip MIME header */
449       if (option_mime && !empty_line_found){
450 	 empty_line_found = (e [0] == 0 ||
451 			     (e [0] == '\r' && e [1] == 0));
452 	 continue;
453       }
454 
455       /* */
456       a = arg_argify( e, 0 );
457 
458       if (formatted){
459 	 assert (host_connected);
460 	 assert (service_connected);
461 
462 	 fprintf (dict_output, "%s\t%s\t%s\t%s\n",
463 		  host_connected, service_connected, arg_get (a,0), arg_get (a,1));
464       }else{
465 	 fprintf (dict_output, format, arg_get (a,0), arg_get (a,1));
466       }
467 
468       arg_destroy(a);
469    }
470 }
471 
client_free_text(lst_List l)472 static void client_free_text( lst_List l )
473 {
474    lst_Position p;
475    char         *e;
476 
477    if (!l) return;
478    LST_ITERATE(l,p,e) {
479       if (e) xfree(e);
480    }
481    lst_destroy(l);
482 }
483 
client_read_status(int s,const char ** message,int * count,const char ** word,const char ** db,const char ** dbname,const char ** msgid)484 static int client_read_status( int s,
485 			       const char **message,
486 			       int *count,
487 			       const char **word,
488 			       const char **db,
489 			       const char **dbname,
490 			       const char **msgid )
491 {
492    static char buf[BUFFERSIZE];
493    arg_List    cmdline;
494    int         argc;
495    char        **argv;
496    int         status;
497    char        *start, *end, *p;
498    int         len;
499 
500    if ((len = net_read( s, buf, BUFFERSIZE )) < 0) {
501       client_close_pager();
502       err_fatal_errno( __func__, "Error reading from socket\n" );
503    }
504    client_bytes += len;
505    PRINTF(DBG_RAW,("* Read: %s\n",buf));
506 
507    if ((status = atoi(buf)) < 100) status = 600;
508    PRINTF(DBG_RAW,("* Status = %d\n",status));
509 
510    if (message && (p = strchr(buf, ' '))) *message = p + 1;
511 
512    if (count)  *count = 0;
513    if (word)   *word = NULL;
514    if (db)     *db = NULL;
515    if (dbname) *dbname = NULL;
516    if (msgid)  *msgid = NULL;
517 
518    switch (status) {
519    case CODE_HELLO:
520       if ((start = strrchr(buf, '<')) && (end = strrchr(buf,'>'))) {
521 	 end[1] = '\0';
522 	 *msgid = str_copy( start );
523 	 PRINTF(DBG_VERBOSE,("Msgid is \"%s\"\n",*msgid));
524       }
525       break;
526    case CODE_DATABASE_LIST:
527    case CODE_STRATEGY_LIST:
528    case CODE_DEFINITIONS_FOUND:
529    case CODE_MATCHES_FOUND:
530       cmdline = arg_argify(buf,0);
531       arg_get_vector( cmdline, &argc, &argv );
532       if (argc > 1 && count) *count = atoi(argv[1]);
533       arg_destroy(cmdline);
534       break;
535    case CODE_DEFINITION_FOLLOWS:
536       cmdline = arg_argify(buf,0);
537       arg_get_vector( cmdline, &argc, &argv );
538       if (argc > 1 && word)   *word   = str_find(argv[1]);
539       if (argc > 2 && db)     *db     = str_find(argv[2]);
540       if (argc > 3 && dbname) *dbname = str_find(argv[3]);
541       arg_destroy(cmdline);
542       break;
543    default:
544       break;
545    }
546 
547    return status;
548 }
549 
make_command(int command,...)550 static struct cmd *make_command( int command, ... )
551 {
552    va_list    ap;
553    struct cmd *c = xmalloc( sizeof( struct cmd ) );
554 
555    memset( c, 0, sizeof( struct cmd ) );
556    c->command = command;
557 
558    va_start( ap, command );
559    switch (command) {
560    case CMD_PRINT:
561       c->comment  = va_arg( ap, const char * );
562       break;
563    case CMD_DEFPRINT:
564       c->database = va_arg( ap, const char * );
565       c->word     = va_arg( ap, const char * );
566       c->flag     = va_arg( ap, int );
567       break;
568    case CMD_CONNECT:
569       c->host     = va_arg( ap, const char * );
570       c->service  = va_arg( ap, const char * );
571       c->user     = va_arg( ap, const char * );
572       c->key      = va_arg( ap, const char * );
573       break;
574    case CMD_CLIENT:
575       c->client   = va_arg( ap, const char * );
576       break;
577    case CMD_AUTH:
578       break;
579    case CMD_INFO:
580       c->database = va_arg( ap, const char * );
581       break;
582    case CMD_SERVER:
583       break;
584    case CMD_DBS:
585       break;
586    case CMD_STRATS:
587       break;
588    case CMD_HELP:
589       break;
590    case CMD_MATCH:
591       c->database = va_arg( ap, const char * );
592       c->strategy = va_arg( ap, const char * );
593       c->word     = va_arg( ap, const char * );
594       break;
595    case CMD_DEFINE:
596       c->database = va_arg( ap, const char * );
597       c->word     = va_arg( ap, const char * );
598       break;
599    case CMD_SPELL:
600       c->database = va_arg( ap, const char * );
601       c->word     = va_arg( ap, const char * );
602       break;
603    case CMD_WIND:
604       c->database = va_arg( ap, const char * );
605       c->strategy = va_arg( ap, const char * );
606       c->word     = va_arg( ap, const char * );
607       break;
608    case CMD_CLOSE:
609       break;
610    case CMD_OPTION_MIME:
611       break;
612    default:
613       err_internal( __func__, "Illegal command %d\n", command );
614    }
615    va_end( ap );
616 
617    return c;
618 }
619 
append_command(struct cmd * c)620 static void append_command( struct cmd *c )
621 {
622    if (!cmd_list) cmd_list = lst_create();
623    lst_append( cmd_list, c );
624 }
625 
626 
prepend_command(struct cmd * c)627 static void prepend_command( struct cmd *c )
628 {
629    if (!cmd_list) cmd_list = lst_create();
630    lst_push( cmd_list, c );
631 }
632 
633 
request(void)634 static void request( void )
635 {
636    char              b[BUFFERSIZE];
637    char              *buffer = alloca( client_pipesize );
638    char              *p = buffer;
639    lst_Position      pos;
640    struct cmd        *c = NULL;
641    unsigned char     digest[16];
642    char              hex[33];
643    struct MD5Context ctx;
644    int               i;
645    unsigned          len;
646    int               total = 0;
647    int               count = 0;
648 
649    *p = '\0';
650    c = lst_top(cmd_list);
651    if (c->command == CMD_CONNECT) {
652       cmd_reply.user = c->user;
653       cmd_reply.key  = c->key;
654    }
655 
656    LST_ITERATE(cmd_list,pos,c) {
657       b[0] = '\0';
658       len = 0;
659       PRINTF(DBG_PIPE,("* Looking at request %d\n",c->command));
660       if (c->sent) {
661 	 PRINTF(DBG_PIPE,("* Skipping\n"));
662 	 return;	/* FIXME!  Keep sending deeper things? */
663       }
664       ++count;
665       switch( c->command) {
666       case CMD_PRINT:                                                 break;
667       case CMD_DEFPRINT:                                              break;
668       case CMD_CONNECT:                                               break;
669       case CMD_AUTH:
670 	 if (!cmd_reply.key || !cmd_reply.user)                       break;
671 	 if (!cmd_reply.msgid)                                        goto end;
672 	 MD5Init(&ctx);
673 	 MD5Update(&ctx, (const unsigned char *) cmd_reply.msgid, strlen(cmd_reply.msgid));
674 	 MD5Update(&ctx, (const unsigned char *) cmd_reply.key, strlen(cmd_reply.key));
675 	 MD5Final(digest, &ctx );
676 	 for (i = 0; i < 16; i++)
677 	    sprintf( hex+2*i, "%02x", digest[i] );
678 	 hex[32] = '\0';
679 	 snprintf( b, BUFFERSIZE, "auth %s %s\n", cmd_reply.user, hex );
680 	 break;
681       case CMD_CLIENT:
682          if (client_text)
683             snprintf( b, BUFFERSIZE, "client \"%s: %s\"\n",
684                             c->client, client_text );
685          else
686             snprintf( b, BUFFERSIZE, "client \"%s\"\n", c->client );
687          break;
688       case CMD_INFO:   snprintf( b, BUFFERSIZE, "show info %s\n",
689                                        c->database );                 break;
690       case CMD_SERVER: snprintf( b, BUFFERSIZE, "show server\n" );    break;
691       case CMD_DBS:    snprintf( b, BUFFERSIZE, "show db\n" );        break;
692       case CMD_STRATS: snprintf( b, BUFFERSIZE, "show strat\n" );     break;
693       case CMD_HELP:   snprintf( b, BUFFERSIZE, "help\n" );           break;
694       case CMD_MATCH:
695 	 cmd_reply.word = c->word;
696 	 snprintf( b, BUFFERSIZE,
697 			 "match %s %s \"%s\"\n",
698 			 c->database, c->strategy, c->word );         break;
699       case CMD_DEFINE:
700 	 cmd_reply.word = c->word;
701 	 snprintf( b, BUFFERSIZE, "define %s \"%s\"\n",
702 			 c->database, c->word );                      break;
703       case CMD_SPELL:                                                 goto end;
704       case CMD_WIND:                                                  goto end;
705       case CMD_CLOSE:  snprintf( b, BUFFERSIZE, "quit\n" );           break;
706       case CMD_OPTION_MIME: snprintf( b, BUFFERSIZE, "option mime\n" );    break;
707       default:
708 	 err_internal( __func__, "Unknown command %d\n", c->command );
709       }
710       len = strlen(b);
711       if (total + len + 3 > client_pipesize) {
712 	 if (count == 1 && p == buffer && total == 0) {
713 				/* The buffer is too small, but we have to
714 				   send something...  */
715 	    PRINTF(DBG_PIPE,("* Reallocating buffer to %d bytes\n",len+1));
716 	    p = buffer = alloca( len + 1 );
717 	 } else {
718 	    break;
719 	 }
720       }
721       strcpy( p, b );
722       p += len;
723       total += len;
724       ++c->sent;
725       if (dbg_test(DBG_SERIAL)) break; /* Don't pipeline. */
726    }
727 
728 end:				/* Ready to send buffer, but are we
729 				   connected? */
730    if (!cmd_reply.s) {
731       c = lst_top(cmd_list);
732       if (c->command != CMD_CONNECT) {
733 	 err_internal( __func__, "Not connected, but no CMD_CONNECT\n" );
734       }
735       if ((cmd_reply.s = net_connect_tcp(
736 	      c->host, c->service ? c->service : DICT_DEFAULT_SERVICE, address_family )) < 0)
737       {
738 	 const char *message;
739 
740 	 switch (cmd_reply.s) {
741 	 case NET_NOHOST:     message = "Can't get host entry for";     break;
742 	 case NET_NOSERVICE:  message = "Can't get service entry for";  break;
743 	 case NET_NOPROTOCOL: message = "Can't get protocol entry for"; break;
744 	 case NET_NOCONNECT:  message = "Can't connect to";             break;
745 	 default:             message = "Unknown error for";            break;
746 	 }
747 	 PRINTF(DBG_VERBOSE,("%s %s.%s\n",
748 			     message,
749 			     c->host,
750 			     c->service ? c->service : DICT_DEFAULT_SERVICE));
751 	 if (lst_length(cmd_list) > 1) {
752 	    c = lst_nth_get(cmd_list,2);
753 	    if (c->command == CMD_CONNECT) {
754 				/* undo pipelining */
755 	       cmd_reply.s = 0;
756 	       if (!dbg_test(DBG_SERIAL)) {
757 		  LST_ITERATE(cmd_list,pos,c) c->sent = 0;
758 	       }
759 	       return;
760 	    }
761 	 }
762          client_close_pager();
763 	 fprintf (stderr, "Cannot connect to any servers%s\n",\
764 		  dbg_test(DBG_VERBOSE) ? "" : " (use -v to see why)" );
765 	 exit (EXST_CONNECTION_FAILED);
766       }
767       cmd_reply.host    = c->host;
768       cmd_reply.service = c->service ? c->service : DICT_DEFAULT_SERVICE;
769       cmd_reply.user    = c->user;
770       cmd_reply.key     = c->key;
771    }
772    if ((len = strlen(buffer))) {
773       char *pt;
774 
775       PRINTF(DBG_PIPE,("* Sending %d commands (%d bytes)\n",count,len));
776       PRINTF(DBG_RAW,("* Send/%d: %s",c->command,buffer));
777       pt = alloca(2*len);
778       client_crlf(pt,buffer);
779       net_write( cmd_reply.s, pt, strlen(pt) );
780    } else {
781       PRINTF(DBG_PIPE,("* Sending nothing\n"));
782       PRINTF(DBG_RAW,("* Send/%d\n",c->command));
783    }
784 }
785 
process(void)786 static void process( void )
787 {
788    struct cmd *c;
789    int        expected;
790    const char *message = NULL;
791    int        i;
792    int        *listed;
793    FILE       *old;
794 
795    while ((c = lst_top( cmd_list ))) {
796       request();		/* Send requests */
797       lst_pop( cmd_list );
798       expected = CODE_OK;
799       switch (c->command) {
800       case CMD_PRINT:
801 	 if (!formatted){
802 	    if (c->comment) fprintf( dict_output, "%s", c->comment );
803 	 }
804 
805 	 if (cmd_reply.match)
806 	    client_print_matches( cmd_reply.data, 1, cmd_reply.word );
807 	 else if (cmd_reply.listed)
808 	    client_print_listed( cmd_reply.data );
809 	 else
810 	    client_print_text( cmd_reply.data, 1 );
811 
812 	 if (flush){
813 	    fflush (dict_output);
814 	 }
815 
816 	 client_free_text( cmd_reply.data );
817 	 cmd_reply.data = NULL;
818 	 cmd_reply.matches = cmd_reply.match = cmd_reply.listed = 0;
819 	 expected = cmd_reply.retcode;
820 	 break;
821       case CMD_DEFPRINT:
822 	 if (cmd_reply.count) {
823 	    if (c->flag) {
824 	       fprintf( dict_output, "%d definition%s found",
825 			cmd_reply.count,
826 			cmd_reply.count == 1 ? "" : "s" );
827 	       fprintf( dict_output, "\n" );
828 	    }
829 	    for (i = 0; i < cmd_reply.count; i++) {
830 	       client_print_definitions (&cmd_reply.defs [i]);
831 
832 	       if (flush){
833 		  fflush (dict_output);
834 	       }
835 
836 	       client_free_text( cmd_reply.defs[i].data );
837 	       cmd_reply.defs[i].data = NULL;
838 	    }
839 	    xfree( cmd_reply.defs );
840 	    cmd_reply.count = 0;
841 
842 	 } else if (cmd_reply.matches) {
843 	    fprintf( dict_error,
844 		     "No definitions found for \"%s\", perhaps you mean:",
845 		     c->word );
846 	    fprintf( dict_error, "\n" );
847 
848 	    old = dict_output;
849 	    dict_output = dict_error;
850 	    client_print_matches( cmd_reply.data, 0, c->word );
851 	    dict_output = old;
852 
853 	    if (flush){
854 	       fflush (dict_output);
855 	    }
856 
857 	    client_free_text( cmd_reply.data );
858 	    cmd_reply.data = NULL;
859 	    cmd_reply.matches = 0;
860 
861 	    set_ex_status (EXST_APPROX_MATCHES);
862 	 } else {
863 	    fprintf( dict_error,
864 		     "No definitions found for \"%s\"\n", c->word );
865 
866 	    set_ex_status (EXST_NO_MATCH);
867 	 }
868 
869 	 expected = cmd_reply.retcode;
870 	 break;
871       case CMD_CONNECT:
872 	 if (!cmd_reply.s) break; /* Connection failed, continue; */
873 	 cmd_reply.retcode = client_read_status( cmd_reply.s,
874 						 &message,
875 						 NULL, NULL, NULL, NULL,
876 						 &cmd_reply.msgid );
877 	 if (cmd_reply.retcode == CODE_ACCESS_DENIED) {
878             client_close_pager();
879 	    err_fatal( NULL,
880 		       "Access to server %s.%s denied when connecting\n",
881 		    cmd_reply.host,
882 		    cmd_reply.service );
883 	 }
884 
885 	 /* */
886 	 host_connected    = c -> host;
887 	 if (c -> service)
888 	    service_connected = c -> service;
889 	 else
890 	    service_connected = DICT_DEFAULT_SERVICE;
891 
892 	 /* */
893 	 expected = CODE_HELLO;
894 	 while (((struct cmd *)lst_top(cmd_list))->command == CMD_CONNECT)
895 	    lst_pop(cmd_list);
896 
897 	 break;
898       case CMD_OPTION_MIME:
899 	 cmd_reply.retcode = client_read_status( cmd_reply.s,
900 						 &message,
901 						 NULL, NULL, NULL, NULL, NULL);
902 	 if (cmd_reply.retcode != expected && dbg_test(DBG_VERBOSE))
903 	    fprintf( dict_error, "Client command gave unexpected status code %d (%s)\n",
904 		    cmd_reply.retcode, message ? message : "no message" );
905 
906 	 expected = cmd_reply.retcode;
907 	 break;
908       case CMD_CLIENT:
909 	 cmd_reply.retcode = client_read_status( cmd_reply.s,
910 						 &message,
911 						 NULL, NULL, NULL, NULL, NULL);
912 	 if (cmd_reply.retcode != expected && dbg_test(DBG_VERBOSE))
913 	    fprintf( dict_error, "Client command gave unexpected status code %d (%s)\n",
914 		    cmd_reply.retcode, message ? message : "no message" );
915 
916 /*	 set_ex_status (cmd_reply.retcode); */
917 
918 	 expected = cmd_reply.retcode;
919 	 break;
920       case CMD_AUTH:
921 	 if (!cmd_reply.key || !cmd_reply.user || !cmd_reply.msgid) {
922 	    expected = cmd_reply.retcode;
923 	    break;
924 	 }
925 	 cmd_reply.retcode = client_read_status( cmd_reply.s,
926 						 &message,
927 						 NULL, NULL, NULL, NULL, NULL);
928 	 expected = CODE_AUTH_OK;
929 	 if (cmd_reply.retcode == CODE_AUTH_DENIED) {
930 	    err_warning( NULL,
931 			 "Authentication to %s.%s denied\n",
932 			 cmd_reply.host,
933 			 cmd_reply.service );
934 	    expected = CODE_AUTH_DENIED;
935 	 }
936 	 break;
937       case CMD_INFO:
938 	 expected = CODE_DATABASE_INFO;
939 	 listed = NULL;
940 	 goto gettext;
941       case CMD_SERVER:
942 	 expected = CODE_SERVER_INFO;
943 	 listed = NULL;
944 	 goto gettext;
945       case CMD_HELP:
946 	 expected = CODE_HELP;
947 	 listed = NULL;
948 	 goto gettext;
949       case CMD_DBS:
950 	 expected = CODE_DATABASE_LIST;
951 	 listed = &cmd_reply.listed;
952 	 goto gettext;
953       case CMD_STRATS:
954 	 expected = CODE_STRATEGY_LIST;
955 	 listed = &cmd_reply.listed;
956 	 goto gettext;
957    gettext:
958 	 cmd_reply.retcode = client_read_status( cmd_reply.s,
959 						 &message,
960 						 listed,
961 						 NULL, NULL, NULL, NULL);
962 	 if (cmd_reply.retcode == expected) {
963 	    cmd_reply.data = client_read_text( cmd_reply.s );
964 	    cmd_reply.retcode = client_read_status( cmd_reply.s,
965 						    &message,
966 						    NULL,NULL,NULL,NULL,NULL);
967 	    expected = CODE_OK;
968 	 }
969 	 break;
970       case CMD_DEFINE:
971 	 cmd_reply.retcode = client_read_status( cmd_reply.s,
972 						 &message,
973 						 &cmd_reply.count,
974 						 NULL, NULL, NULL, NULL );
975 	 if (!client_defines) tim_start( "define" );
976 	 switch (expected = cmd_reply.retcode) {
977 	 case CODE_DEFINITIONS_FOUND:
978 	    cmd_reply.defs = xmalloc(cmd_reply.count*sizeof(struct def));
979 	    expected = CODE_DEFINITION_FOLLOWS;
980 	    for (i = 0; i < cmd_reply.count; i++) {
981 	       ++client_defines;
982 	       cmd_reply.retcode
983 		  = client_read_status( cmd_reply.s,
984 					&message,
985 					NULL,
986 					&cmd_reply.defs[i].word,
987 					&cmd_reply.defs[i].db,
988 					&cmd_reply.defs[i].dbname,
989 					NULL );
990 	       if (cmd_reply.retcode != expected) goto error;
991 	       cmd_reply.defs[i].data = client_read_text( cmd_reply.s );
992 	    }
993 	    expected = CODE_OK;
994 	    cmd_reply.retcode = client_read_status( cmd_reply.s,
995 						    &message,
996 						    NULL,NULL,NULL,NULL,NULL );
997 	    break;
998 	 case CODE_NO_MATCH:
999 	    PRINTF(DBG_VERBOSE,
1000 		   ("No match found for \"%s\" in %s\n",c->word,c->database));
1001 	    break;
1002 	 case CODE_INVALID_DB:
1003 	    fprintf(stderr, "%s is not a valid database, use -D for a list\n",
1004 		    c->database );
1005 	    set_ex_status (EXST_INVALID_DB);
1006 	    break;
1007 	 case CODE_NO_DATABASES:
1008 	    fprintf( dict_error, "There are no databases currently available\n" );
1009 
1010 	    set_ex_status (EXST_NO_DATABASES);
1011 
1012 	    break;
1013 	 default:
1014 	    expected = CODE_OK;
1015 	 }
1016    error:
1017 	 break;
1018       case CMD_MATCH:
1019 	 cmd_reply.match = 1;
1020 	 cmd_reply.retcode = client_read_status( cmd_reply.s,
1021 						 &message,
1022 						 &cmd_reply.matches,
1023 						 NULL, NULL, NULL, NULL );
1024 	 switch (expected = cmd_reply.retcode) {
1025 	 case CODE_MATCHES_FOUND:
1026 	    cmd_reply.data = client_read_text( cmd_reply.s );
1027 	    expected = CODE_OK;
1028 	    cmd_reply.retcode = client_read_status( cmd_reply.s,
1029 						    &message,
1030 						    NULL,NULL,NULL,NULL,NULL );
1031 	    break;
1032 	 case CODE_TEMPORARILY_UNAVAILABLE:
1033 	    fprintf (stderr,
1034 		    "Server temporarily unavailable\n");
1035 
1036 	    set_ex_status (EXST_TEMPORARILY_UNAVAILABLE);
1037 
1038 	    break;
1039 	 case CODE_NO_MATCH:
1040 	    PRINTF(DBG_VERBOSE,
1041 		   ("No match found in %s for \"%s\" using %s\n",
1042 		    c->database,c->word,c->strategy));
1043 
1044 	    set_ex_status (EXST_NO_MATCH);
1045 
1046 	    break;
1047 	 case CODE_INVALID_DB:
1048 	    fprintf( dict_error,
1049 		     "%s is not a valid database, use -D for a list\n",
1050 		     c->database );
1051 
1052 	    set_ex_status (EXST_INVALID_DB);
1053 
1054 	    break;
1055 	 case CODE_INVALID_STRATEGY:
1056 	    fprintf( dict_error,
1057 		     "%s is not a valid search strategy, use -S for a list\n",
1058 		     c->strategy );
1059 
1060 	    set_ex_status (EXST_INVALID_STRATEGY);
1061 
1062 	    break;
1063 	 case CODE_NO_DATABASES:
1064 	    fprintf( dict_error,
1065 		     "There are no databases currently available\n" );
1066 
1067 	    set_ex_status (EXST_NO_DATABASES);
1068 
1069 	    break;
1070 	 case CODE_NO_STRATEGIES:
1071 	    fprintf( dict_error,
1072 		     "There are no search strategies currently available\n" );
1073 
1074 	    set_ex_status (EXST_NO_STRATEGIES);
1075 
1076 	    break;
1077 	 default:
1078 	    expected = CODE_OK;
1079 	 }
1080 	 break;
1081       case CMD_SPELL:
1082 	 if (cmd_reply.retcode == CODE_NO_MATCH) {
1083 	    prepend_command( make_command( CMD_MATCH,
1084 					   c->database, DEF_STRAT, c->word ) );
1085 	 }
1086 	 expected = cmd_reply.retcode;
1087 	 break;
1088       case CMD_WIND:
1089 	  if (cmd_reply.matches) {
1090 	    if (!cmd_reply.data)
1091 	       err_internal( __func__,
1092 			     "%d matches, but no list\n", cmd_reply.matches );
1093 
1094 	    for (i = cmd_reply.matches; i > 0; --i) {
1095 	       /* skip MIME header */
1096 	       const char *line = lst_nth_get( cmd_reply.data, i );
1097 	       arg_List   a;
1098 	       const char *orig, *s;
1099 	       char       *escaped, *d;
1100 
1101 	       if (option_mime){
1102 		  if (line [0] == 0 ||
1103 		      (line [0] == '\r' && line [1] == '\0'))
1104 		  {
1105 		     break;
1106 		  }
1107 	       }
1108 
1109 	       /* */
1110 	       a = arg_argify( line, 0 );
1111 	       if (arg_count(a) != 2)
1112 		  err_internal( __func__,
1113 				"MATCH command didn't return 2 args: \"%s\"\n",
1114 				line );
1115 
1116 	       prepend_command( make_command( CMD_DEFPRINT,
1117 					      str_find(arg_get(a,0)),
1118 					      str_copy(arg_get(a,1)),
1119 					      0 ) );
1120 
1121 				/* Escape " and \ in word before sending */
1122 	       orig    = arg_get(a,1);
1123 	       escaped = xmalloc(strlen(orig) * 2 + 1);
1124 	       for (s = orig, d = escaped; *s;) {
1125 		   switch (*s) {
1126 		   case '"':
1127 		   case '\\':
1128 		       *d++ = '\\';
1129 		   default:
1130 		       *d++ = *s++;
1131 		   }
1132 	       }
1133 	       *d++ = '\0';
1134 	       prepend_command( make_command( CMD_DEFINE,
1135 					      str_find(arg_get(a,0)),
1136 					      str_copy(escaped),
1137 					      0 ) );
1138 	       xfree(escaped);
1139 	       arg_destroy(a);
1140 	    }
1141 	    client_free_text( cmd_reply.data );
1142 	    cmd_reply.matches = 0;
1143 	 } else {
1144 	    fprintf( dict_error, "No matches found for \"%s\"", c->word );
1145 	    fprintf( dict_error, "\n" );
1146 
1147 	    set_ex_status (EXST_NO_MATCH);
1148 	 }
1149 	 expected = cmd_reply.retcode;
1150 	 break;
1151       case CMD_CLOSE:
1152 	 cmd_reply.retcode = client_read_status( cmd_reply.s,
1153 						 &message,
1154 						 NULL, NULL, NULL, NULL, NULL);
1155 	 expected = CODE_GOODBYE;
1156 	 break;
1157       default:
1158 	 err_internal( __func__, "Illegal command %d\n", c->command );
1159       }
1160       if (cmd_reply.s && cmd_reply.retcode != expected) {
1161          client_close_pager();
1162 	 unexpected_status_code (expected, cmd_reply.retcode, message);
1163       }
1164       PRINTF(DBG_RAW,("* Processed %d\n",c->command));
1165       xfree(c);
1166    }
1167 }
1168 
1169 #if 0
1170 static void handler( int sig )
1171 {
1172    const char *name = NULL;
1173 
1174    switch (sig) {
1175    case SIGHUP:  name = "SIGHUP";  break;
1176    case SIGINT:  name = "SIGINT";  break;
1177    case SIGQUIT: name = "SIGQUIT"; break;
1178    case SIGILL:  name = "SIGILL";  break;
1179    case SIGTRAP: name = "SIGTRAP"; break;
1180    case SIGTERM: name = "SIGTERM"; break;
1181    case SIGPIPE: name = "SIGPIPE"; break;
1182    }
1183 
1184    if (name)
1185       err_fatal( __func__, "Caught %s, exiting\n", name );
1186    else
1187       err_fatal( __func__, "Caught signal %d, exiting\n", sig );
1188 
1189    exit(0);
1190 }
1191 
1192 static void setsig( int sig, void (*f)(int) )
1193 {
1194    struct sigaction   sa;
1195 
1196    sa.sa_handler = f;
1197    sigemptyset(&sa.sa_mask);
1198    sa.sa_flags = 0;
1199    sigaction(sig, &sa, NULL);
1200 }
1201 #endif
1202 
client_config_print(FILE * stream,lst_List c)1203 static void client_config_print( FILE *stream, lst_List c )
1204 {
1205    FILE         *s = stream ? stream : stderr;
1206    lst_Position p;
1207    dictServer   *e;
1208 
1209    printf( "Configuration file:\n" );
1210    LST_ITERATE(dict_Servers,p,e) {
1211       if (e->port || e->user || e->secret) {
1212 	 fprintf( s, "   server %s {\n", e->host );
1213 	 if (e->port) fprintf( s, "      port %s\n", e->port );
1214 	 if (e->user) fprintf( s, "      user %s %s\n",
1215 			       e->user,
1216 			       e->secret ? "*" : "(none)" );
1217 	 fprintf( s, "   }\n" );
1218       } else {
1219 	 fprintf( s, "   server %s\n", e->host );
1220       }
1221    }
1222 }
1223 
id_string(void)1224 static const char *id_string (void)
1225 {
1226    static char buffer[BUFFERSIZE];
1227 
1228    snprintf( buffer, BUFFERSIZE, "%s", DICT_VERSION );
1229 
1230    return buffer;
1231 }
1232 
client_get_banner(void)1233 static const char *client_get_banner( void )
1234 {
1235    static char       *buffer= NULL;
1236    struct utsname    uts;
1237 
1238    if (buffer) return buffer;
1239    uname( &uts );
1240    buffer = xmalloc(256);
1241    snprintf( buffer, 256,
1242 	     "%s %s/rf on %s %s", err_program_name (), id_string (),
1243 	     uts.sysname, uts.release );
1244    return buffer;
1245 }
1246 
banner(FILE * out_stream)1247 static void banner( FILE *out_stream )
1248 {
1249    fprintf( out_stream , "%s\n", client_get_banner() );
1250    fprintf( out_stream,
1251 	    "Copyright 1997-2002 Rickard E. Faith (faith@dict.org)\n" );
1252    fprintf( out_stream,
1253 	    "Copyright 2002-2007 Aleksey Cheusov (vle@gmx.net)\n" );
1254    fprintf( out_stream, "\n" );
1255 }
1256 
license(void)1257 static void license( void )
1258 {
1259    static const char *license_msg[] = {
1260      "This program is free software; you can redistribute it and/or modify it",
1261      "under the terms of the GNU General Public License as published by the",
1262      "Free Software Foundation; either version 1, or (at your option) any",
1263      "later version.",
1264      "",
1265      "This program is distributed in the hope that it will be useful, but",
1266      "WITHOUT ANY WARRANTY; without even the implied warranty of",
1267      "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU",
1268      "General Public License for more details.",
1269      "",
1270      "You should have received a copy of the GNU General Public License along",
1271      "with this program; if not, write to the Free Software Foundation, Inc.,",
1272      "675 Mass Ave, Cambridge, MA 02139, USA.",
1273    0 };
1274    const char        **p = license_msg;
1275 
1276    banner ( stdout );
1277    while (*p) fprintf( stdout, "   %s\n", *p++ );
1278 }
1279 
help(FILE * out_stream)1280 static void help( FILE *out_stream )
1281 {
1282    static const char *help_msg[] = {
1283    "Usage: dict [options] [word]",
1284    "Query a dictd server for the definition of a word",
1285    "",
1286       "-h --host <server>        specify server",
1287       "-p --port <service>       specify port",
1288       "-d --database <dbname>    select a database to search",
1289       "-m --match                match instead of define",
1290       "-s --strategy <strategy>  strategy for matching or defining",
1291       "-c --config <file>        specify configuration file",
1292       "-C --nocorrect            disable attempted spelling correction",
1293       "-D --dbs                  show available databases",
1294       "-S --strats               show available search strategies",
1295       "-H --serverhelp           show server help",
1296       "-i --info <dbname>        show information about a database",
1297       "-I --serverinfo           show information about the server",
1298       "-a --noauth               disable authentication",
1299       "-u --user <username>      username for authentication",
1300       "-k --key <key>            shared secret for authentication",
1301       "-V --version              display version information",
1302       "-L --license              display copyright and license information",
1303       "-4                        forces dict to use IPv4 addresses only.",
1304       "-6                        forces dict to use IPv6 addresses only.",
1305       "   --help                 display this help",
1306       "-v --verbose              be verbose",
1307       "-r --raw                  trace raw transaction",
1308       "   --debug <flag>         set debugging flag",
1309       "   --pipesize <size>      specify buffer size for pipelining (256)",
1310       "   --client <text>        additional text for client command",
1311       "-M --mime                 send OPTION MIME command if server supports it",
1312       "-f --formatted            use strict tabbed format of output",
1313       0 };
1314    const char        **p = help_msg;
1315 
1316    banner( out_stream );
1317    while (*p) fprintf( out_stream, "%s\n", *p++ );
1318 }
1319 
main(int argc,char ** argv)1320 int main( int argc, char **argv )
1321 {
1322    int                c;
1323    const char         *host       = NULL;
1324    const char         *service    = NULL;
1325    const char         *user       = NULL;
1326    const char         *key        = NULL;
1327    const char         *database   = DEF_DB;
1328    const char         *strategy   = DEF_STRAT;
1329    const char         *configFile = NULL;
1330    const char         *word       = NULL;
1331    int                doauth      = 1;
1332    int                docorrect   = 1;
1333    int                offset      = 0;
1334    int                i;
1335    enum { DEFINE = 0x0001,
1336 	  MATCH  = 0x0002,
1337 	  INFO   = 0x0010,
1338 	  SERVER = 0x0020,
1339 	  DBS    = 0x0040,
1340 	  STRATS = 0x0080,
1341 	  HELP        = 0x0100,
1342 	  OPTION_MIME = 0x0200,
1343    }      function    = DEFINE;
1344    struct option      longopts[]  = {
1345       { "host",       1, 0, 'h' },
1346       { "port",       1, 0, 'p' },
1347       { "database",   1, 0, 'd' },
1348       { "info",       1, 0, 'i' },
1349       { "serverinfo", 0, 0, 'I' },
1350       { "match",      0, 0, 'm' },
1351       { "strategy",   1, 0, 's' },
1352       { "nocorrect",  0, 0, 'C' },
1353       { "config",     1, 0, 'c' },
1354       { "dbs",        0, 0, 'D' },
1355       { "strats",     0, 0, 'S' },
1356       { "serverhelp", 0, 0, 'H' },
1357       { "noauth",     0, 0, 'a' },
1358       { "user",       1, 0, 'u' },
1359       { "key",        1, 0, 'k' },
1360       { "version",    0, 0, 'V' },
1361       { "license",    0, 0, 'L' },
1362       { "help",       0, 0, 501 },
1363       { "verbose",    0, 0, 'v' },
1364       { "raw",        0, 0, 'r' },
1365       { "pager",      1, 0, 'P' },
1366       { "debug",      1, 0, 502 },
1367       { "pipesize",   1, 0, 504 },
1368       { "client",     1, 0, 505 },
1369       { "mime",       1, 0, 'M' },
1370       { "formatted",  0, 0, 'f' },
1371       { "flush",      0, 0, 'F' },
1372       { 0,            0, 0,  0  }
1373    };
1374 
1375    dict_output = stdout;
1376    dict_error  = stderr;
1377 
1378    maa_init (argv[0]);
1379 
1380    dbg_register( DBG_VERBOSE, "verbose" );
1381    dbg_register( DBG_RAW,     "raw" );
1382    dbg_register( DBG_SCAN,    "scan" );
1383    dbg_register( DBG_PARSE,   "parse" );
1384    dbg_register( DBG_PIPE,    "pipe" );
1385    dbg_register( DBG_SERIAL,  "serial" );
1386    dbg_register( DBG_TIME,    "time" );
1387    dbg_register( DBG_URL,     "url" );
1388 
1389    while ((c = getopt_long( argc, argv,
1390 			    "46h:p:d:i:Ims:DSHau:c:Ck:VLvrP:MfF",
1391 			    longopts, NULL )) != EOF)
1392    {
1393       switch (c) {
1394       case 'h': host = optarg;                         break;
1395       case '4': address_family = AF_INET;              break;
1396       case '6': address_family = AF_INET6;             break;
1397       case 'p': service = optarg;                      break;
1398       case 'd': database = optarg;                     break;
1399       case 'i': database = optarg; function |= INFO;   break;
1400       case 'I':                    function |= SERVER; break;
1401       case 'm': function &= ~DEFINE; function |= MATCH; break;
1402       case 's': strategy = optarg;                     break;
1403       case 'D':                    function |= DBS;    break;
1404       case 'S':                    function |= STRATS; break;
1405       case 'H':                    function |= HELP;   break;
1406       case 'M': option_mime = 1; function |= OPTION_MIME;   break;
1407       case 'c': configFile = optarg;                   break;
1408       case 'C': docorrect = 0;                         break;
1409       case 'a': doauth = 0;                            break;
1410       case 'u': user = optarg;                         break;
1411       case 'k': key = optarg;                          break;
1412       case 'V': banner( stdout ); exit(1);             break;
1413       case 'L': license(); exit(1);                    break;
1414       case 'v': dbg_set( "verbose" );                  break;
1415       case 'r': dbg_set( "raw" );                      break;
1416       case 'P':
1417 	 if (strcmp (optarg, "-")){
1418 	    fprintf (stderr, "Option --pager is now deprecated");
1419 	    exit (1);
1420 	 }
1421 	 break;
1422       case 505: client_text = optarg;                  break;
1423       case 504: client_pipesize = atoi(optarg);        break;
1424       case 502: dbg_set( optarg );                     break;
1425       case 501:	help( stdout );	exit(1);               break;
1426       case 'f': formatted = 1;                         break;
1427       case 'F': flush = 1;                         break;
1428       default:  help( stderr ); exit(1);               break;
1429       }
1430    }
1431 
1432    if (optind == argc && (!(function & ~(DEFINE|MATCH)))) {
1433       banner( stderr );
1434       fprintf( stderr, "Use --help for help\n" );
1435       exit(1);
1436    }
1437 
1438    if (client_pipesize > 1000000) client_pipesize = 1000000;
1439 
1440    if (dbg_test(DBG_PARSE))     prs_set_debug(1);
1441    if (dbg_test(DBG_SCAN))      yy_flex_debug = 1;
1442    else                         yy_flex_debug = 0;
1443 
1444    if (configFile) {
1445       prs_file_nocpp( configFile );
1446    } else {
1447       char b[256];
1448       char *env = getenv("HOME");
1449 
1450       snprintf( b, 256, "%s/%s", env ? env : "./", DICT_RC_NAME );
1451       PRINTF(DBG_VERBOSE,("Trying %s...\n",b));
1452       if (!access(b, R_OK)) {
1453 	 prs_file_nocpp(b);
1454       } else {
1455 	 PRINTF(DBG_VERBOSE,("Trying %s...\n",
1456 			     DICT_CONFIG_PATH DICT_CONFIG_NAME));
1457 	 if (!access( DICT_CONFIG_PATH DICT_CONFIG_NAME, R_OK ))
1458 	    prs_file_nocpp( DICT_CONFIG_PATH DICT_CONFIG_NAME );
1459       }
1460    }
1461    if (dbg_test(DBG_VERBOSE)) {
1462       if (dict_Servers) client_config_print( NULL, dict_Servers );
1463       else              fprintf( stderr, "No configuration\n" );
1464    }
1465 
1466    if (optind < argc && !strncmp(argv[optind],"dict://",7)) {
1467       char *p;
1468       int  state, fin;
1469       char *s;
1470 
1471 /*  dict://<user>:<passphrase>@<host>:<port>/d:<word>:<database>:<n>
1472            000000 111111111111 222222 333333 4 555555 6666666666 777
1473 
1474     dict://<user>:<passphrase>@<host>/d:<word>:<database>:<n>
1475            000000 111111111111 222222 4 555555 6666666666 777
1476 
1477     dict://<host>:<port>/d:<word>:<database>:<n>
1478            000000 111111 4 555555 6666666666 777
1479 
1480     dict://<host>/d:<word>:<database>:<n>
1481            000000 4 555555 6666666666 777
1482 
1483     dict://<host>/d:<word>:<database>:<n>
1484            000000 4 555555 6666666666 777
1485 
1486     dict://<user>:<passphrase>@<host>:<port>/m:<word>:<database>:<strat>:<n>
1487            000000 111111111111 222222 333333 4 555555 6666666666 7777777 888
1488 
1489     dict://<host>:<port>/m:<word>:<database>:<strat>:<n>
1490            000000 111111 4 555555 6666666666 7777777 888
1491 
1492     dict://<host>/m:<word>:<database>:<strat>:<n>
1493            000000 4 555555 6666666666 7777777 888
1494 
1495     Instead of <host> one may use [IPv6address]	where [ and ] are symbols.
1496     States for '[' and IPv6 address characters is '[', state for ']' -- ']'.
1497 */
1498 
1499       for (s = p = argv[optind] + 7, state = 0, fin = 0; !fin; ++p) {
1500 	 switch (*p) {
1501 	 case '\0': ++fin;
1502 	 case '[': state = '['; s=p+1; break;
1503 	 case ']': *p = '\0'; host = cpy(s); state = ']'; s=p+1; break;
1504 	 case ':':
1505 	    switch (state) {
1506 	    case '[': continue;
1507 	    case ']': state = 1; s=p+1; break;
1508 	    case 0: *p = '\0'; host = user = cpy(s);     ++state; s=p+1; break;
1509 	    case 2: *p = '\0'; host = cpy(s);            ++state; s=p+1; break;
1510 	    case 4:
1511 	       if (s == p - 1) {
1512 		  if (*s == 'd') function = DEFINE;
1513 		  else if (*s == 'm') function = MATCH;
1514 		  else {
1515 		     PRINTF(DBG_URL,("State = %d, s = %s\n",state,s));
1516                      client_close_pager();
1517 		     err_fatal( NULL, "Parse error at %s\n", p );
1518 		  }
1519 		                                         ++state; s=p+1; break;
1520 	       } else {
1521 		  PRINTF(DBG_URL,("State = %d, s = %s\n",state,s));
1522                   client_close_pager();
1523 		  err_fatal( NULL, "Parse error at %s\n", p );
1524 	       }
1525 	       break;
1526 	    case 5: *p = '\0'; word = cpy(s);            ++state; s=p+1; break;
1527 	    case 6: *p = '\0'; database = cpy(s);        ++state; s=p+1; break;
1528 	    case 7: *p = '\0';
1529 	       if (function == DEFINE) offset = atoi(s);
1530 	       else                    strategy = cpy(s);
1531 	                                                 ++state; s=p+1; break;
1532 	    case 8: *p = '\0';
1533 	       if (function == MATCH) offset = atoi(s); ++state; s=p+1; break;
1534 				/* FALLTHROUGH */
1535 	    default:
1536 	       PRINTF(DBG_URL,("State = %d, s = %s\n",state,s));
1537                client_close_pager();
1538 	       err_fatal( NULL, "Parse error at %s\n", p );
1539 	    }
1540 	    break;
1541 	 case '@':
1542 	    switch (state) {
1543 	    case 1: *p = '\0'; key = xstrdup(s);         ++state; s=p+1; break;
1544 	    default:
1545 	       PRINTF(DBG_URL,("State = %d, s = %s\n",state,s));
1546                client_close_pager();
1547 	       err_fatal( NULL, "Parse error at %s\n", p );
1548 	    }
1549 	    break;
1550 	 case '/':
1551 	    switch (state) {
1552 	    case 0: *p = '\0'; host = xstrdup(s);      state = 4; s=p+1; break;
1553 	    case 3:
1554 	    case 1: *p = '\0'; service = xstrdup(s);   state = 4; s=p+1; break;
1555 	    case 2: *p = '\0'; host = xstrdup(s);      state = 4; s=p+1; break;
1556 	    case ']': state = 4; s=p+1; break;
1557 	    default:
1558 	       PRINTF(DBG_URL,("State = %d, s = %s\n",state,s));
1559                client_close_pager();
1560 	       err_fatal( NULL, "Parse error at %s\n", p );
1561 	    }
1562 	    break;
1563 	 }
1564       }
1565 
1566       if (!key)      user = NULL;
1567       if (!database) database = DEF_DB;
1568       if (!strategy) strategy = DEF_STRAT;
1569 
1570       if (dbg_test(DBG_URL)) {
1571 	 printf( "user = %s, passphrase = %s\n",
1572 		 user ? user : "(null)", key ? key : "(null)" );
1573 	 printf( "host = %s, port = %s\n",
1574 		 host ? host : "(null)", service ? service : "(null)" );
1575 	 printf( "word = %s, database = %s, strategy = %s\n",
1576 		 word ? word : "(null)",
1577 		 strategy ? strategy : "(null)",
1578 		 database ? database : "(null)" );
1579       }
1580       if (host && !host[0]) {   /* Empty host causes default to be used. */
1581           xfree((void *)host);
1582           host = NULL;
1583       }
1584    }
1585 
1586 #if 0
1587    setsig(SIGHUP,  handler);
1588    setsig(SIGINT,  handler);
1589    setsig(SIGQUIT, handler);
1590    setsig(SIGILL,  handler);
1591    setsig(SIGTRAP, handler);
1592    setsig(SIGTERM, handler);
1593    setsig(SIGPIPE, handler);
1594 #endif
1595 
1596    fflush(stdout);
1597    fflush(stderr);
1598 
1599    tim_start("total");
1600 
1601    if (host) {
1602       append_command( make_command( CMD_CONNECT, host, service, user, key ) );
1603    } else {
1604       lst_Position p;
1605       dictServer   *s;
1606 
1607       if (dict_Servers) {
1608 	 LST_ITERATE(dict_Servers,p,s) {
1609 	    append_command( make_command( CMD_CONNECT,
1610 					  s->host,
1611 					  s->port,
1612 					  s->user,
1613 					  s->secret ) );
1614 	 }
1615       }else{
1616 	 fprintf (stderr, "'dict.conf' doesn't specify any dict server\n");
1617 	 exit (1);
1618       }
1619    }
1620    append_command( make_command( CMD_CLIENT, client_get_banner() ) );
1621    if (doauth) append_command( make_command( CMD_AUTH ) );
1622    if (function & OPTION_MIME) {
1623       append_command( make_command( CMD_OPTION_MIME ) );
1624       append_command( make_command( CMD_PRINT, NULL ) );
1625    }
1626    if (function & INFO) {
1627       append_command( make_command( CMD_INFO, database ) );
1628       append_command( make_command( CMD_PRINT, NULL ) );
1629    }
1630    if (function & SERVER) {
1631       append_command( make_command( CMD_SERVER ) );
1632       append_command( make_command( CMD_PRINT, NULL ) );
1633    }
1634    if (function & DBS) {
1635       append_command( make_command( CMD_DBS ) );
1636       append_command( make_command( CMD_PRINT, "Databases available:\n" ) );
1637    }
1638    if (function & STRATS) {
1639       append_command( make_command( CMD_STRATS ) );
1640       append_command( make_command( CMD_PRINT, "Strategies available:\n" ) );
1641    }
1642    if (function & HELP) {
1643       append_command( make_command( CMD_HELP ) );
1644       append_command( make_command( CMD_PRINT, "Server help:\n" ) );
1645    }
1646 
1647    if (function & MATCH) {
1648       if (word) {
1649 	 append_command( make_command( CMD_MATCH, database, strategy, word ) );
1650 	 append_command( make_command( CMD_PRINT, NULL ) );
1651       } else {
1652 	 for (i = optind; i < argc; i++) {
1653 	    append_command( make_command( CMD_MATCH,
1654 					  database, strategy, argv[i] ) );
1655 	    append_command( make_command( CMD_PRINT, NULL ) );
1656 	 }
1657       }
1658    } else if (function & DEFINE) {
1659       if (word) {
1660 	 append_command( make_command( CMD_DEFINE, database, word ) );
1661 	 append_command( make_command( CMD_DEFPRINT, database, word, 1 ) );
1662       } else {
1663 	 for (i = optind; i < argc; i++) {
1664 	    if (!strcmp(strategy, DEF_STRAT)) {
1665 	       append_command( make_command( CMD_DEFINE, database, argv[i] ) );
1666 	       if (docorrect)
1667 		  append_command( make_command( CMD_SPELL, database, argv[i]));
1668 	       append_command( make_command(CMD_DEFPRINT,database,argv[i],1) );
1669 	    } else {
1670 	       append_command( make_command( CMD_MATCH,
1671 					     database, strategy, argv[i] ) );
1672 	       append_command( make_command( CMD_WIND,
1673 					     database, strategy, argv[i] ) );
1674 	    }
1675 	 }
1676       }
1677    }
1678    append_command( make_command( CMD_CLOSE ) );
1679 
1680    if (!dbg_test(DBG_VERBOSE|DBG_TIME)) client_open_pager();
1681    process();
1682    client_close_pager();
1683 
1684    if (dbg_test(DBG_TIME)) {
1685       fprintf( dict_output, "\n" );
1686       tim_stop("total");
1687       if (client_defines) {
1688 	 tim_stop("define");
1689 	 fprintf( stderr,
1690 		  "* %ld definitions in %.2fr %.2fu %.2fs"
1691 		  " => %.1f d/sec\n",
1692 		  client_defines,
1693 		  tim_get_real( "define" ),
1694 		  tim_get_user( "define" ),
1695 		  tim_get_system( "define" ),
1696 		  client_defines / tim_get_real( "define" ) );
1697       }
1698       fprintf( stderr,
1699 	       "* %ld bytes total in %.2fr %.2fu %.2fs => %.0f bps\n",
1700 	       client_bytes,
1701 	       tim_get_real( "total" ),
1702 	       tim_get_user( "total" ),
1703 	       tim_get_system( "total" ),
1704 	       client_bytes / tim_get_real( "total" ) );
1705    }
1706 
1707    return ex_status;
1708 }
1709