home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume11 / mush5.7 / part07 / commands.c next >
Encoding:
C/C++ Source or Header  |  1987-09-17  |  19.1 KB  |  738 lines

  1. /* @(#)cmds.c    (c) copyright 10/18/86 (Dan Heller) */
  2.  
  3. #include "mush.h"
  4.  
  5. /*
  6.  * Note that all of the routines in here act upon and return 0 or -1.
  7.  * if -1, then the main loop will clear message lists.
  8.  */
  9.  
  10. struct cmd cmds[] = {
  11. #ifdef SIGSTOP
  12.     { "stop", stop },
  13. #endif SIGSTOP
  14.     { "?",       question_mark },{ "sh", sh },
  15.     { "alias",       do_alias    },  { "unalias",    do_alias   },
  16.     { "expand",      do_alias    },  { "cmd",     do_alias   },
  17.     { "uncmd",       do_alias    },  { "from",    do_from    },
  18.     { "un_hdr",      do_alias    },  { "my_hdr",      do_alias   },
  19.     { "fkey",       do_alias    },  { "unfkey",     do_alias   },
  20.     { "set",       set         },  { "unset",     set        },
  21.     { "ignore",      set         },  { "unignore", set        },
  22.     { "version",  do_version  },  { "help",    print_help },
  23.     { "pick",       do_pick     },  { "sort",     sort        },
  24.     { "next",      readmsg     },  { "previous", readmsg    },
  25.     { "type",     readmsg     },  { "print",    readmsg    },
  26.     { "history",  disp_hist   },  { "top",    readmsg       },
  27.     { "saveopts", save_opts   },  { "source",   source        },
  28.     { "headers",  do_hdrs     },  { "ls",    ls       },
  29.     { "folder",   folder      },  { "update",   folder     },
  30.     { "cd",       cd          },  { "pwd",    cd        },
  31.     { "exit",      quit        },  { "quit",     quit        },
  32.     { "write",       save_msg    },  { "save",     save_msg   },
  33.     { "copy",       save_msg    },  { "folders",  folders    },
  34. #ifdef CURSES
  35.     { "curses",   curses_init },  { "bind", bind_it   }, { "unbind", bind_it },
  36. #endif CURSES
  37.     { "preserve", preserve    },  { "unpreserve",  preserve   },
  38.     { "replyall", respond     },  { "replysender", respond    },
  39.     { "delete",      delete      },  { "undelete",    delete     },
  40.     { "mail",       do_mail     },  { "echo",       do_echo    },
  41.     { "lpr",       lpr           },  { "alternates",  alts       },
  42.     { NULL, quit }
  43. };
  44.  
  45. struct cmd ucb_cmds[] = {
  46.     { "t",   readmsg   }, { "n",  readmsg  }, { "p", readmsg  },
  47.     { "+",   readmsg   }, { "-", readmsg   },
  48.     { "x",   quit      }, { "q",  quit     },
  49.     { ":a",  do_hdrs   }, { ":d", do_hdrs  },
  50.     { ":o",  do_hdrs   }, { ":u", do_hdrs  }, { ":n", do_hdrs },
  51.     { "z",   do_hdrs   }, { "z-", do_hdrs  }, { "z+", do_hdrs },
  52.     { "h",   do_hdrs   }, { "H",  do_hdrs  },
  53.     { "f",   do_from   }, { "m",  do_mail  }, { "alts", alts  },
  54.     { "d",   delete    }, { "dt", delete   }, { "dp", delete  },
  55.     { "u",   delete    }, { "fo", folder   },
  56.     { "s",   save_msg  }, { "co", save_msg },
  57.     { "pre", preserve  }, { "w",  save_msg },
  58.     { "R",   respond   }, { "r",   respond },
  59.     { "reply", respond }, { "respond", respond },
  60.     { NULL, quit }
  61. };
  62.  
  63. struct cmd hidden_cmds[] = {
  64.     { "debug", toggle_debug }, { "open",     nopenfiles },
  65.     { "flags", msg_flags    }, { "stty",    my_stty    },
  66.     { "setenv",    Setenv      }, { "unsetenv",     Unsetenv   },
  67.     { "printenv", Printenv  },
  68.     { NULL, quit }
  69. };
  70.  
  71. toggle_debug(argc, argv)
  72. char **argv;
  73. {
  74.     if (argc < 2) /* no value -- toggle "debug" (off/on) */
  75.     debug = !debug;
  76.     else
  77.     debug = atoi(*++argv);
  78.     print("debugging value: %d\n", debug);
  79.     return -1;
  80. }
  81.  
  82. static
  83. sorter(cmd1, cmd2)
  84. register struct cmd *cmd1, *cmd2;
  85. {
  86.     return strcmp(cmd1->command, cmd2->command);
  87. }
  88.  
  89. sort_commands()
  90. {
  91.     qsort((char *)cmds, sizeof(cmds)/sizeof(struct cmd)-1,
  92.             sizeof(struct cmd), sorter);
  93. }
  94.  
  95. /* if + was specified, then print messages without headers.
  96.  * n or \n (which will be NULL) will print next unread or undeleted message.
  97.  */
  98. readmsg(x, argv, list)
  99. register char **argv, list[];
  100. {
  101.     register char *p = x? *argv : NULL;
  102.     register long flg = 0;
  103.  
  104.     if (x && *++argv && !strcmp(*argv, "-?"))
  105.     return help(0, "readmsg", cmd_help);
  106. #ifdef NOT_NOW
  107.     if (ison(glob_flags, IS_GETTING)) {
  108.     print("Finish editing your letter first.\n");
  109.     return -1;
  110.     }
  111. #endif NOT_NOW
  112.     if (!msg_cnt) {
  113.     print("No messages.\n");
  114.     return -1;
  115.     }
  116.     if (x && !strcmp(p, "top"))
  117.     turnon(flg, TOP);
  118.     if (p && (*p == '+')) {
  119.     turnon(flg, NO_PAGE);
  120.     turnon(flg, NO_HEADER);
  121.     }
  122.     if (x && (x = get_msg_list(argv, list)) == -1)
  123.     return -1;
  124.     else if (x == 0) {  /* no arguments were parsed (or given) */
  125.     /* get_msg_list sets current msg on */
  126.     unset_msg_bit(list, current_msg);
  127.     /* most commands move to the "next" message. type and print don't */
  128.     if ((!p || !*p || *p == 'n' || *p == '+') && current_msg < msg_cnt &&
  129.                     isoff(msg[current_msg].m_flags, UNREAD))
  130.         current_msg++;
  131.     if (p && (*p == '-' || !strcmp(p, "previous"))) {
  132.         while (--current_msg >= 0 && ison(msg[current_msg].m_flags, DELETE))
  133.         ;
  134.         if (current_msg < 0) {
  135.         print("No previous message.\n");
  136.         current_msg = 0;
  137.         return -1;
  138.         }
  139.     } else {
  140.         /*
  141.          * To be compatible with ucb-mail, find the next available unread
  142.          * message.  Stop at the end (don't wrap). Not the greatest way to
  143.          * do it, but people complain if it doesn't do it.
  144.          */
  145. #ifdef NOT_ANYMORE
  146.         if (current_msg == msg_cnt) /* wrap around */
  147.         current_msg = 0;
  148. #endif NOT_ANYMORE
  149.         /* "type" or "print" prints the current only -- "next" goes on.. */
  150.         if (!p || !*p || *p == 'n')
  151.         while (current_msg < msg_cnt &&
  152.             ison(msg[current_msg].m_flags, DELETE))
  153.             current_msg++;
  154.         if (current_msg >= msg_cnt) {
  155.         print("No more messages.\n");
  156.         current_msg = msg_cnt - 1;
  157.         return -1;
  158.         }
  159.     }
  160.     set_msg_bit(list, current_msg);
  161.     }
  162.     /* If we're piping messages, just return the message list */
  163.     if (ison(glob_flags, DO_PIPE))
  164.     return 0;
  165.     current_msg = 0;
  166.     for (x = 0; x < msg_cnt; x++)
  167.     if (msg_bit(list, x)) {
  168.         current_msg = x;
  169. #ifdef SUNTOOL
  170.         if (istool > 1) {
  171.         read_mail(NO_ITEM, 0, NO_EVENT);
  172.         return 0;
  173.         }
  174. #endif SUNTOOL
  175.         display_msg(x, flg);
  176.     }
  177.     return 0;
  178. }
  179.  
  180. preserve(n, argv, list)
  181. register int n;        /* no use for argc, so use space for a local variable */
  182. register char **argv, list[];
  183. {
  184.     register int unpre;
  185.  
  186.     unpre = !strncmp(*argv, "un", 2);
  187.     if (*++argv && !strcmp(*argv, "-?"))
  188.     return help(0, "preserve_help", cmd_help);
  189.     if (get_msg_list(argv, list) == -1)
  190.     return -1;
  191.     for (n = 0; n < msg_cnt; n++)
  192.     if (msg_bit(list, n))
  193.         if (unpre)
  194.         turnoff(msg[n].m_flags, PRESERVE);
  195.         else {
  196.         turnon(msg[n].m_flags, PRESERVE);
  197.         turnoff(msg[n].m_flags, DELETE);
  198.         }
  199.     if (istool)
  200.     (void) do_hdrs(0, DUBL_NULL, NULL);
  201.     return 0;
  202. }
  203.  
  204. lpr(n, argv, list)
  205. register int n;  /* no use for argc, so use its address space for a variable */
  206. register char **argv, list[];
  207. {
  208.     register FILE    *pp;
  209.     register long     flags = 0;
  210.     char        print_cmd[128], *printer, c, *cmd;
  211.     int            total = 0, (*oldint)(), (*oldquit)();
  212.  
  213.     turnon(flags, NO_IGNORE);
  214.     if (!(printer = do_set(set_options, "printer")) || !*printer)
  215.     printer = DEF_PRINTER;
  216.     while (argv && *++argv && **argv == '-') {
  217.     n = 1;
  218.     while (c = argv[0][n++])
  219.         switch(c) {
  220.         case 'n': turnon(flags, NO_HEADER);
  221.         when 'h': turnoff(flags, NO_IGNORE);
  222.         when 'P': case 'd':
  223.             if (!argv[0][n]) {
  224.                 print("specify printer!\n");
  225.                 return -1;
  226.             }
  227.             printer = argv[0] + n;
  228.             n += strlen(printer);
  229.         otherwise: return help(0, "lpr", cmd_help);
  230.         }
  231.     }
  232.     if (get_msg_list(argv, list) == -1)
  233.     return -1;
  234.  
  235.     if (cmd = do_set(set_options, "print_cmd"))
  236.     (void) strcpy(print_cmd, cmd);
  237.     else
  238. #ifdef SYSV
  239.     (void) sprintf(print_cmd, "%s -d%s", LPR, printer);
  240. #else
  241.     (void) sprintf(print_cmd, "%s -P%s", LPR, printer);
  242. #endif SYSV
  243.     Debug("print command: %s\n", print_cmd);
  244.     if (!(pp = popen(print_cmd, "w"))) {
  245.     error("cannot print");
  246.     return -1;
  247.     }
  248.     on_intr();
  249.     for (n = 0; isoff(glob_flags, WAS_INTR) && n < msg_cnt; n++) {
  250.     if (msg_bit(list, n)) {
  251.         if (total++)
  252.         fputc('\f', pp); /* send a formfeed for multiple copies */
  253.         print("printing message %d...", n+1);
  254.         print_more("(%d lines)\n", copy_msg(n, pp, flags));
  255.     }
  256.     }
  257.     off_intr();
  258.     (void) pclose(pp);
  259.     print_more("%d message%s printed ", total, (total==1)? "": "s");
  260.     if (cmd)
  261.     print_more("through \"%s\".\n", cmd);
  262.     else
  263.     print_more("at \"%s\".\n", printer);
  264.     return 0;
  265. }
  266.  
  267. /* save [msg_list] [file] */
  268. save_msg(n, argv, list)   /* argc isn't used, so use space for variable 'n' */
  269. register char **argv, list[];
  270. {
  271.     register FILE    *mail_fp;
  272.     register char     *file = NULL, *mode, firstchar = **argv, *tmp;
  273.     int         msg_number, force;
  274.     long         flg = 0;
  275.  
  276.     if (*++argv && !strcmp(*argv, "-?"))
  277.     return help(0, "save_help", cmd_help);
  278.     if (force = (*argv && !strcmp(*argv, "!")))
  279.     argv++;
  280.     if ((n = get_msg_list(argv, list)) == -1)
  281.     return -1;
  282.     argv += n;
  283.     if (*argv && *(file = *argv) == '\\')
  284.     file++;
  285.     else if (!file) {
  286.     /* if no filename specified, save in ~/mbox */
  287.     if (firstchar == 'w') {
  288.         /* mbox should have headers. If he really wants it, specify it */
  289.         print("Must specify file name for 'w'\n");
  290.         return -1;
  291.     }
  292.     if (!(file = do_set(set_options, "mbox")) || !*file)
  293.         file = DEF_MBOX;
  294.     }
  295.     n = 1; /* tell getpath to ignore no such file or directory */
  296.     tmp = getpath(file, &n);
  297.     if (n < 0) {
  298.     print("%s: %s\n", file, tmp);
  299.     return -1;
  300.     } else if (n) {
  301.     print("%s is a directory\n", file);
  302.     return -1;
  303.     }
  304.     file = tmp;
  305.     if (force || access(file, 0))
  306.     mode = "w", force = 0;
  307.     else
  308.     mode = "a";
  309.  
  310.     if (!(mail_fp = fopen(file, mode))) {
  311.     error("cannot save in \"%s\"", file);
  312.     return -1;
  313.     }
  314. #ifdef SUNTOOL
  315.     if (istool)
  316.     lock_cursors();
  317. #endif SUNTOOL
  318.     turnon(flg, NO_IGNORE);
  319.     if (firstchar == 'w')
  320.     turnon(flg, NO_HEADER);
  321.     else
  322.     turnon(flg, UPDATE_STATUS);
  323.     if (do_set(set_options, "keepsave"))
  324.     firstchar = 'c';
  325.     for (n = msg_number = 0; msg_number < msg_cnt; msg_number++)
  326.     if (msg_bit(list, msg_number)) {
  327.             print("%sing msg %d... ",
  328.         (firstchar == 's')? "Sav" : "Writ", msg_number+1);
  329.         print_more("(%d lines)\n", copy_msg(msg_number, mail_fp, flg));
  330.         /* only mark "deleted" if mailfile is /usr/spool/mail and
  331.          * we're not "copying.  If keepsave is set, then firstchar
  332.          * will have already been changed to 'c'
  333.          */
  334.         if (is_spool(mailfile) && firstchar != 'c')
  335.         turnon(msg[msg_number].m_flags, DELETE);
  336.         n++;
  337.     }
  338.     fclose(mail_fp);
  339.     print_more("%s %d msg%s to %s\n",
  340.         (*mode == 'a')? "Appended" : "Saved", n, (n != 1)? "s": "", file);
  341.     if (is_spool(mailfile))
  342.     turnon(glob_flags, DO_UPDATE);
  343. #ifdef SUNTOOL
  344.     if (istool) {
  345.     unlock_cursors();
  346.     (void) do_hdrs(0, DUBL_NULL, NULL);
  347.     }
  348. #endif SUNTOOL
  349.     return 0;
  350. }
  351.  
  352. respond(n, argv, list)
  353. register int n;  /* no use for argc, so use its address space for a variable */
  354. register char **argv, *list;
  355. {
  356.     register char *cmd = *argv;
  357.     char list1[MAXMSGS_BITS];
  358.  
  359.     if (*++argv && !strcmp(*argv, "-?"))
  360.     return help(0, "respond_help", cmd_help);
  361.     if ((n = get_msg_list(argv, list)) == -1)
  362.     return -1;
  363.     /* make into our own list so ~: commands don't overwrite this list */
  364.     bitput(list, list1, MAXMSGS, =);
  365.     /* back up one arg to replace "cmd" in the new argv[0] */
  366.     argv += (n-1);
  367.     if (!strcmp(cmd, "replyall"))
  368.     Upper(*cmd);
  369.     strdup(argv[0], cmd);
  370.     current_msg = -1;
  371.     for (n = 0; n < msg_cnt; n++)
  372.     if (msg_bit(list1, n)) {
  373.         if (current_msg > -1 && istool) {
  374.         print("tool mode can not respond to multiple messages.");
  375.         continue;
  376.         }
  377.         current_msg = n;
  378.         set_isread(n);
  379.         if (do_mail(1, argv) == -1) /* do_mail doesn't look at argc */
  380.         return -1;
  381.     }
  382.     return 0;
  383. }
  384.  
  385. /* cd to a particular directory specified by "p" */
  386. cd(x, argv) /* argc, unused -- use space for a non-register variable */
  387. register char **argv;
  388. {
  389.     char *tmp, cwd[128], *p = argv[1];
  390. #ifdef SYSV
  391.     char *getcwd();
  392. #else
  393.     char *getwd();
  394. #endif SYSV
  395.     int err = 0;
  396.  
  397.     if (!strcmp(*argv, "pwd")) {
  398.     p = do_set(set_options, "cwd");
  399.     if (p && *p) {
  400.         print("%s\n", p);
  401.         return -1;
  402.     }
  403.     }
  404.     if (!p || !*p)
  405.     p = (**argv == 'p')? "." : "~";
  406.     x = 0;
  407.     tmp = getpath(p, &x);
  408.     if (x == -1)
  409.         print("%s: %s.\n", p, tmp), err++;
  410.     else if (!x)
  411.         print("%s: not a directory.\n", tmp), err++;
  412.     else if (chdir(tmp))
  413.         error("can't chdir to %s", tmp), err++;
  414. #ifdef SYSV
  415.     if (getcwd(cwd, 128) == NULL)
  416. #else
  417.     if (getwd(cwd) == NULL)
  418. #endif SYSV
  419.     print("can't get cwd: %s.\n", cwd), err++;
  420.     else {
  421.     char **new_argv, buf[256];
  422.     (void) sprintf(buf, "set cwd = \"%s\"", cwd);
  423.     Debug("%s\n", buf);
  424.     if (new_argv = mk_argv(buf, &x, 1))
  425.         (void) add_option(&set_options, new_argv), free_vec(new_argv);
  426.     }
  427.     if (istool || iscurses || err) {
  428.     if (err)
  429.         turnon(glob_flags, CONT_PRNT);
  430.     print("Working dir: %s\n", cwd);
  431.     }
  432.     return -1;
  433. }
  434.  
  435. quit(argc, argv)
  436. char **argv;
  437. {
  438.     if (argc > 1 && !strcmp(argv[1], "-?"))
  439.      return help(0, "quit_help", cmd_help);
  440.     if ((!argc || (*argv && **argv == 'q')) && ison(glob_flags, DO_UPDATE)
  441.     && !copyback())
  442.     return -1;
  443. #ifdef CURSES
  444.     if (iscurses) {
  445.     /* we may already be on the bottom line; some cases won't be */
  446.     move(LINES-1, 0), refresh();
  447.     }
  448. #endif CURSES
  449.     cleanup(0);
  450. #ifdef lint
  451.     return 0;
  452. #endif lint
  453. }
  454.  
  455. delete(argc, argv, list)
  456. register int argc;
  457. register char **argv, list[];
  458. {
  459.     register int prnt_next, undel = argc && **argv == 'u';
  460.  
  461.     prnt_next = (argv && (!strcmp(*argv, "dt") || !strcmp(*argv, "dp")));
  462.  
  463.     if (argc && *++argv && !strcmp(*argv, "-?")) {
  464.     print("usage: delete/undelete [msg_list]\n");
  465.     return -1;
  466.     }
  467.  
  468.     if (get_msg_list(argv, list) == -1)
  469.     return -1;
  470.     for (argc = 0; argc < msg_cnt; argc++)
  471.     if (msg_bit(list, argc))
  472.         if (undel)
  473.         turnoff(msg[argc].m_flags, DELETE);
  474.         else
  475.         turnon(msg[argc].m_flags, DELETE);
  476.  
  477.     /* only if current_msg has been affected && not in curses mode */
  478.     if (prnt_next == 0 && !iscurses && msg_bit(list, current_msg))
  479.     prnt_next = !!do_set(set_options, "autoprint"); /* change to boolean */
  480.  
  481.     turnon(glob_flags, DO_UPDATE);
  482.  
  483.     /* goto next available message if current was just deleted.
  484.      * If there are no more messages, turnoff prnt_next.
  485.      */
  486.     if (!iscurses && !undel && ison(msg[current_msg].m_flags, DELETE) &&
  487.                 !next_msg(FALSE, DELETE) && prnt_next)
  488.         prnt_next = 0;
  489.  
  490.     if (prnt_next)
  491.     display_msg(current_msg, (long)0);
  492.     if (istool)
  493.     (void) do_hdrs(0, DUBL_NULL, NULL);
  494.     return 0;
  495. }
  496.  
  497. /*
  498.  * historically from the "from" command in ucb-mail, this just prints
  499.  * the composed header of the messages set in list or in pipe.
  500.  */
  501. do_from(n, argv, list)
  502. char **argv, list[];
  503. {
  504.     int inc_cur_msg = 0;
  505.  
  506.     if (argv && *++argv && !strcmp(*argv, "-?"))
  507.     return help(0, "from", cmd_help);
  508.     if (argv && *argv && (!strcmp(*argv, "+") || !strcmp(*argv, "-")))
  509.     if (!strcmp(*argv, "+")) {
  510.         if (!*++argv && current_msg < msg_cnt-1)
  511.         current_msg++;
  512.         inc_cur_msg = 1;
  513.     } else if (!strcmp(*argv, "-")) {
  514.         if (!*++argv && current_msg > 0)
  515.         current_msg--;
  516.         inc_cur_msg = -1;
  517.     }
  518.     if (get_msg_list(argv, list) == -1)
  519.     return -1;
  520.     for (n = 0; n < msg_cnt; n++)
  521.     if (msg_bit(list, n)) {
  522.         wprint("%s\n", compose_hdr(n));
  523.         /* if -/+ given, set current message pointer to this message */
  524.         if (inc_cur_msg) {
  525.         current_msg = n;
  526.         /* if - was given, then set to first listed message.
  527.          * otherwise, + means last listed message -- let it go...
  528.          */
  529.         if (inc_cur_msg < 0)
  530.             inc_cur_msg = 0;
  531.         }
  532.     }
  533.     return 0;
  534. }
  535.  
  536. /*
  537.  * Do an ls from the system.
  538.  * Read from a popen and use wprint in case the tool does this command.
  539.  * The folders command uses this command.
  540.  */
  541. ls(x, argv)
  542. char **argv;
  543. {
  544.     register char  *p, *tmp;
  545.     char       buf[128];
  546.     register FILE  *pp;
  547.  
  548.     if (*++argv && !strcmp(*argv, "-?"))
  549.     return help(0, "ls", cmd_help);
  550.     if (!(p = do_set(set_options, "lister")))
  551.     p = "";
  552.     (void) sprintf(buf, "%s -C%s ", LS_COMMAND, p);
  553.     p = buf+strlen(buf);
  554.     for ( ; *argv; ++argv) {
  555.     x = 0;
  556.     if (**argv != '-')
  557.         tmp = getpath(*argv, &x);
  558.     else
  559.         tmp = *argv;
  560.     if (x == -1) {
  561.         wprint("%s: %s\n", *argv, tmp);
  562.         return -1;
  563.     }
  564.     p += strlen(sprintf(p, " %s", tmp));
  565.     }
  566.     if (!(pp = popen(buf, "r"))) {
  567.     error(buf);
  568.     return -1;
  569.     }
  570.     turnon(glob_flags, IGN_SIGS);
  571.     while (fgets(buf, 127, pp))
  572.     wprint(buf);
  573.     (void) pclose(pp);
  574.     turnoff(glob_flags, IGN_SIGS);
  575.     return 0;
  576. }
  577.  
  578. sh(un_used, argv)
  579. char **argv;
  580. {
  581.     register char *p;
  582.     char buf[128];
  583.  
  584.     if (istool > 1 || *++argv && !strcmp(*argv, "-?"))
  585.     return help(0, "shell", cmd_help);
  586.     if (!(p = do_set(set_options, "shell"))
  587.     && !(p = do_set(set_options, "SHELL")))
  588.     p = DEF_SHELL;
  589.     if (!*argv)
  590.     (void) strcpy(buf, p);
  591.     else
  592.     (void) argv_to_string(buf, argv);
  593.     echo_on();
  594.     (void) system(buf);
  595.     echo_off();
  596.     return 0;
  597. }
  598.  
  599. question_mark(x, argv)
  600. char **argv;
  601. {
  602.     int n = 0;
  603.     char *Cmds[50], *p, *malloc(), buf[30];
  604.  
  605.     if (!*++argv) {
  606.     for (x = 0; cmds[x].command; x++) {
  607.         if (!(x % 5))
  608.         if (!(p = Cmds[n++] = malloc(80))) {
  609.             error("malloc in question_mark()");
  610.             free_vec(Cmds);
  611.             return -1;
  612.         }
  613.         p += strlen(sprintf(p, "%-11.11s  ", cmds[x].command));
  614.     }
  615.     Cmds[n++] = savestr("Type: `command' -? for help with most commands.");
  616.     Cmds[n] = NULL;
  617.     (void) help(0, Cmds, NULL);
  618.     free_vec(Cmds);
  619.     } else {
  620.     for (x = 0; cmds[x].command; x++)
  621.         if (!strcmp(*argv, cmds[x].command))
  622.         return cmd_line(sprintf(buf, "%s -?", *argv), msg_list);
  623.     print("Unknown command: %s\n", *argv);
  624.     }
  625.     return -1;
  626. }
  627.  
  628. #ifdef SIGSTOP
  629. stop(argc, argv)
  630. char **argv;
  631. {
  632.     if (istool)
  633.     print("Not a tool-based option.");
  634.     if (argc && *++argv && !strcmp(*argv, "-?"))
  635.     return help(0, "stop", cmd_help);
  636.     if (kill(getpid(), SIGTSTP) == -1)
  637.     error("couldn't stop myself");
  638.     return -1;
  639. }
  640. #endif SIGSTOP
  641.  
  642. extern char **environ;
  643. static int spaces = 0;
  644.  
  645. Setenv(i, argv)
  646. char **argv;
  647. {
  648.     char *newstr;
  649.  
  650.     if (i < 2 || i > 3 || !strcmp(argv[1], "-?"))
  651.     return help(0, "setenv", cmd_help);
  652.  
  653.     if (i == 3) {
  654.     newstr = malloc(strlen(argv[1]) + strlen(argv[2]) + 2);
  655.     (void) sprintf(newstr, "%s=%s", argv[1], argv[2]);
  656.     } else {
  657.     newstr = malloc(strlen(argv[1]) + 2);
  658.     (void) sprintf(newstr, "%s=", argv[1]);
  659.     }
  660.  
  661.     (void) Unsetenv(2, argv);
  662.  
  663.     for (i = 0; environ[i]; i++);
  664.     if (!spaces) {
  665.     char **new_environ = (char **)malloc((i+2) * sizeof(char *));
  666.     /* add 1 for the new item, and 1 for null-termination */
  667.     if (!new_environ) {
  668.         free(newstr);
  669.         return -1;
  670.     }
  671.     spaces = 1;
  672.     for (i = 0; new_environ[i] = environ[i]; i++);
  673.     xfree(environ);
  674.     environ = new_environ;
  675.     }
  676.     environ[i] = newstr;
  677.     environ[i+1] = NULL;
  678.     spaces--;
  679.     return -1;
  680. }
  681.  
  682. Unsetenv(n, argv)
  683. char **argv;
  684. {
  685.     char **envp, **last;
  686.  
  687.     if (n != 2 || !strcmp(argv[1], "-?"))
  688.     return help(0, "unsetenv", cmd_help);
  689.  
  690.     n = strlen(argv[1]);
  691.     for (last = environ; *last; last++);
  692.     last--;
  693.  
  694.     for (envp = environ; envp <= last; envp++) {
  695.     if (strncmp(argv[1], *envp, n) == 0 && (*envp)[n] == '=') {
  696.         xfree(*envp);
  697.         *envp = *last;
  698.         *last-- = NULL;
  699.         spaces++;
  700.     }
  701.     }
  702.     return -1;
  703. }
  704.  
  705. Printenv()
  706. {
  707.     char **e = environ;
  708.     while (*e)
  709.     wprint("%s\n", *e++);
  710.     return -1;
  711. }
  712.  
  713. /*
  714.  * internal stty call to allow the user to change his tty character
  715.  * settings.  sorry, no way to change cbreak/echo modes.  Save echo_flg
  716.  * so that execute() won't reset it.
  717.  */
  718. my_stty(un_used, argv)
  719. char **argv;
  720. {
  721.     long save_echo = ison(glob_flags, ECHO_FLAG);
  722.  
  723.     turnon(glob_flags, ECHO_FLAG);
  724.     execute(argv);
  725.     if (save_echo)
  726.     turnon(glob_flags, ECHO_FLAG);
  727.     else
  728.     turnoff(glob_flags, ECHO_FLAG);
  729.  
  730.     savetty();
  731. #ifdef TIOCGLTC
  732.     if (ioctl(0, TIOCGLTC, <chars))
  733.     error("TIOCGLTC");
  734. #endif TIOCGLTC
  735.     echo_off();
  736.     return -1;
  737. }
  738.