1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  *	.nn/init file handling
6  */
7 
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <stdio.h>
13 #include <ctype.h>
14 #include "config.h"
15 #include "global.h"
16 #include "articles.h"
17 #include "admin.h"
18 #include "answer.h"
19 #include "db.h"
20 #include "execute.h"
21 #include "folder.h"
22 #include "group.h"
23 #include "hostname.h"
24 #include "init.h"
25 #include "keymap.h"
26 #include "kill.h"
27 #include "macro.h"
28 #include "menu.h"
29 #include "newsrc.h"
30 #include "nn.h"
31 #include "printconf.h"
32 #include "save.h"
33 #include "sequence.h"
34 #include "sort.h"
35 #include "nn_term.h"
36 #include "variable.h"
37 
38 #ifdef USE_MALLOC_H
39 #include <malloc.h>
40 #endif
41 
42 /* init.c */
43 
44 static char    *strip_str(register char *cmd);
45 static int      split_command(register char *cmd);
46 static char    *argv(int i);
47 static int      is_sequence(char *cmd);
48 static void     load_init_file(char *name, FILE ** seq_hook_ptr, int only_seq);
49 static void     print_debug_info(void);
50 static void     print_command(char *str);
51 static int      do_show(char *table, int mode_arg);
52 static int      do_map(FILE * initf);
53 static void     parse_on_to_end(FILE * f);
54 
55 
56 extern char    *help_directory, *lib_directory;
57 
58 int             in_init = 0;	/* true when parsing init file */
59 int             alt_cmd_key;	/* K_ when parse_command returns AC_KEYCMD */
60 
61 long            initial_memory_break;	/* for :debug statistics */
62 
63 int             first_time_user = 0;
64 
65 static int      init_err = 0;	/* errors in init file */
66 
67 extern char    *term_name;
68 extern FILE    *loc_seq_hook, *glob_seq_hook;
69 extern int      list_offset;
70 extern int      in_menu_mode;
71 extern char    *dflt_enter_macro;
72 /* extern char    *dflt_exit_macro; */
73 extern int      terminal_speed, slow_speed;
74 extern char    *pname;
75 extern char    *start_up_macro;
76 extern char    *newsrc_file;
77 extern int      in_menu_mode;
78 extern int      do_kill_handling;
79 extern char     printer[];
80 extern char    *mail_box;
81 
82 
83 
84 void
init_message(char * fmt,...)85 init_message(char *fmt,...)
86 {
87     va_list         ap;
88 
89     va_start(ap, fmt);
90 
91     if (in_init) {
92 	visual_off();
93 	printf("init error: ");
94 	vprintf(fmt, ap);
95 	putchar(NL);
96 	init_err++;
97     } else
98 	vmsg(fmt, ap);
99 
100     va_end(ap);
101 }
102 
103 
104 #define MAXARG 10
105 
106 static char    *argvec[MAXARG + 2];
107 static int      argc;
108 
109 static char    *
strip_str(register char * cmd)110 strip_str(register char *cmd)
111 {
112     if (cmd == NULL)
113 	return cmd;
114 
115     while (*cmd && isascii(*cmd) && isspace(*cmd))
116 	cmd++;
117     if (*cmd == NUL || *cmd == NL)
118 	return NULL;
119 
120     return cmd;
121 }
122 
123 
124 static int
split_command(register char * cmd)125 split_command(register char *cmd)
126 {
127     /* split command string */
128 
129     for (argc = 0; argc < MAXARG + 2; argc++)
130 	argvec[argc] = NULL;
131 strip_more:
132     if ((cmd = strip_str(cmd)) == NULL || *cmd == '#')
133 	return 0;
134     if (*cmd == ':') {
135 	cmd++;
136 	goto strip_more;
137     }
138     argc = 0;
139     argvec[0] = cmd;
140 
141     return 1;
142 }
143 
144 static char    *
argv(int i)145 argv(int i)
146 {
147     register char  *cmd;
148 
149     if (i > MAXARG)
150 	return NULL;
151 
152     if (argc <= i) {
153 	if (argvec[argc]) {
154 	    cmd = argvec[argc];
155 	    while (argc <= i) {
156 		while (*cmd && (!isascii(*cmd) || !isspace(*cmd)))
157 		    cmd++;
158 		if (*cmd == NUL) {
159 		    argc = MAXARG;
160 		    break;
161 		}
162 		*cmd++ = NUL;
163 		if ((cmd = strip_str(cmd)) == NULL) {
164 		    argc = MAXARG;
165 		    break;
166 		}
167 		argvec[++argc] = cmd;
168 	    }
169 	} else {
170 	    argc = MAXARG;
171 	}
172     }
173     return argvec[i];
174 }
175 
176 static int
is_sequence(char * cmd)177 is_sequence(char *cmd)
178 {
179     if (!split_command(cmd))
180 	return 0;
181     if ((cmd = argv(0)) == NULL)
182 	return 0;
183     return strcmp(cmd, "sequence") == 0;
184 }
185 
186 #define START_SEQUENCE 555
187 #define CHAIN_FILE 556		/* chain file */
188 #define STOP_FILE 557		/* stop */
189 
190 static void
load_init_file(char * name,FILE ** seq_hook_ptr,int only_seq)191 load_init_file(char *name, FILE ** seq_hook_ptr, int only_seq)
192 {
193     FILE           *init;
194     char            cmdbuf[1024], *cmd, *term;
195 
196     /* use cmdbuf temporarily (to handle @ expansion) */
197     for (cmd = cmdbuf; *name; name++)
198 	if (*name == '@') {
199 	    term = term_name;
200 	    while (term && *term)
201 		*cmd++ = *term++;
202 	} else
203 	    *cmd++ = *name;
204     *cmd = NUL;
205     name = cmdbuf;
206 
207 chain_file:
208     if (strchr(name, '/') == NULL)
209 	name = relative(nn_directory, name);
210 
211     init = open_file(name, OPEN_READ);
212     if (init == NULL)
213 	return;
214 
215     while (fgets_multi(cmdbuf, 1024, init)) {
216 	if (only_seq) {
217 	    if (!is_sequence(cmdbuf))
218 		continue;
219 	    *seq_hook_ptr = init;
220 	    return;
221 	}
222 	/* we use AC_REDRAW to avoid !-commands clear the screen */
223 	switch (parse_command(cmdbuf, AC_REDRAW, init)) {
224 	    case CHAIN_FILE:
225 		fclose(init);
226 		name = argvec[argc];	/* ARGTAIL */
227 		if (name == NULL)
228 		    return;
229 		goto chain_file;
230 
231 	    case STOP_FILE:
232 		fclose(init);
233 		return;
234 
235 	    case START_SEQUENCE:
236 		if (seq_hook_ptr) {
237 		    *seq_hook_ptr = init;
238 		    return;	/* no close !! */
239 		} else {
240 		    init_message("load file contains 'sequence'");
241 		    fclose(init);
242 		    return;
243 		}
244 	}
245     }
246 
247     fclose(init);
248 }
249 
250 static char     dflt_init_files[] = ",init";
251 
252 void
visit_init_file(int only_seq,char * first_arg)253 visit_init_file(int only_seq, char *first_arg)
254 {
255     char           *next_arg;
256 
257     in_init = 1;
258     load_init_file(relative(lib_directory, "setup"), (FILE **) NULL, 0);
259     in_init = 0;
260 
261     if (first_arg && strncmp(first_arg, "-I", 2) == 0) {
262 	if (first_arg[2] == NUL)
263 	    return;
264 	first_arg += 2;
265     } else
266 	first_arg = dflt_init_files;
267 
268     in_init = 1;
269     while (first_arg) {
270 	next_arg = strchr(first_arg, ',');
271 	if (next_arg)
272 	    *next_arg++ = NUL;
273 
274 	if (*first_arg == NUL) {
275 	    if (glob_seq_hook == NULL)
276 		load_init_file(relative(lib_directory, "init"), &glob_seq_hook, only_seq);
277 	} else {
278 	    if (loc_seq_hook != NULL) {
279 		fclose(loc_seq_hook);
280 		loc_seq_hook = NULL;
281 	    }
282 	    load_init_file(first_arg, &loc_seq_hook, only_seq);
283 	}
284 	first_arg = next_arg;
285     }
286 
287     if (init_err)
288 	nn_exit(1);
289     in_init = 0;
290 }
291 
292 
293 /*
294  * parse a command (also :-commands)
295  */
296 
297 static char    *sw_string;
298 static int      sw_loop_once;
299 
300 #define	SWITCH(str)	\
301     for (sw_string = str, sw_loop_once = 1; --sw_loop_once == 0; )
302 
303 #define CASE(str)	\
304     if (strcmp(sw_string, str) == 0)
305 
306 
307 #define ARG(i, str)	(argv(i) && strcmp(argv(i), str) == 0)
308 #define ARGVAL(i)	atol(argv(i))
309 #define ARGTAIL		argvec[argc]
310 
311 static struct alt_commands {
312     char           *alt_name;
313     int             alt_len;
314     int             alt_type;
315 }               alt_commands[] = {
316 
317     "admin", 5, 0,
318     "bug", 3, 0,
319     "cd", 2, 1,
320     "compile", 7, 0,
321     "coredump", 8, 0,
322     "cost", 4, 0,
323     "decode", 6, 0,
324     "define", 6, 0,
325     "help", 4, 2,
326     "load", 4, 0,
327     "local", 5, 3,
328     "lock", 4, 3,
329     "make", 4, -1,
330     "make map ", 9, -2,
331     "man", 3, 0,
332     "map", 3, 4,
333     "mkdir", 5, 1,
334     "motd", 4, 0,
335     "patch", 5, 0,		/* QUICK HACK */
336     "post", 4, 0,		/* QUICK HACK */
337     "print", 5, -3,		/* QUICK HACK */
338     "print-variables", 15, 0,
339     "pwd", 3, 0,
340     "rmail", 5, 0,
341     "set", 3, 3,
342     "show", 4, -1,
343     "show config", 11, 0,
344     "show groups", 11, -1,
345     "show groups all", 15, 0,
346     "show groups subscr", 18, 0,
347     "show groups total", 17, 0,
348     "show groups unsub", 17, 0,
349     "show kill", 9, 0,
350     "show map", 8, -1,
351     "show map #", 10, 0,
352     "show map key", 12, 0,
353     "show map menu", 13, 0,
354     "show map show", 13, 0,
355     "show rc ", 8, 0,
356     "sort", 4, -1,
357     "sort arrival", 12, 0,
358     "sort date", 9, 0,
359     "sort lexical", 12, 0,
360     "sort sender", 11, 0,
361     "sort subject", 12, 0,
362     "toggle", 6, 3,
363     "unread", 6, 0,
364     "unset", 5, 3,
365     "unshar", 6, 0,		/* QUICK HACK */
366     NULL, 0, 0
367 };
368 
369 int
alt_completion(char * buf,int index)370 alt_completion(char *buf, int index)
371 {
372     static char    *head, *tail = NULL, buffer[FILENAME];
373     static int      len;
374     static struct alt_commands *alt, *help_alt;
375     static fct_type other_compl;
376     int             temp;
377     register char  *p, *q;
378 
379     if (other_compl) {
380 	temp = CALL(other_compl) (buf, index);
381 	if (index == 0 && temp == 1 && tail)
382 	    strcpy(tail, head);
383 	if (index < 0 || (index == 0 && temp == 0)) {
384 	    other_compl = NULL;
385 	    list_offset = 0;
386 	}
387 	return temp;
388     }
389     if (index < 0)
390 	return 0;
391 
392     if (buf) {
393 	if (index >= 1 && buf[0] == '!')
394 	    return -1;		/* :! is special */
395 
396 	head = buf;
397 	tail = buf + index;
398 	alt = help_alt = alt_commands;
399 	len = tail - head;
400 	other_compl = NULL;
401 
402 	for (; alt->alt_name; alt++) {
403 	    if (len <= alt->alt_len)
404 		continue;
405 	    if (head[alt->alt_len] != SP) {
406 		if (alt->alt_type != -2)
407 		    continue;
408 		if (strncmp(alt->alt_name, head, alt->alt_len))
409 		    continue;
410 		return -1;
411 	    }
412 	    index = strncmp(alt->alt_name, head, alt->alt_len);
413 	    if (index < 0)
414 		continue;
415 	    if (index > 0)
416 		break;
417 
418 	    if (alt->alt_type < 0) {
419 		if (len > alt->alt_len)
420 		    continue;
421 		break;
422 	    }
423 	    if (alt->alt_type == 0)
424 		return -1;	/* cannot be further compl */
425 
426 	    head += alt->alt_len;
427 	    while (*head && *head == SP)
428 		head++;
429 	    len = tail - head;
430 	    temp = -1;
431 
432 	    switch (alt->alt_type) {
433 		case 1:
434 		    other_compl = file_completion;
435 		    tail = NULL;
436 		    temp = file_completion(head, len);
437 		    break;
438 
439 		case 2:
440 		    other_compl = file_completion;
441 		    sprintf(buffer, "%s.%s",
442 			    relative(help_directory, "help"), head);
443 		    len = strlen(buffer);
444 		    head = buffer + len;
445 		    list_offset = 5;
446 		    temp = file_completion(buffer, len);
447 		    break;
448 
449 		case 3:
450 		    /* [set ]variable[ value] */
451 		    for (p = head; *p;)
452 			if (*p++ == SP)
453 			    return -1;
454 		    other_compl = var_completion;
455 		    var_compl_opts((int) (tail - buf));
456 		    tail = NULL;
457 		    temp = var_completion(head, len);
458 		    break;
459 
460 		case 4:
461 		    /* [map XXX ]Y command[ N] */
462 		    if (*head == '#')
463 			return -1;
464 		    for (p = head, temp = 0; *p;)
465 			if (*p++ == SP) {
466 			    while (*p && *p == SP)
467 				p++;
468 			    head = p;
469 			    temp++;
470 			}
471 		    if (temp == 0)
472 			other_compl = keymap_completion;
473 		    else if (temp == 2)
474 			other_compl = cmd_completion;
475 		    else
476 			return -1;
477 
478 		    tail = NULL;
479 		    len = p - head;
480 		    temp = CALL(other_compl) (head, len);
481 		    break;
482 	    }
483 	    if (temp <= 0)
484 		other_compl = NULL;
485 	    return temp;
486 	}
487 
488 	alt = alt_commands;
489 	return 1;
490     }
491     if (index) {
492 	list_completion((char *) NULL);
493 	if (help_alt->alt_name == NULL)
494 	    help_alt = alt_commands;
495 	list_offset = 0;
496 	if ((p = strrchr(head, ' ')))
497 	    list_offset = p - head;
498 
499 	while (help_alt->alt_name) {
500 	    if (len > help_alt->alt_len ||
501 		(index = strncmp(help_alt->alt_name, head, len)) < 0) {
502 		help_alt++;
503 		continue;
504 	    }
505 	    if (index > 0) {
506 		help_alt = alt_commands;
507 		break;
508 	    }
509 	    p = help_alt->alt_name;
510 	    if (list_completion(p) == 0)
511 		break;
512 	    temp = help_alt->alt_len;
513 
514 	    if (help_alt->alt_type == -3) {
515 		help_alt++;
516 		continue;
517 	    }
518 	    do
519 		help_alt++;
520 	    while (((q = help_alt->alt_name)) && help_alt->alt_len > temp &&
521 		   strncmp(p, q, temp) == 0);
522 	}
523 	fl;
524 	list_offset = 0;
525 	return 1;
526     }
527     for (; alt->alt_name; alt++) {
528 	if (len == 0)
529 	    index = 0;
530 	else
531 	    index = strncmp(alt->alt_name, head, len);
532 	if (index < 0)
533 	    continue;
534 	if (index > 0)
535 	    break;
536 
537 	p = alt->alt_name;
538 	sprintf(tail, "%s%s", p + len, alt->alt_type <= -2 ? "" : " ");
539 	temp = alt->alt_len;
540 
541 	if (alt->alt_type == -3) {
542 	    alt++;
543 	    return 1;
544 	}
545 	do
546 	    alt++;
547 	while (((q = alt->alt_name)) && alt->alt_len > temp &&
548 	       strncmp(p, q, temp) == 0);
549 
550 	return 1;
551     }
552 
553     cmd_completion(head, len);
554     if ((temp = cmd_completion((char *) NULL, 0))) {
555 	other_compl = cmd_completion;
556 	tail = NULL;
557     }
558     return temp;
559 }
560 
561 static void
print_debug_info(void)562 print_debug_info(void)
563 {
564 
565 #ifdef USE_MALLOC_H
566     struct mallinfo mallinfo(), mi;
567 #endif
568 
569     static long     prev_mem = 0;
570     long            cur_mem;
571 
572     clrdisp();
573     tprintf("group=%s, nart=%ld\n\r", current_group->group_name, n_articles);
574 
575     cur_mem = (((long) sbrk(0)) - initial_memory_break) / 1024;
576 
577     tprintf("\nMemory usage: %ldk, previous: %ldk, change: %ldk\n\r",
578 	    cur_mem, prev_mem, cur_mem - prev_mem);
579     prev_mem = cur_mem;
580 
581 #ifdef USE_MALLOC_H
582     mi = mallinfo();
583     tprintf("\nMalloc info.  Total allocation: %d\n\r", mi.arena);
584     tprintf("Ordinary blocks: %d, space in use: %d, space free: %d\n\r",
585 	    mi.ordblks, mi.uordblks, mi.fordblks);
586     tprintf("Small blocks: %d, space in use: %d, space free: %d\n\r",
587 	    mi.smblks, mi.usmblks, mi.fsmblks);
588     tprintf("Holding blocks: %d, space in headers: %d\n\r",
589 	    mi.hblks, mi.hblkhd);
590 #endif
591 
592     any_key(0);
593 }
594 
595 static void
print_command(char * str)596 print_command(char *str)
597 {
598     char          **av;
599     char            buf[1024];
600 
601     if (!in_init) {
602 	msg(str);
603 	return;
604     }
605     buf[0] = NUL;
606     for (av = argvec; *av; av++) {
607 	strcat(buf, " ");
608 	strcat(buf, *av);
609     }
610     init_message("%s: %s", str, buf);
611 }
612 
613 
614 static int
do_show(char * table,int mode_arg)615 do_show(char *table, int mode_arg)
616 {
617     register group_header *gh;
618     int             ret = 1;
619 
620     if (in_init || table == NULL)
621 	return 0;
622 
623     no_raw();
624 
625     SWITCH(table) {
626 
627 	CASE("config") {
628 	    clrdisp();
629 	    print_config(stdout);
630 	    any_key(0);
631 	    break;
632 	}
633 
634 	CASE("kill") {
635 	    clrdisp();
636 	    dump_kill_list();
637 	    break;
638 	}
639 
640 	CASE("groups") {
641 
642 	    clrdisp();
643 	    if ARG
644 		(mode_arg, "all")
645 		    group_overview(1);
646 	    else if ARG
647 		(mode_arg, "total")
648 		    group_overview(2);
649 	    else if ARG
650 		(mode_arg, "unsub")
651 		    group_overview(3);
652 	    else if ARG
653 		(mode_arg, "subscr")
654 		    group_overview(4);
655 	    else if ARG
656 		(mode_arg, "sequence")
657 		    group_overview(-1);
658 	    else
659 		group_overview(0);
660 
661 	    break;
662 	}
663 
664 	CASE("map") {
665 	    char           *name;
666 
667 	    if ((name = argv(mode_arg)) == NULL)
668 		name = in_menu_mode ? "menu" : "show";
669 
670 	    if (name[0] == '#') {
671 		clrdisp();
672 		dump_multi_keys();
673 		break;
674 	    }
675 	    SWITCH(name) {
676 
677 		CASE("key") {
678 		    clrdisp();
679 		    dump_global_map();
680 		    break;
681 		}
682 		if (dump_key_map(name) >= 0)
683 		    break;
684 
685 		init_message("unknown map '%s'", argv(mode_arg));
686 		ret = 0;
687 		/* break;	goto err */
688 	    }
689 
690 	    break;
691 	}
692 
693 	CASE("rc") {
694 	    if (argv(2)) {
695 		gh = lookup(argv(2));
696 		if (gh == NULL) {
697 		    msg("Unknown: %s", argv(2));
698 		    break;
699 		}
700 	    } else
701 		gh = current_group;
702 	    if (gh->group_flag & G_FAKED)
703 		break;
704 
705 	    clrdisp();
706 
707 	    tprintf("Available: %ld - %ld  (unread %ld)\n\n\r",
708 		(long) (gh->first_db_article), (long) (gh->last_db_article),
709 		    (long) (gh->unread_count));
710 	    tprintf(".newsrc:\n\r>%s\r<%s\n\rselect:\n\r>%s\r<%s\n\r",
711 		    gh->newsrc_line ? gh->newsrc_line : "(null)\n",
712 		    gh->newsrc_orig == gh->newsrc_line ? "(same)\n" :
713 		    gh->newsrc_orig ? gh->newsrc_orig : "(null)\n",
714 		    gh->select_line ? gh->select_line : "(null)\n",
715 		    gh->select_orig == gh->select_line ? "(same)\n" :
716 		    gh->select_orig ? gh->select_orig : "(null)\n");
717 	    any_key(0);
718 	    break;
719 	}
720 
721 	init_message("unknown table '%s'", table);
722 	ret = 0;
723 	/* break; */
724 	/* goto err; */
725 	/* NOTREACHED */
726     }
727 
728     nn_raw();
729     return ret;
730 /*
731 err:
732     nn_raw();
733     return 0;
734 */
735 }
736 
737 
738 static int
do_map(FILE * initf)739 do_map(FILE * initf)
740 {
741     int             code, map_menu, map_show, must_redraw = 0;
742     key_type        bind_to;
743     register struct key_map_def *map_def;
744     register int   *map;
745 
746     code = lookup_keymap(argv(1));
747     if (code < 0) {
748 	print_command("unknown map");
749 	goto out;
750     }
751     map_def = &keymaps[code];
752 
753     if (map_def->km_flag & K_GLOBAL_KEY_MAP) {
754 	if (argv(3) == NULL)
755 	    goto mac_err;
756 	if (argv(2) == NULL) {
757 	    dump_global_map();
758 	    return 1;
759 	}
760 	global_key_map[parse_key(argv(2))] = parse_key(argv(3));
761 	return 0;
762     }
763     if (map_def->km_flag & K_MULTI_KEY_MAP) {
764 	key_type        multi_buffer[16], *mb;
765 	int             i;
766 
767 	if (argv(1)[1] == NUL) {
768 	    dump_multi_keys();
769 	    return 1;
770 	}
771 	if (isdigit(argv(1)[1]))
772 	    bind_to = K_function(argv(1)[1] - '0');
773 	else {
774 	    bind_to = parse_key(argv(1) + 1);
775 	    if (bind_to < K_up_arrow || bind_to > K_right_arrow)
776 		goto mac_err;
777 	}
778 
779 	for (i = 2, mb = multi_buffer; argv(i); i++)
780 	    *mb++ = parse_key(argv(i));
781 	*mb = NUL;
782 
783 	enter_multi_key(bind_to, (key_type *) copy_str((char *) multi_buffer));
784 	return 0;
785     }
786     code = K_UNBOUND;
787     map = map_def->km_map;
788     map_show = map_def->km_flag & K_BOTH_MAPS;
789     map_menu = map_def->km_flag & K_BIND_ORIG;
790 
791     if (ARG(3, "(")) {
792 	code = (int) m_define("-2", initf);
793 	must_redraw = 1;
794 	if (code == K_UNBOUND)
795 	    goto mac_err;
796     }
797     if (code == K_UNBOUND && argv(3))
798 	code = lookup_command(argv(3), map_def->km_flag & (K_ONLY_MENU | K_ONLY_MORE));
799 
800     switch (code) {
801 	case K_INVALID - 1:
802 	    init_message("Cannot bind '%s' in '%s' map", argv(3), argv(1));
803 	    goto out;
804 
805 	case K_EQUAL_KEY:
806 	    if (argv(4) == NULL)
807 		goto mac_err;
808 	    code = map[parse_key(argv(4))];
809 	    break;
810 
811 	case K_MACRO:
812 	case K_ARTICLE_ID:
813 	    if (argv(4) == NULL)
814 		goto mac_err;
815 	    code |= atoi(argv(4));
816 	    break;
817 
818 	case K_PREFIX_KEY:
819 	    if (argv(4) == NULL)
820 		goto mac_err;
821 	    code = lookup_keymap(argv(4));
822 	    if (code < 0) {
823 		print_command("unknown prefix map");
824 		goto out;
825 	    }
826 	    code |= K_PREFIX_KEY;
827 	    break;
828     }
829 
830     if (code == K_INVALID) {
831 	init_message("unknown key command: %s", argv(3));
832 	goto out;
833     }
834     bind_to = parse_key(argv(2));
835     if (map_menu) {
836 	if (code & K_MACRO && orig_menu_map[bind_to] == 0)
837 	    orig_menu_map[bind_to] = map[bind_to];
838     }
839     map[bind_to] = code;
840     if (map_show)
841 	more_key_map[bind_to] = code;
842     goto out;
843 
844 mac_err:
845     print_command("map argument missing");
846 out:
847     return must_redraw;
848 }
849 
850 static void
parse_on_to_end(FILE * f)851 parse_on_to_end(FILE * f)
852 {
853     register char  *cp;
854     char            buf[1024];
855     static char    *last_cmd_res = NULL;
856     int             i;
857     int             err_flag = 0;
858 
859     if (ARGTAIL == NULL || *ARGTAIL == NUL)
860 	goto on_err;
861 
862     cp = NULL;
863     switch (*ARGTAIL) {
864 	case '#':		/* on #... end: skipped (+hack for else) */
865 	    goto skip_to_end;
866 
867 	case '`':		/* on `shell command` str1 str2 ... */
868 	    {
869 		FILE           *p;
870 		char           *cmd = ARGTAIL + 1, *t;
871 
872 		if ((cp = strrchr(cmd, '`')) == NULL)
873 		    goto syntax_err;
874 		if ((t = strip_str(cp + 1)) == NULL)
875 		    goto syntax_err;
876 		*cp = NUL;
877 		ARGTAIL = t;
878 
879 		if (cmd[0]) {
880 		    buf[0] = NUL;
881 		    if ((p = popen(cmd, "r"))) {
882 			if (fgets(buf, 1024, p))
883 			    buf[strlen(buf) - 1] = NUL;
884 			pclose(p);
885 		    }
886 		    if (last_cmd_res != NULL && strcmp(last_cmd_res, buf)) {
887 			free(last_cmd_res);
888 			last_cmd_res = NULL;
889 		    }
890 		    if (buf[0] == NUL)
891 			goto skip_to_end;
892 		    last_cmd_res = copy_str(buf);
893 		}
894 		for (i = 1; argv(i) != NULL; i++)
895 		    if (strcmp(argv(i), last_cmd_res) == 0)
896 			return;
897 	    }
898 	    goto skip_to_end;
899 
900 	case '$':		/* on $VAR [ a b c ... ] */
901 	    cp = argv(1);
902 	    if ((cp = getenv(cp + 1)) == NULL)
903 		goto skip_to_end;
904 	    if (ARGTAIL == NULL)
905 		return;
906 	    for (i = 2; argv(i) != NULL; i++)
907 		if (strcmp(argv(i), cp) == 0)
908 		    return;
909 	    goto skip_to_end;
910 
911 	case '!':		/* on !shell-command */
912 	    cp = ARGTAIL + 1;
913 	    break;
914 
915 	case '[':		/* on [ test ] */
916 	    cp = ARGTAIL + strlen(ARGTAIL) - 1;
917 	    if (*cp != ']')
918 		goto syntax_err;
919 	    cp = ARGTAIL;
920 	    break;
921 
922 	default:
923 	    break;
924     }
925 
926     if (cp) {
927 	if (run_shell(cp, -2, 1) == 0)
928 	    return;
929 	goto skip_to_end;
930     }
931     if (argv(1) == NULL)
932 	goto on_err;
933 
934     SWITCH(argv(1)) {
935 
936 	CASE("entry") {
937 	    group_header   *gh;
938 	    char           *macro;
939 	    int             ii;
940 
941 	    macro = parse_enter_macro(f, NL);
942 	    if (ARGTAIL) {
943 		for (ii = 2; argv(ii); ii++) {
944 		    start_group_search(argv(ii));
945 		    while ((gh = get_group_search()))
946 			gh->enter_macro = macro;
947 		}
948 	    } else
949 		dflt_enter_macro = macro;
950 	    return;
951 	}
952 
953 /*	CASE( "exit" ) {
954 	    dflt_exit_macro = parse_enter_macro(f, NL);
955 	    return;
956 	}
957 */
958 	CASE("slow") {
959 	    if (terminal_speed <= (slow_speed / 10))
960 		return;
961 	    break;
962 	}
963 
964 	CASE("fast") {
965 	    if (terminal_speed > (slow_speed / 10))
966 		return;
967 	    break;
968 	}
969 
970 	CASE("term") {
971 	    int             ii;
972 
973 	    for (ii = 2; argv(ii) != NULL; ii++)
974 		if (strcmp(argv(ii), term_name) == 0)
975 		    return;
976 	    break;
977 	}
978 
979 	CASE("host") {
980 	    char            local_host[100];
981 	    int             ii;
982 
983 	    nn_gethostname(local_host, 100);
984 	    for (ii = 2; argv(ii) != NULL; ii++)
985 		if (strcmp(argv(ii), local_host) == 0)
986 		    return;
987 	    break;
988 	}
989 
990 	CASE("program") {
991 	    char           *pname1;
992 	    int             ii;
993 
994 	    for (pname1 = pname; *pname1 == 'n'; pname1++);
995 
996 	    for (ii = 2; argv(ii) != NULL; ii++) {
997 		if (strcmp(argv(ii), pname) == 0)
998 		    return;
999 		if (pname1[0] && strcmp(argv(ii), pname1) == 0)
1000 		    return;
1001 	    }
1002 	    break;
1003 	}
1004 
1005 	CASE("start-up") {
1006 	    start_up_macro = parse_enter_macro(f, NL);
1007 	    return;
1008 	}
1009 
1010 	CASE("first-use") {
1011 	    if (!first_time_user)
1012 		break;
1013 	    if (argv(2) == NULL || ARG(2, "all"))
1014 		return;
1015 	    if (newsrc_file == NULL)	/* == code from visit_rc_file == */
1016 		newsrc_file = home_relative(".newsrc");
1017 	    if (ARG(2, "old") && file_exist(newsrc_file, (char *) NULL))
1018 		return;
1019 	    if (ARG(2, "new") && !file_exist(newsrc_file, (char *) NULL))
1020 		return;
1021 	    break;
1022 	}
1023 
1024 	err_flag = 1;
1025 /*	goto on_err; */
1026     }
1027     if (err_flag == 1)
1028 	goto on_err;
1029 
1030 skip_to_end:
1031     while (fgets_multi(buf, 1024, f)) {
1032 	for (cp = buf; *cp && isascii(*cp) && isspace(*cp); cp++);
1033 	if (*cp != 'e')
1034 	    continue;
1035 	if (strncmp(cp, "end", 3) == 0)
1036 	    return;
1037 	if (strncmp(cp, "else", 4) == 0)
1038 	    return;
1039     }
1040     init_message("end missing (on %s)", argv(1));
1041     return;
1042 
1043 on_err:
1044     init_message("on `what'?");
1045     return;
1046 
1047 syntax_err:
1048     init_message("syntax error: on %s", ARGTAIL);
1049 }
1050 
1051 int
parse_command(char * cmd,int ok_val,FILE * initf)1052 parse_command(char *cmd, int ok_val, FILE * initf)
1053 {
1054 
1055     if (!split_command(cmd))
1056 	return ok_val;
1057 
1058     if (*ARGTAIL == '!') {
1059 	if (ok_val == AC_UNCHANGED) {	/* in macro */
1060 	    if (ARGTAIL[1] == '!')	/* !!cmd => guarantee no output! */
1061 		run_shell(ARGTAIL + 2, -2, 1);
1062 	    else
1063 		run_shell(ARGTAIL + 1, -1, 1);
1064 	    return ok_val;
1065 	}
1066 	if (run_shell(ARGTAIL + 1, ok_val == AC_PROMPT ? 1 : 0, in_init) >= 0) {
1067 	    any_key(0);
1068 	    return AC_REDRAW;
1069 	}
1070 	return ok_val;
1071     }
1072     SWITCH(argv(0)) {
1073 
1074 	CASE("unset") {
1075 	    if (argv(1) == NULL)
1076 		goto stx_err;
1077 
1078 	    if (set_variable(argv(1), 0, (char *) NULL))
1079 		return AC_REDRAW;
1080 	    else
1081 		return ok_val;
1082 	}
1083 
1084 	CASE("local") {
1085 	    if (ARGTAIL == NULL)
1086 		goto stx_err;
1087 
1088 	    cmd = argv(1);
1089 	    if (!push_variable(cmd))
1090 		return ok_val;
1091 
1092 	    if (ARGTAIL && set_variable(cmd, 1, ARGTAIL))
1093 		return AC_REDRAW;
1094 	    else
1095 		return ok_val;
1096 	}
1097 
1098 	CASE("set") {
1099 	    if (ARGTAIL == NULL || ARGTAIL[0] == '/') {
1100 		disp_variables(0, ARGTAIL);
1101 		return AC_REDRAW;
1102 	    }
1103 	    cmd = argv(1);	/* get ARGTAIL right */
1104 	    if (cmd != NULL && strcmp(cmd, "all") == 0) {
1105 		disp_variables(1, (char *) NULL);
1106 		return AC_REDRAW;
1107 	    }
1108 	    if (set_variable(cmd, 1, ARGTAIL))
1109 		return AC_REDRAW;
1110 	    else
1111 		return ok_val;
1112 	}
1113 
1114 	CASE("toggle") {
1115 	    if (argv(1) == NULL)
1116 		goto stx_err;
1117 	    toggle_variable(argv(1));
1118 	    break;
1119 	}
1120 
1121 	CASE("lock") {
1122 	    if (argv(1) == NULL)
1123 		goto stx_err;
1124 	    lock_variable(argv(1));
1125 	    break;
1126 	}
1127 
1128 	CASE("define") {
1129 	    if (in_init) {
1130 		if (argv(1) == NULL) {
1131 		    init_message("macro number missing");
1132 		    break;
1133 		}
1134 		m_define(argv(1), initf);
1135 	    } else if (m_define(argv(1), (FILE *) NULL))
1136 		return AC_REDRAW;
1137 
1138 	    break;
1139 	}
1140 
1141 	CASE("map") {
1142 	    if (argv(2) == NULL) {
1143 		if (do_show("map", 1))
1144 		    return AC_REDRAW;
1145 		break;
1146 	    }
1147 	    if (do_map(initf))
1148 		return AC_REDRAW;
1149 	    break;
1150 	}
1151 
1152 	CASE("make") {
1153 	    if (ARG(1, "map") && argv(2)) {
1154 		switch (make_keymap(argv(2))) {
1155 		    case -1:
1156 			init_message("map %s already exists", argv(2));
1157 			break;
1158 		    case -2:
1159 			init_message("cannot make %s: too many maps", argv(2));
1160 			break;
1161 		}
1162 		break;
1163 	    }
1164 	    print_command("invalid make command");
1165 	    break;
1166 	}
1167 
1168 	CASE("cd") {
1169 	    if (change_dir(argv(1), in_init))
1170 		init_message("chdir %s FAILED", argv(1));
1171 
1172 	    break;
1173 	}
1174 
1175 	CASE("clear") {
1176 	    clrdisp();
1177 	    break;
1178 	}
1179 
1180 	if (in_init) {
1181 
1182 	    CASE("load") {
1183 		if (argv(1))
1184 		    load_init_file(argv(1), (FILE **) NULL, 0);
1185 		break;
1186 	    }
1187 
1188 	    CASE("on") {
1189 		parse_on_to_end(initf);
1190 		break;
1191 	    }
1192 
1193 	    CASE("else") {
1194 		ARGTAIL = "#";	/* skip to end */
1195 		parse_on_to_end(initf);
1196 		break;
1197 	    }
1198 
1199 	    CASE("end") {
1200 		break;
1201 	    }
1202 
1203 	    CASE("echo") {
1204 		tprintf("\r%s\n\r", ARGTAIL);
1205 		break;
1206 	    }
1207 
1208 	    CASE("error") {
1209 		nn_exitmsg(1, "%s\n", ARGTAIL);
1210 	    }
1211 
1212 	    CASE("exit") {
1213 		nn_exit(ARGTAIL != NULL ? atoi(ARGTAIL) : 0);
1214 	    }
1215 
1216 	    CASE("chain") {
1217 		return CHAIN_FILE;
1218 	    }
1219 
1220 	    CASE("stop") {
1221 		return STOP_FILE;
1222 	    }
1223 
1224 	    CASE("sequence") {
1225 		return START_SEQUENCE;
1226 	    }
1227 
1228 	    CASE("save-files") {
1229 		parse_save_files(initf);
1230 		break;
1231 	    }
1232 
1233 	    print_command("unknown command");
1234 	    break;
1235 	}
1236 
1237 	/*
1238 	 * commands only available from : command line
1239 	 */
1240 
1241 	if (ok_val != AC_REDRAW) {
1242 
1243 	    alt_cmd_key = lookup_command(sw_string,
1244 				  in_menu_mode ? K_ONLY_MENU : K_ONLY_MORE);
1245 	    if (alt_cmd_key > K_INVALID && alt_cmd_key != K_HELP) {
1246 		if (alt_cmd_key == K_MACRO) {
1247 		    if (ARGTAIL == NULL)
1248 			break;
1249 		    alt_cmd_key |= atoi(ARGTAIL);
1250 		}
1251 		return AC_KEYCMD;
1252 	    }
1253 	}
1254 	CASE("load") {
1255 	    clrdisp();
1256 	    in_init = 1;
1257 	    init_err = 0;
1258 	    load_init_file(argv(1) ? argv(1) : "init", (FILE **) NULL, 0);
1259 	    in_init = 0;
1260 	    if (init_err)
1261 		any_key(0);
1262 	    return AC_REDRAW;
1263 	}
1264 
1265 	CASE("q") {
1266 	    break;
1267 	}
1268 
1269 	CASE("Q") {
1270 	    return AC_QUIT;
1271 	}
1272 
1273 	CASE("q!") {
1274 	    if (restore_bak())
1275 		return AC_QUIT;
1276 	    break;
1277 	}
1278 
1279 	CASE("x") {
1280 	    update_rc_all(current_group, 0);
1281 	    return AC_QUIT;
1282 	}
1283 
1284 	CASE("help") {
1285 	    if (argv(1) == NULL)
1286 		display_help("help");
1287 	    else
1288 		display_help(argv(1));
1289 	    return AC_REDRAW;
1290 	}
1291 
1292 	CASE("man") {
1293 	    char           *manual;
1294 	    group_header   *orig_group;
1295 
1296 	    manual = relative(help_directory, "Manual");
1297 	    if (!file_exist(manual, "fr")) {
1298 		manual = relative(lib_directory, "Manual");
1299 		if (!file_exist(manual, "fr")) {
1300 		    msg("Online manual is not available");
1301 		    break;
1302 		}
1303 	    }
1304 	    orig_group = current_group;
1305 	    folder_menu(manual, 1);
1306 	    init_group(orig_group);
1307 
1308 	    return AC_REDRAW;
1309 	}
1310 
1311 	CASE("motd") {
1312 	    if (display_motd(0))
1313 		return AC_REDRAW;
1314 	    msg("no motd file");
1315 	    break;
1316 	}
1317 
1318 	CASE("sort") {
1319 	    if (argv(1) == NULL)
1320 		sort_articles(-1);
1321 	    else if (ARG(1, "no") || ARG(1, "arrival"))
1322 		sort_articles(0);
1323 	    else if ARG
1324 		(1, "subject")
1325 		    sort_articles(1);
1326 	    else if ARG
1327 		(1, "lexical")
1328 		    sort_articles(2);
1329 	    else if (ARG(1, "date") || ARG(1, "age"))
1330 		sort_articles(3);
1331 	    else if (ARG(1, "sender") || ARG(1, "from"))
1332 		sort_articles(4);
1333 	    else {
1334 		msg("Unknown sort mode '%s'", argv(1));
1335 		break;
1336 	    }
1337 
1338 	    return AC_REORDER;
1339 	}
1340 
1341 	CASE("unread") {
1342 	    group_header   *gh;
1343 	    int             ix;
1344 
1345 	    if (argv(1) && (gh = lookup(argv(1))) != NULL)
1346 		ix = 2;
1347 	    else {
1348 		ix = 1;
1349 		gh = current_group;
1350 	    }
1351 
1352 	    if (gh == current_group)
1353 		return AC_REENTER_GROUP;
1354 
1355 	    if (argv(ix)) {
1356 		if (!restore_rc(gh, gh->last_db_article - (article_number) ARGVAL(ix)))
1357 		    break;
1358 	    } else if (!restore_unread(gh))
1359 		break;
1360 	    break;
1361 	}
1362 
1363 	CASE("dump") {
1364 	    if (do_show(argv(1), 2))
1365 		return AC_REDRAW;
1366 	    break;
1367 	}
1368 
1369 	CASE("show") {
1370 	    if (do_show(argv(1), 2))
1371 		return AC_REDRAW;
1372 	    break;
1373 	}
1374 
1375 	CASE("compile") {
1376 	    clrdisp();
1377 	    rm_kill_file();
1378 	    free_kill_entries();
1379 	    do_kill_handling = init_kill() && do_kill_handling;
1380 	    return AC_REDRAW;
1381 	}
1382 
1383 	CASE("print-variables") {
1384 	    FILE           *p;
1385 	    if ((p = popen(ARGTAIL ? ARGTAIL : printer, "w"))) {
1386 		print_variable_config(p, 1);
1387 		pclose(p);
1388 		msg("Variables printed");
1389 	    }
1390 	    break;
1391 	}
1392 
1393 	CASE("pwd") {
1394 	    FILE           *p = popen("exec pwd", "r");
1395 	    char            dir[FILENAME];
1396 	    if (p) {
1397 		if (fgets(dir, FILENAME, p)) {
1398 		    dir[strlen(dir) - 1] = NUL;
1399 		    msg("%s", dir);
1400 		}
1401 		pclose(p);
1402 	    }
1403 	    break;
1404 	}
1405 
1406 	CASE("rmail") {
1407 	    group_header   *orig_group;
1408 	    int             rv;
1409 
1410 	    if (mail_box == NULL) {
1411 		msg("'mail' path not defined");
1412 		break;
1413 	    }
1414 	    orig_group = current_group;
1415 	    rv = folder_menu(mail_box, 2);
1416 	    init_group(orig_group);
1417 
1418 	    return rv == ME_NO_REDRAW ? ok_val : AC_REDRAW;
1419 	}
1420 
1421 	CASE("mkdir") {
1422 	    char           *dir;
1423 	    char            name_buf[FILENAME];
1424 
1425 	    if ((dir = run_mkdir(argv(1), name_buf))) {
1426 		prompt("Change to %s", dir);
1427 		if (yes(0))
1428 		    change_dir(dir, 0);
1429 	    }
1430 	    break;
1431 	}
1432 
1433 	CASE("sh") {
1434 	    suspend_nn();
1435 	    s_redraw = 0;
1436 	    return AC_REDRAW;
1437 	}
1438 
1439 	CASE("admin") {
1440 	    group_header   *cur_group;
1441 
1442 	    cur_group = current_group;
1443 	    no_raw();
1444 	    clrdisp();
1445 	    tprintf("\n\n\n\rADMINISTRATION MODE\r\n\n\n");
1446 	    admin_mode((char *) NULL);
1447 	    clrdisp();
1448 	    nn_raw();
1449 	    init_group(cur_group);
1450 	    return AC_REDRAW;
1451 	}
1452 
1453 	CASE("cost") {
1454 
1455 #ifdef ACCOUNTING
1456 	    gotoxy(0, Lines - 1);
1457 	    clrline();
1458 	    account('C', 1);
1459 #else
1460 	    msg("No accounting");
1461 #endif
1462 
1463 	    break;
1464 	}
1465 
1466 	CASE("bug") {
1467 	    if (answer((article_header *) NULL, K_BUG_REPORT, 0))
1468 		return AC_REDRAW;
1469 	    break;
1470 	}
1471 
1472 	CASE("debug") {
1473 	    print_debug_info();
1474 	    return AC_REDRAW;
1475 	}
1476 
1477 	CASE("coredump") {
1478 	    unset_raw();
1479 	    abort();
1480 	}
1481 
1482 	msg("unknown command: \"%s\"", argv(0));
1483     }
1484 
1485     return ok_val;
1486 
1487 stx_err:
1488     print_command("syntax error");
1489     return ok_val;
1490 }
1491 
1492 
1493 void
display_help(char * subject)1494 display_help(char *subject)
1495 {
1496     char            file[FILENAME];
1497 
1498     strcpy(file, "help.");
1499     strcpy(file + 5, subject);
1500 
1501     display_file(file, CLEAR_DISPLAY | CONFIRMATION);
1502 }
1503