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