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