home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / init.c < prev    next >
C/C++ Source or Header  |  1996-08-14  |  28KB  |  1,431 lines

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