home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / mush.lzh / mush.5 < prev    next >
Text File  |  1990-05-06  |  56KB  |  1,979 lines

  1.  
  2. #! /bin/sh
  3. # This is a shell archive.  Remove anything before this line, then feed it
  4. # into a shell via "sh file" or similar.  To overwrite existing files,
  5. # type "sh file -c".
  6. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  7. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  8. # If this archive is complete, you will see the following message at the end:
  9. #        "End of archive 5 (of 19)."
  10. # Contents:  mush/loop.c mush/pick.c
  11. # Wrapped by argv@turnpike on Wed May  2 13:59:22 1990
  12. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  13. if test -f 'mush/loop.c' -a "${1}" != "-c" ; then 
  14.   echo shar: Will not clobber existing file \"'mush/loop.c'\"
  15. else
  16. echo shar: Extracting \"'mush/loop.c'\" \(34930 characters\)
  17. sed "s/^X//" >'mush/loop.c' <<'END_OF_FILE'
  18. X/* loop.c     (c) copyright 1986 (Dan Heller) */
  19. X
  20. X/*
  21. X * Here is where the main loop for text mode exists. Also, all the
  22. X * history is kept here and all the command parsing and execution
  23. X * and alias expansion in or out of text/graphics mode is done here.
  24. X */
  25. X
  26. X#include "mush.h"
  27. X#include "version.h"
  28. X
  29. X#ifdef BSD
  30. X#include <sys/wait.h>
  31. X#else
  32. X#ifndef SYSV
  33. X#include <wait.h>
  34. X#endif /* SYSV */
  35. X#endif /* BSD */
  36. X
  37. X#define ever (;;)
  38. X#define MAXARGS        100
  39. X#define isdelimeter(c)    (index(" \t;|", c))
  40. X
  41. Xchar *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
  42. Xchar *calloc();
  43. X
  44. Xstruct history {
  45. X    int histno;
  46. X    char **argv;
  47. X    struct history *prev;
  48. X    struct history *next;
  49. X};
  50. Xstatic struct history *hist_head, *hist_tail;
  51. X#define malloc(n)    (struct history *)calloc((unsigned)1,(unsigned)(n))
  52. X#define NULL_HIST    (struct history *)0
  53. X
  54. Xstatic char *last_aliased;
  55. Xstatic int hist_size, print_only;
  56. X
  57. Xdo_loop()
  58. X{
  59. X    register char *p, **argv;
  60. X    char      **last_argv = DUBL_NULL, line[256];
  61. X    int         argc, c = (iscurses - 1);
  62. X#ifdef CURSES
  63. X    int          save_echo_flg = FALSE;
  64. X#endif /* CURSES */
  65. X
  66. X    /* catch the right signals -- see main.c for other signal catching */
  67. X    (void) signal(SIGINT, catch);
  68. X    (void) signal(SIGQUIT, catch);
  69. X    (void) signal(SIGHUP, catch);
  70. X#ifndef SUNTOOL
  71. X    (void) signal(SIGTERM, catch);
  72. X    (void) signal(SIGCHLD,
  73. X#ifndef SYSV
  74. X               sigchldcatcher
  75. X#else /* SYSV */
  76. X               SIG_DFL
  77. X#endif /* SYSV */
  78. X               );
  79. X#endif /* SUNTOOL*/
  80. X
  81. X    turnoff(glob_flags, IGN_SIGS);
  82. X    if (hist_size == 0) /* if user didn't set history in .rc file */
  83. X    hist_size = 1;
  84. X
  85. X    for ever {
  86. X    if (setjmp(jmpbuf)) {
  87. X        Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
  88. X#ifdef CURSES
  89. X        if (c > 0) { /* don't pass last command back to curses_command() */
  90. X        iscurses = TRUE;
  91. X        c = hit_return();
  92. X        }
  93. X#endif /* CURSES */
  94. X    }
  95. X#ifdef CURSES
  96. X    if (iscurses || c > -1) {
  97. X        /* if !iscurses, we know that we returned from a curses-based
  98. X         * call and we really ARE still in curses. Reset tty modes!
  99. X         */
  100. X        if (ison(glob_flags, ECHO_FLAG)) {
  101. X        turnoff(glob_flags, ECHO_FLAG);
  102. X        echo_off();
  103. X        save_echo_flg = TRUE;
  104. X        }
  105. X        if (!iscurses) {
  106. X        iscurses = TRUE;
  107. X        c = hit_return();
  108. X        }
  109. X        if (c < 0)
  110. X        c = 0;
  111. X        if ((c = curses_command(c)) == -1 && save_echo_flg) {
  112. X        echo_on();
  113. X        turnon(glob_flags, ECHO_FLAG);
  114. X        save_echo_flg = FALSE;
  115. X        }
  116. X        continue;
  117. X    }
  118. X#endif /* CURSES */
  119. X    clear_msg_list(msg_list);
  120. X    (void) check_new_mail();
  121. X
  122. X    /* print a prompt according to printf like format:
  123. X     * (current message, deleted, unread, etc) are found in mail_status.
  124. X     */
  125. X    mail_status(1);
  126. X    if (Getstr(line, sizeof(line), 0) > -1)
  127. X        p = line;
  128. X    else {
  129. X        if (isatty(0) && (p = do_set(set_options, "ignoreeof"))) {
  130. X        if (!*p)
  131. X            continue;
  132. X        else
  133. X            p = strcpy(line, p); /* so processing won't destroy var */
  134. X        } else {
  135. X        putchar('\n');
  136. X        (void) mush_quit(0, DUBL_NULL);
  137. X        continue; /* quit may return if new mail arrives */
  138. X        }
  139. X    }
  140. X
  141. X    skipspaces(0);
  142. X    if (!*p && !(p = do_set(set_options, "newline"))) {
  143. X        (void) readmsg(0, DUBL_NULL, msg_list);
  144. X        continue;
  145. X    }
  146. X    if (!*p) /* if newline is set, but no value, then continue */
  147. X        continue;
  148. X
  149. X    /* upon error, argc = -1 -- still save in history so user can
  150. X     * modify syntax error. if !argv, error is too severe.  We pass
  151. X     * the last command typed in last_argv for history reference, and
  152. X     * get back the current command _as typed_ (unexpanded by aliases
  153. X     * or history) in last_argv.
  154. X     */
  155. X    if (!(argv = make_command(p, &last_argv, &argc)))
  156. X        continue;
  157. X    /* now save the old argv in a newly created history structure */
  158. X    (void) add_history(0, last_argv); /* argc is currently ignored */
  159. X
  160. X    if (print_only) {
  161. X        print_only = 0;
  162. X        free_vec(argv);
  163. X    } else if (argc > -1)
  164. X        (void) do_command(argc, argv, msg_list);
  165. X    }
  166. X}
  167. X
  168. X/* Add a command to the history list
  169. X */
  170. X/*ARGSUSED*/
  171. Xadd_history(un_used, argv)
  172. Xchar **argv;
  173. X{
  174. X    struct history *new;
  175. X
  176. X    if (!(new = malloc(sizeof (struct history))))
  177. X    error("can't increment history");
  178. X    else {
  179. X    new->histno = ++hist_no;
  180. X    new->argv = argv;    /* this is the command _as typed_ */
  181. X    new->next = NULL_HIST;
  182. X    new->prev = hist_head;
  183. X    /* if first command, the tail of the list is "new" because
  184. X     * nothing is in the list.  If not the first command, the
  185. X     * head of the list's "next" pointer points to the new command.
  186. X     */
  187. X    if (hist_head)
  188. X        hist_head->next = new;
  189. X    else
  190. X        hist_tail = new;
  191. X    hist_head = new;
  192. X    }
  193. X    /*
  194. X     * truncate the history list to the size of the history.
  195. X     * Free the outdated command (argv) and move the tail closer to front.
  196. X     * use a while loop in case the last command reset histsize to "small"
  197. X     */
  198. X    while (hist_head->histno - hist_tail->histno >= hist_size) {
  199. X    hist_tail = hist_tail->next;
  200. X    free_vec(hist_tail->prev->argv);
  201. X    xfree((char *) (hist_tail->prev));
  202. X    hist_tail->prev = NULL_HIST;
  203. X    }
  204. X}
  205. X
  206. X/* make a command from "buf".
  207. X * first, expand history references. make an argv from that and save
  208. X * in last_argv (to be passed back and stored in history). After that,
  209. X * THEN expand aliases. return that argv to be executed as a command.
  210. X */
  211. Xchar **
  212. Xmake_command(start, last_argv, argc)
  213. Xregister char *start, ***last_argv;
  214. Xint *argc;
  215. X{
  216. X    register char *p, **tmp;
  217. X    char buf[BUFSIZ];
  218. X
  219. X    if (!last_argv)
  220. X    tmp = DUBL_NULL;
  221. X    else
  222. X    tmp = *last_argv;
  223. X    /* first expand history -- (here's where argc gets set)
  224. X     * pass the buffer, the history list to reference if \!* (or whatever)
  225. X     * result in static buffer (pointed to by p) -- even if history parsing is
  226. X     * ignored, do this to remove \'s behind !'s and verifying matching quotes
  227. X     */
  228. X    if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > sizeof buf)
  229. X    return DUBL_NULL;
  230. X    /* if history was referenced in the command, echo new command */
  231. X    if (*argc)
  232. X    puts(buf);
  233. X
  234. X    /* argc may == -1; ignore this error for now but catch it later */
  235. X    if (!(tmp = mk_argv(buf, argc, 0)))
  236. X    return DUBL_NULL;
  237. X
  238. X    /* save this as the command typed */
  239. X    if (last_argv)
  240. X    *last_argv = tmp;
  241. X
  242. X    /* expand all aliases (recursively)
  243. X     * pass _this_ command (as typed and without aliases) to let aliases
  244. X     * with "!*" be able to reference the command line just typed.
  245. X     */
  246. X    if (alias_stuff(buf, *argc, tmp) == -1)
  247. X    return DUBL_NULL;
  248. X
  249. X    if (!last_argv)
  250. X    free_vec(tmp);
  251. X
  252. X    /* with everything expanded, build final argv from new buffer
  253. X     * Note that backslashes and quotes still exist. Those are removed
  254. X     * because argument final is 1.
  255. X     */
  256. X    tmp = mk_argv(buf, argc, 1);
  257. X    return tmp;
  258. X}
  259. X
  260. X/* Return values from commands, see check_internal() */
  261. Xstatic int last_status;            /* Changes after every command */
  262. Xstatic char last_output[MAXMSGS];    /* Changes after SUCCESSFUL command */
  263. X
  264. X/*
  265. X * do the command specified by the argument vector, argv.
  266. X * First check to see if argc < 0. If so, someone called this
  267. X * command and they should not have! make_command() will return
  268. X * an argv but it will set argc to -1 if there's a syntax error.
  269. X */
  270. Xdo_command(argc, argv, list)
  271. Xchar **argv, list[];
  272. X{
  273. X    register char *p;
  274. X    char **tmp = argv, *next_cmd = NULL;
  275. X    int i, status = 0;
  276. X    long do_pipe = ison(glob_flags, DO_PIPE);
  277. X
  278. X    if (argc <= 0) {
  279. X    turnoff(glob_flags, DO_PIPE);
  280. X    return -1;
  281. X    }
  282. X
  283. X    clear_msg_list(list);
  284. X
  285. X    for (i = 0; do_pipe >= 0 && argc; argc--) {
  286. X    p = argv[i];
  287. X    /* mk_argv inserts a boolean in argv[i][2] for separators */
  288. X    if ((!strcmp(p, "|") || !strcmp(p, ";")) && p[2]) {
  289. X        if (do_pipe = (*p == '|'))
  290. X        turnon(glob_flags, DO_PIPE);
  291. X        else if (next_cmd = argv[i+1])
  292. X        argv[i+1] = NULL, argc--;
  293. X        argv[i] = NULL;
  294. X        if ((status = exec_argv(i, argv, list)) <= -1)
  295. X        mac_flush();
  296. X        else
  297. X        list_to_str(list, last_output);
  298. X        turnon(glob_flags, IGN_SIGS); /* prevent longjmp */
  299. X        /* if piping, then don't call next command if this one failed. */
  300. X        if (status <= -1 && do_pipe) {
  301. X        print("Broken pipe.\n");
  302. X        do_pipe = -1, turnoff(glob_flags, DO_PIPE);
  303. X        }
  304. X        last_status = status;
  305. X        /* if command failed and piping, or command worked and not piping */
  306. X        if (do_pipe <= 0)
  307. X        status = 0, clear_msg_list(list);
  308. X        /* else command worked and piping: set is_pipe */
  309. X        else if (!status)
  310. X        turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
  311. X        argv[i] = p;
  312. X        argv += (i+1);
  313. X        i = 0;
  314. X        turnoff(glob_flags, IGN_SIGS);
  315. X    } else
  316. X        i++;
  317. X    }
  318. X    if (*argv && do_pipe >= 0) {
  319. X    status = exec_argv(i, argv, list);
  320. X    turnon(glob_flags, IGN_SIGS);
  321. X    if (status < 0) {
  322. X        mac_flush();
  323. X    } else
  324. X        list_to_str(list, last_output);
  325. X    last_status = status;
  326. X    }
  327. X    Debug("freeing: "), print_argv(tmp);
  328. X    free_vec(tmp);
  329. X    turnoff(glob_flags, DO_PIPE), turnoff(glob_flags, IS_PIPE);
  330. X    if (next_cmd) {
  331. X    if (tmp = mk_argv(next_cmd, &argc, 1)) {
  332. X        turnoff(glob_flags, IGN_SIGS);
  333. X        status = do_command(argc, tmp, list);
  334. X        turnon(glob_flags, IGN_SIGS);
  335. X    } else
  336. X        status = argc;
  337. X    xfree(next_cmd);
  338. X    }
  339. X    turnoff(glob_flags, IGN_SIGS);
  340. X    return status;
  341. X}
  342. X
  343. Xexec_argv(argc, argv, list)
  344. Xregister char **argv, list[];
  345. X{
  346. X    register int n;
  347. X
  348. X    if (!argv || !*argv || argv[0][0] == '\\' && !argv[0][1]) {
  349. X    if (ison(glob_flags, IS_PIPE))
  350. X        print("Invalid null command.\n");
  351. X    else if (ison(glob_flags, DO_PIPE)) {
  352. X        set_msg_bit(list, current_msg);
  353. X        return 0;
  354. X    }
  355. X    return -1;
  356. X    } else if (argv[0][0] == '\\') {
  357. X    /* Can't change *argv (breaks free_vec),
  358. X     *  so shift to remove the backslash
  359. X     */
  360. X    for (n = 0; argv[0][n]; n++)
  361. X        argv[0][n] = argv[0][n+1];
  362. X    }
  363. X    Debug("executing: "), print_argv(argv);
  364. X
  365. X    /* if interrupted during execution of a command, return -1 */
  366. X    if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
  367. X    Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
  368. X    return -1;
  369. X    }
  370. X
  371. X    /* standard commands */
  372. X    for (n = 0; cmds[n].command; n++)
  373. X    if (!strcmp(argv[0], cmds[n].command))
  374. X        return (*cmds[n].func)(argc, argv, list);
  375. X
  376. X    /* ucb-Mail compatible commands */
  377. X    for (n = 0; ucb_cmds[n].command; n++)
  378. X    if (!strcmp(argv[0], ucb_cmds[n].command))
  379. X        return (*ucb_cmds[n].func)(argc, argv, list);
  380. X
  381. X    /* for hidden, undocumented commands */
  382. X    for (n = 0; hidden_cmds[n].command; n++)
  383. X    if (!strcmp(argv[0], hidden_cmds[n].command))
  384. X        return (*hidden_cmds[n].func)(argc, argv, list);
  385. X
  386. X    n = -1; /* default to failure */
  387. X    if ((isdigit(**argv) || index("^.*$-`{}", **argv))
  388. X            && (n = get_msg_list(argv, list)) != 0) {
  389. X    if (n < 0)
  390. X        return -1;
  391. X    else if (isoff(glob_flags, DO_PIPE))
  392. X        for (n = 0; n < msg_cnt; n++)
  393. X        if (msg_bit(list, n)) {
  394. X            display_msg((current_msg = n), (long)0);
  395. X            unset_msg_bit(list, n);
  396. X        }
  397. X    return 0;
  398. X    } else {
  399. X    /* get_msg_list will set the current message bit if nothing parsed */
  400. X    if (n == 0)
  401. X        unset_msg_bit(list, current_msg);
  402. X    if (strlen(*argv) == 1 && index("$^.", **argv)) {
  403. X        if (!msg_cnt) {
  404. X        print("No messages.");
  405. X        return -1;
  406. X        } else {
  407. X        if (**argv != '.')
  408. X            current_msg = (**argv == '$') ? msg_cnt-1 : 0;
  409. X        set_msg_bit(list, current_msg);
  410. X        display_msg(current_msg, (long)0);
  411. X        }
  412. X        return 0;
  413. X    }
  414. X    }
  415. X
  416. X    if (!istool && do_set(set_options, "unix")) {
  417. X    if (ison(glob_flags, IS_PIPE)) {
  418. X        return pipe_msg(argc, argv, list);
  419. X    } else
  420. X        execute(argv);  /* try to execute a unix command */
  421. X    return -1; /* doesn't affect messages! */
  422. X    }
  423. X
  424. X    print("%s: command not found.\n", *argv);
  425. X    if (!istool)
  426. X    print("type '?' for valid commands, or type `help'\n");
  427. X    return -1;
  428. X}
  429. X
  430. X/* recursively look for aliases on a command line.  aliases may
  431. X * reference other aliases.
  432. X */
  433. Xalias_stuff(b, argc, Argv)
  434. Xregister char     *b, **Argv;
  435. X{
  436. X    register char     *p, **argv = DUBL_NULL;
  437. X    register int     n = 0, i = 0, Argc;
  438. X    static int         loops;
  439. X    int         dummy;
  440. X
  441. X    if (++loops == 20) {
  442. X    print("Alias loop.\n");
  443. X    return -1;
  444. X    }
  445. X    for (Argc = 0; Argc < argc; Argc++) {
  446. X    register char *h = Argv[n + ++i];
  447. X    register char *p2 = "";
  448. X    int sep;
  449. X
  450. X    /* we've hit a command separator or the end of the line */
  451. X    if (h && strcmp(h, ";") && strcmp(h, "|"))
  452. X        continue;
  453. X
  454. X    /* create a new argv containing this (possible subset) of argv */
  455. X    if (!(argv = (char **)calloc((unsigned)(i+1), sizeof (char *))))
  456. X        continue;
  457. X    sep = n + i;
  458. X    while (i--)
  459. X        strdup(argv[i], Argv[n+i]);
  460. X
  461. X    if ((!last_aliased || strcmp(last_aliased, argv[0]))
  462. X            && (p = alias_expand(argv[0]))) {
  463. X        /* if history was referenced, ignore the rest of argv
  464. X         * else copy all of argv onto the end of the buffer.
  465. X         */
  466. X        if (!(p2 = hist_expand(p, argv, &dummy)))
  467. X        break;
  468. X        if (!dummy)
  469. X        (void) argv_to_string(p2+strlen(p2), argv+1);
  470. X        if (Strcpy(b, p2) > BUFSIZ) {
  471. X        print("Not enough buffer space.\n");
  472. X        break;
  473. X        }
  474. X        /* release old argv and build a new one based on new string */
  475. X        free_vec(argv);
  476. X        if (!(argv = mk_argv(b, &dummy, 0)))
  477. X        break;
  478. X        if (alias_stuff(b, dummy, argv) == -1)
  479. X        break;
  480. X    } else
  481. X        b = argv_to_string(b, argv);
  482. X    xfree(last_aliased), last_aliased = NULL;
  483. X    free_vec(argv);
  484. X    b += strlen(b);
  485. X    if (h) {
  486. X        b += strlen(sprintf(b, " %s ", h));
  487. X        while (++Argc < argc && (h = Argv[Argc]))
  488. X        if (Argc > sep && strcmp(h, ";"))
  489. X            break;
  490. X        n = Argc--;
  491. X    }
  492. X    i = 0;
  493. X    }
  494. X    xfree(last_aliased), last_aliased = NULL;
  495. X    --loops;
  496. X    if (Argc < argc) {
  497. X    free_vec(argv);
  498. X    return -1;
  499. X    }
  500. X    return 0;
  501. X}
  502. X
  503. Xchar *
  504. Xalias_expand(cmd)
  505. Xregister char *cmd;
  506. X{
  507. X    register char *p;
  508. X    register int x;
  509. X
  510. X    if (!(p = do_set(functions, cmd)))
  511. X    return NULL;
  512. X    last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
  513. X    if (isoff(glob_flags, WARNING))
  514. X    return p;
  515. X    for (x = 0; cmds[x].command; x++)
  516. X    if (!strcmp(cmd, cmds[x].command)) {
  517. X        wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
  518. X        return p;
  519. X    }
  520. X    for (x = 0; ucb_cmds[x].command; x++)
  521. X    if (!strcmp(cmd, ucb_cmds[x].command)) {
  522. X        wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
  523. X        return p;
  524. X    }
  525. X    return p;
  526. X}
  527. X
  528. Xstatic int nonobang;
  529. X
  530. X/* expand history references and separate message lists from other tokens */
  531. Xchar *
  532. Xhist_expand(str, argv, hist_was_referenced)
  533. Xregister char *str, **argv;
  534. Xregister int *hist_was_referenced;
  535. X{
  536. X    static char   buf[BUFSIZ];
  537. X    register int  b = 0, inquotes = 0;
  538. X    int       first_space = 0, ignore_bang;
  539. X
  540. X    ignore_bang = (ison(glob_flags, IGN_BANG) ||
  541. X           do_set(set_options, "ignore_bang"));
  542. X    nonobang = !!do_set(set_options, "nonobang");
  543. X
  544. X    if (hist_was_referenced)
  545. X    *hist_was_referenced = 0;
  546. X    while (*str) {
  547. X    while (!inquotes && isspace(*str))
  548. X        str++;
  549. X    do  {
  550. X        if (!*str)
  551. X        break;
  552. X        if (b >= sizeof(buf)-1) {
  553. X        print("argument list too long.\n");
  554. X        return NULL;
  555. X        }
  556. X        if ((buf[b] = *str++) == '\'') {
  557. X        /* make sure there's a match! */
  558. X        inquotes = !inquotes;
  559. X        }
  560. X        if (!first_space && !inquotes && index("0123456789{}*$^.", buf[b])
  561. X                 && b && !index("0123456789{}-^. \t", buf[b-1])) {
  562. X        buf[b+1] = buf[b];
  563. X        buf[b++] = ' ';
  564. X        while ((buf[++b] = *str++) && index("0123456789-,${}.", buf[b]))
  565. X            ;
  566. X        if (!buf[b])
  567. X            str--;
  568. X        first_space++;
  569. X        }
  570. X        /* check for (;) (|) or any other delimiter and separate it from
  571. X         * other tokens.
  572. X         */
  573. X        if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b]) &&
  574. X            (b < 0 || buf[b-1] != '\\')) {
  575. X        if (!isspace(buf[b]))
  576. X            first_space = -1; /* resume msg-list separation */
  577. X        if (b && !isspace(buf[b-1]))
  578. X            buf[b+1] = buf[b], buf[b++] = ' ';
  579. X        b++;
  580. X        break;
  581. X        }
  582. X        /*
  583. X         * If double-quotes, just copy byte by byte, char by char,
  584. X         *  but do remove backslashes from in front of !s
  585. X         */
  586. X        if (!inquotes && buf[b] == '"') {
  587. X        int B = b;
  588. X        while ((buf[++B] = *str++) && buf[B] != '"')
  589. X            if (*str == '!' && buf[B] == '\\')
  590. X            buf[B] = '!', str++;
  591. X        if (buf[B])
  592. X            b = B;
  593. X        else
  594. X            str--;
  595. X        b++;
  596. X        continue;
  597. X        }
  598. X        if (buf[b] == '\\') {
  599. X        first_space = 1;    /* don't split escaped words */
  600. X        if ((buf[++b] = *str) == '!')
  601. X            buf[--b] = '!';
  602. X        ++str;
  603. X        } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
  604. X               && !ignore_bang) {
  605. X        char word[BUFSIZ], *s;
  606. X        if (!(s = reference_hist(str, word, argv))) {
  607. X            if (!nonobang)
  608. X            return NULL;
  609. X        } else {
  610. X            str = s;
  611. X            if (hist_was_referenced)
  612. X            *hist_was_referenced = 1;
  613. X            if (strlen(word) + b >= sizeof buf) {
  614. X            print("argument list too long.\n");
  615. X            return NULL;
  616. X            }
  617. X            b += Strcpy(&buf[b], word) - 1;
  618. X        }
  619. X        }
  620. X        b++;
  621. X    } while (*str && (!isdelimeter(*str) || str[-1] == '\\'));
  622. X    if (!inquotes)
  623. X        first_space++, buf[b++] = ' ';
  624. X    }
  625. X    buf[b] = 0;
  626. X    return buf;
  627. X}
  628. X
  629. X/*
  630. X * expand references to internal variables.  This allows such things
  631. X * as $iscurses, $hdrs_only, etc. to work correctly.
  632. X */
  633. Xchar *
  634. Xcheck_internal(str)
  635. Xregister char *str;
  636. X{
  637. X    int ret_val = -1;
  638. X    static char version[80], get_status[4];
  639. X
  640. X    if (!strcmp(str, "iscurses"))
  641. X    ret_val = (iscurses || ison(glob_flags, PRE_CURSES));
  642. X    else if (!strcmp(str, "istool"))
  643. X    ret_val = istool;
  644. X    else if (!strcmp(str, "hdrs_only"))
  645. X    ret_val = (hdrs_only && *hdrs_only);
  646. X    else if (!strcmp(str, "is_shell"))
  647. X    ret_val = is_shell;
  648. X    else if (!strcmp(str, "is_sending"))
  649. X    ret_val = (ison(glob_flags, IS_SENDING) != 0);
  650. X    else if (!strcmp(str, "redirect"))
  651. X    ret_val = (isatty(0) != 0);
  652. X    else if (!strcmp(str, "thisfolder"))
  653. X    return (mailfile && *mailfile) ? mailfile : NULL;
  654. X    else if (!strcmp(str, "status"))
  655. X    return sprintf(get_status, "%d", last_status);
  656. X    else if (!strcmp(str, "output"))
  657. X    return last_output;
  658. X    else if (!strcmp(str, "version")) {
  659. X    /* Create the version string ONCE, then re-use it. */
  660. X    if (!*version)
  661. X        (void) sprintf(version, "%s (%d.%s.%d %s)",
  662. X              MUSHNAME, RELEASE, REVISION, PATCHLEVEL, RELEASE_DATE);
  663. X    return version;
  664. X    }
  665. X
  666. X    return ret_val > 0 ? "1" : ret_val == 0? "0" : NULL;
  667. X}
  668. X
  669. X/*
  670. X * Structure for expansions
  671. X * (move to a header file?)
  672. X */
  673. Xstruct expand {
  674. X    char *orig;        /* string beginning with substring to be expanded */
  675. X    char *exp;        /* result of expansion of substring */
  676. X    char *rest;        /* rest of the original string beyond substring */
  677. X};
  678. X
  679. X/*
  680. X * Parse and expand a single variable reference.  Variable references
  681. X * begin with a '$' and thereafter look like any of:
  682. X *    $    $$ is the pid of the current process
  683. X *    [%x]    $[%x] expands %x as a hdr_format character ($%x is same)
  684. X *    (%x)    $(%x) expands %x as a prompt format character
  685. X *    name    Value of variable "name" (error if not set)
  686. X *    v:x    Modified expansion; v is any of above, x is any of
  687. X *            h    head of a file pathname
  688. X *            t    tail of a file pathname
  689. X *            l    value converted to lowercase
  690. X *            u    value converted to uppercase
  691. X *             q    quote against further expansion (not yet)
  692. X *              <num>    select the <num>th space-separated field
  693. X *    ?name    Set/unset truth value of "name"
  694. X *    {v}    Separate v (any of above) from surrounding text
  695. X * A variable name may include alphabetics, numbers, or underscores but
  696. X * must begin with an alphabetic or underscore.
  697. X */
  698. Xvarexp(ref)
  699. Xstruct expand *ref;
  700. X{
  701. X    char *str = ref->orig, c, *p, *var, *end = NULL, *op = NULL;
  702. X    int do_bool, do_fmt = 0, expanded = 0;
  703. X
  704. X    if (*str == '$') {
  705. X    /* Allow a $ all by itself to stand */
  706. X    if (!*++str || isspace(*str)) {
  707. X        ref->exp = savestr("$");
  708. X        ref->rest = str;
  709. X        return 1;
  710. X    }
  711. X    /* Handle $?{name} for backwards compatibility */
  712. X    if (do_bool = (*str == '?'))
  713. X        str++;
  714. X    if (*str == '{')
  715. X        if (p = index(str + 1, '}')) {
  716. X        var = str + 1;
  717. X        end = p;
  718. X        } else
  719. X        goto bad_var;
  720. X    else
  721. X        var = str;
  722. X    /* Handle $?name and ${?name} (normal cases) */
  723. X    if (*var == '?') {
  724. X        if (do_bool) /* backwards compatibility clash */
  725. X        goto bad_var;
  726. X        ++var, do_bool = 1;
  727. X    }
  728. X    switch (*var) {
  729. X        case '$':
  730. X        if (str[0] == '{' && str[2] != '}')
  731. X            goto bad_var;
  732. X        else {
  733. X            char buf[16];
  734. X            (void) sprintf(buf, "%d", getpid());
  735. X            ref->exp = savestr(buf);
  736. X            ref->rest = (end ? end : var) + 1;
  737. X            return 1;
  738. X        }
  739. X        when '%':
  740. X        for (p = var + 1; *p && !index(" \t\n;|\"'$", *p); p++)
  741. X            if (*p == ':') {
  742. X            if (!do_bool && !op) {
  743. X                op = p;
  744. X                do_fmt = p - var;
  745. X            } else
  746. X                break;
  747. X            }
  748. X        if (!do_fmt)
  749. X            do_fmt = p - var;
  750. X        end = p;
  751. X        when '[': case '(':  /*)*/
  752. X        p = any(var, *var == '(' ? ") \t\n" : "] \t\n");
  753. X        if (!p || isspace(*p))
  754. X            goto bad_var;
  755. X        if (end && p > end)
  756. X            goto bad_var;
  757. X        else {
  758. X            var++;
  759. X            do_fmt = p - var;
  760. X            if (*++p == ':')
  761. X            op = p;
  762. X            else
  763. X            end = p;
  764. X        }
  765. X        /* fall through */
  766. X        default:
  767. X        if (!do_fmt && !isalpha(*var) && *var != '_')
  768. X            goto bad_var;
  769. X        if (!end)
  770. X            end = var + strlen(var);
  771. X        for (p = (op ? op : var + do_fmt) + 1; p < end; p++)
  772. X            if (!do_bool && !op && *p == ':') {
  773. X            op = p;
  774. X            } else if (!isalnum(*p) && *p != '_') {
  775. X            if (*str == '{') /*}*/
  776. X                goto bad_var;
  777. X            end = p;
  778. X            break;
  779. X            }
  780. X        if (op && op > end)
  781. X            op = NULL;
  782. X    }
  783. X    /* replace the end of "var" (end) with a nul,
  784. X     * and save char in `c'.  Similarly chop at op.
  785. X     */
  786. X    c = *end, *end = 0;
  787. X    if (op)
  788. X        *op++ = 0;
  789. X
  790. X    if (!do_fmt && debug > 3)
  791. X        printf("expanding (%s) ", var);
  792. X
  793. X    /* get the value of the variable. */
  794. X    if (do_fmt) {
  795. X        char c1 = var[do_fmt];
  796. X        var[do_fmt] = 0;
  797. X        if (debug > 3)
  798. X        printf("expanding (%s) ", var);
  799. X        if (/*(*/ ')' == c1)
  800. X        p = format_prompt(current_msg, var);
  801. X        else
  802. X        p = format_hdr(current_msg, var, FALSE) + 9;
  803. X        var[do_fmt] = c1;
  804. X    } else if (!(p = check_internal(var)))
  805. X        p = do_set(set_options, var);
  806. X    if (do_bool) {
  807. X        ref->exp = savestr((p && (*p || !do_fmt)) ? "1" : "0");
  808. X        expanded = 1;
  809. X        if (debug > 3)
  810. X        printf("--> (%s)\n", p);
  811. X    } else if (p) {
  812. X        if (debug > 3)
  813. X        printf("--> (%s)", p);
  814. X        if (op && isdigit(*op)) {
  815. X        int varc, ix = atoi(op) - 1;
  816. X        char **varv = mk_argv(p, &varc, FALSE);
  817. X        /* Ignore non-fatal errors like unmatched quotes */
  818. X        if (varv && varc < 0)
  819. X            for (varc = 0; varv[varc]; varc++)
  820. X            ;
  821. X        if (ix < 0 || varc <= ix || !varv)
  822. X            ref->exp = savestr("");
  823. X        else
  824. X            ref->exp = savestr(varv[ix]);
  825. X        expanded = 1;
  826. X        free_vec(varv);
  827. X        } else if (op) {
  828. X        char *p2 = rindex(p, '/');
  829. X        expanded = (*op == 'h' || *op == 't');
  830. X        if (*op == 't' && p2)
  831. X            p = p2 + 1;
  832. X        else if (*op == 'h' && p2)
  833. X            *p2 = 0;
  834. X        ref->exp = savestr(p);
  835. X        if (*op == 'h' && p2)
  836. X            *p2 = '/';
  837. X        else if (*op == 'l' || *op == 'u') {
  838. X            expanded = 1;
  839. X            for (p = ref->exp; *p; p++)
  840. X            if (*op == 'u')
  841. X                Upper(*p);
  842. X            else
  843. X                Lower(*p);
  844. X        }
  845. X        if (!expanded) {
  846. X            print("Unknown colon modifier :%c.\n", *op);
  847. X            xfree(ref->exp);
  848. X        } else
  849. X            if (debug > 3)
  850. X            printf("--> (%s)\n", p);
  851. X        } else {
  852. X        ref->exp = savestr(p);
  853. X        expanded = 1;
  854. X        if (debug > 3)
  855. X            printf("\n");
  856. X        }
  857. X    } else {
  858. X        print("%s: undefined variable\n", var);
  859. X        expanded = 0;
  860. X    }
  861. X    *end = c; /* replace the null with the old character */
  862. X    if (op)
  863. X        *--op = ':'; /* Put back the colon */
  864. X    ref->rest = end + (*str == '{'); /* } */
  865. X    }
  866. X    return expanded;
  867. Xbad_var:
  868. X    print("Illegal variable name.\n");
  869. X    return 0;
  870. X}
  871. X
  872. X/*
  873. X * find mush variable references and expand them to their values.
  874. X * variables are preceded by a '$' and cannot be within single
  875. X * quotes.  Only if expansion has been made do we copy buf back into str.
  876. X * We expand only as far as the first unprotected `;' separator in str,
  877. X * to get the right behavior when multiple commands are on one line.
  878. X * RETURN 0 on failure, 1 on success.
  879. X */
  880. Xvariable_expand(str)
  881. Xregister char *str;
  882. X{
  883. X    register int     b = 0, inquotes = 0;
  884. X    char             buf[BUFSIZ], *start = str;
  885. X    int             expanded = 0;
  886. X
  887. X    while (*str && b < sizeof buf - 1) {
  888. X    if (*str == '~' && (str == start || isspace(*(str-1)))) {
  889. X        register char *p = any(str, " \t"), *tmp;
  890. X        int x = 1;
  891. X        if (p)
  892. X        *p = 0;
  893. X        tmp = getpath(str, &x);
  894. X        /* if error, print message and return 0 */
  895. X        if (x == -1) {
  896. X        wprint("%s: %s\n", str, tmp);
  897. X        return 0;
  898. X        }
  899. X        b += Strcpy(buf+b, tmp);
  900. X        if (p)
  901. X        *p = ' ', str = p;
  902. X        else
  903. X        str += strlen(str);
  904. X        expanded = 1;
  905. X    }
  906. X    /* if single-quotes, just copy byte by byte, char by char ... */
  907. X    if ((buf[b] = *str++) == '\'' && !inquotes) {
  908. X        while ((buf[++b] = *str++) && buf[b] != '\'')
  909. X        ;
  910. X        if (!buf[b])
  911. X        str--;
  912. X    } else if (!inquotes && buf[b] == '\\' && *str) {
  913. X        buf[++b] = *str++;
  914. X        b++;
  915. X        continue;
  916. X    } else if (buf[b] == '"')
  917. X        inquotes = !inquotes;
  918. X    /* If $ is eol, continue.  Variables must start with a `$'
  919. X     * and continue with {, _, a-z, A-Z or it is not a variable.      }
  920. X     */
  921. X    if (buf[b] == '$' && *str) {
  922. X        struct expand expansion;
  923. X        expansion.orig = str - 1;
  924. X        if (varexp(&expansion)) {
  925. X        b += Strcpy(&buf[b], expansion.exp);
  926. X        xfree(expansion.exp);
  927. X        str = expansion.rest;
  928. X        expanded = 1;
  929. X        } else
  930. X        return 0;
  931. X    } else if (!inquotes && buf[b] == ';') {
  932. X        while (buf[++b] = *str++)
  933. X        ;
  934. X        b++;
  935. X        break;
  936. X    } else
  937. X        b++;
  938. X    }
  939. X    buf[b] = 0;
  940. X    if (expanded) /* if any expansions were done, copy back into orig buf */
  941. X    (void) strcpy(start, buf);
  942. X    if (debug > 3)
  943. X    printf("expanded to: %s\n", start);
  944. X    return 1;
  945. X}
  946. X
  947. X/* make an argv of space delimited character strings out of string "str".
  948. X * place in "argc" the number of args made.  If final is true, then expand
  949. X * variables and file names and remove quotes and backslants according to
  950. X * standard.
  951. X */
  952. Xchar **
  953. Xmk_argv(str, argc, final)
  954. Xregister char *str;
  955. Xint *argc;
  956. X{
  957. X    register char    *s = NULL, *p;
  958. X    register int    tmp, err = 0, unq_sep = 0;
  959. X    char        *newargv[MAXARGS], **argv, *p2, c, buf[BUFSIZ];
  960. X
  961. X    if (debug > 3)
  962. X    (void) printf("Working on: %s\n",str);
  963. X    /* If final is true, do variable expansions first */
  964. X    if (final) {
  965. X    (void) strcpy(buf, str);
  966. X    str = buf;
  967. X    if (!variable_expand(str))
  968. X        return DUBL_NULL;
  969. X    }
  970. X    *argc = 0;
  971. X    while (*str && *argc < MAXARGS) {
  972. X    while (isspace(*str))
  973. X        ++str;
  974. X    /* When we have hit an unquoted `;', final must be true,
  975. X     * so we're finished.  Stuff the rest of the string at the
  976. X     * end of the argv -- do_command will pass it back later,
  977. X     * for further processing -- and break out of the loop.
  978. X     * NOTE: *s is not yet valid the first time through this
  979. X     * loop, so unq_sep should always be initialized to 0.
  980. X     */
  981. X    if (unq_sep && s && *s == ';') {
  982. X        if (*str) { /* Don't bother saving a null string */
  983. X        newargv[*argc] = savestr(str);
  984. X        (*argc)++;
  985. X        }
  986. X        break;
  987. X    }
  988. X    if (*str) {        /* found beginning of a word */
  989. X        unq_sep = 0;    /* innocent until proven guilty */
  990. X        s = p = str;
  991. X        do  {
  992. X        if (p - s >= sizeof buf-1) {
  993. X            print("argument list too long.\n");
  994. X            return DUBL_NULL;
  995. X        }
  996. X        if (*str == ';' || *str == '|')
  997. X            unq_sep = final; /* Mark an unquoted separator */
  998. X        if ((*p = *str++) == '\\') {
  999. X            if (final && (*str == ';' || *str == '|'))
  1000. X            --p; /* Back up to overwrite the backslash */
  1001. X            if (*++p = *str) /* assign and compare to NUL */
  1002. X            str++;
  1003. X            continue;
  1004. X        }
  1005. X        if (p2 = index("\"'", *p)) {
  1006. X            register char c2 = *p2;
  1007. X            /* you can't escape quotes inside quotes of the same type */
  1008. X            if (!(p2 = index(str, c2))) {
  1009. X            if (final)
  1010. X                print("Unmatched %c.\n", c2);
  1011. X            err++;
  1012. X            p2 = str;
  1013. X            }
  1014. X            tmp = (int)(p2 - str) + 1; /* take up to & include quote */
  1015. X            (void) strncpy(p + !final, str, tmp);
  1016. X            p += tmp - 2 * !!final; /* change final to a boolean */
  1017. X            if (*(str = p2))
  1018. X            str++;
  1019. X        }
  1020. X        } while (++p, *str && (!isdelimeter(*str) || str[-1] == '\\'));
  1021. X        if (c = *str) /* set c = *str, check for null */
  1022. X        str++;
  1023. X        *p = 0;
  1024. X        if (*s) {
  1025. X        /* To differentiate real separators from quoted or
  1026. X         * escaped ones, always store 3 chars:
  1027. X         *  1) The separator character
  1028. X         *  2) A nul (string terminator)
  1029. X         *  3) An additional boolean (0 or 1)
  1030. X         * The boolean is checked by do_command.  Note that this
  1031. X         * applies only to "solitary" separators, i.e. those not
  1032. X         * part of a larger word.
  1033. X         */
  1034. X        if (final && (!strcmp(s, ";") || !strcmp(s, "|"))) {
  1035. X            char *sep = savestr("xx"); /* get 3 char slots */
  1036. X            sep[0] = *s, sep[1] = '\0', sep[2] = unq_sep;
  1037. X            newargv[*argc] = sep;
  1038. X        } else
  1039. X            newargv[*argc] = savestr(s);
  1040. X        (*argc)++;
  1041. X        }
  1042. X        *p = c;
  1043. X    }
  1044. X    }
  1045. X    if (!*argc)
  1046. X    return DUBL_NULL;
  1047. X    /* newargv[*argc] = NULL; */
  1048. X    if (!(argv = (char **)calloc((unsigned)((*argc)+1), sizeof(char *)))) {
  1049. X    perror("mk_argv: calloc");
  1050. X    return DUBL_NULL;
  1051. X    }
  1052. X    for (tmp = 0; tmp < *argc; tmp++)
  1053. X    argv[tmp] = newargv[tmp];
  1054. X    if (err)
  1055. X    *argc = -1;
  1056. X    else if (debug > 3)
  1057. X    (void) printf("Made argv: "), print_argv(argv);
  1058. X    return argv;
  1059. X}
  1060. X
  1061. X/*
  1062. X * Report a history parsing error.
  1063. X * Suppress the message if nonobang is true.
  1064. X */
  1065. X#define hist_error    if (nonobang) {;} else print
  1066. X
  1067. X/*
  1068. X * reference previous history from syntax of str and place result into buf
  1069. X * We know we've got a history reference -- we're passed the string starting
  1070. X * the first char AFTER the '!' (which indicates history reference)
  1071. X */
  1072. Xchar *
  1073. Xreference_hist(str, buf, hist_ref)
  1074. Xregister char *str, **hist_ref;
  1075. Xchar buf[];
  1076. X{
  1077. X    int        relative; /* relative from current hist_no */
  1078. X    int        old_hist, argstart = 0, lastarg, argend = 0, n = 0;
  1079. X    register char  *p, *rb = NULL, **argv = hist_ref;
  1080. X    struct history *hist;
  1081. X
  1082. X    buf[0] = 0;
  1083. X    if (*str == '{')
  1084. X    if (!(rb = index(str, '}'))) {   /* { */
  1085. X        hist_error("Unmatched '}'");
  1086. X        return NULL;
  1087. X    } else
  1088. X        *rb = 0, ++str;
  1089. X    relative = *str == '-';
  1090. X    if (index("!:$*", *str)) {
  1091. X    old_hist = hist_no;
  1092. X    if (*str == '!')
  1093. X        str++;
  1094. X    } else if (isdigit(*(str + relative)))
  1095. X    str = my_atoi(str + relative, &old_hist);
  1096. X    else if (!(p = hist_from_str(str, &old_hist))) {
  1097. X    if (rb) /* { */
  1098. X        *rb = '}';
  1099. X    return NULL;
  1100. X    } else
  1101. X    str = p;
  1102. X    if (relative)
  1103. X    old_hist = (hist_no - old_hist) + 1;
  1104. X    if (old_hist == hist_no) {
  1105. X    if (!(argv = hist_ref))
  1106. X        hist_error("You haven't done anything yet!\n");
  1107. X    } else {
  1108. X    if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
  1109. X        old_hist <= 0) {
  1110. X        if (old_hist <= 0)
  1111. X        hist_error("You haven't done that many commands, yet.\n");
  1112. X        else
  1113. X        hist_error("Event %d %s.\n", old_hist,
  1114. X            (old_hist > hist_no)? "hasn't happened yet": "expired");
  1115. X        if (rb) /* { */
  1116. X        *rb = '}';
  1117. X        return NULL;
  1118. X    }
  1119. X    hist = hist_head;
  1120. X    while (hist && hist->histno != old_hist)
  1121. X        hist = hist->prev;
  1122. X    if (hist)
  1123. X        argv = hist->argv;
  1124. X    }
  1125. X    if (!argv) {
  1126. X    if (rb) /* { */
  1127. X        *rb = '}';
  1128. X    return NULL;
  1129. X    }
  1130. X    while (argv[argend+1])
  1131. X    argend++;
  1132. X    lastarg = argend;
  1133. X    if (*str && index(":$*-", *str)) {
  1134. X    int isrange;
  1135. X    if (*str == ':' && isdigit(*++str))
  1136. X        str = my_atoi(str, &argstart);
  1137. X    if (isrange = (*str == '-'))
  1138. X        str++;
  1139. X    if (!isdigit(*str)) {
  1140. X        if (*str == 'p')
  1141. X        str++, print_only = 1;
  1142. X        else if (*str == '*') {
  1143. X        str++;
  1144. X        if (!isrange) {
  1145. X            if (argv[0]) {
  1146. X            if (argv[1])
  1147. X                argstart = 1;
  1148. X            else {
  1149. X                if (rb) /* { */
  1150. X                *rb = '}';
  1151. X                return (rb ? rb + 1 : str);
  1152. X            }
  1153. X            } else
  1154. X            argstart = 0;
  1155. X        }
  1156. X        } else if (*str == '$') {
  1157. X        if (!isrange)
  1158. X            argstart = argend;
  1159. X        str++;
  1160. X        } else if (isrange && argend > argstart)
  1161. X        argend--; /* unspecified end of range implies last-1 arg */
  1162. X        else 
  1163. X        argend = argstart; /* no range specified; use arg given */
  1164. X    } else
  1165. X        str = my_atoi(str, &argend);
  1166. X    }
  1167. X    if (argstart > lastarg || argend > lastarg || argstart > argend) {
  1168. X    hist_error("Bad argument selector.\n");
  1169. X    if (rb) /* { */
  1170. X        *rb = '}';
  1171. X    return (nonobang ? rb ? rb + 1 : str : NULL);
  1172. X    }
  1173. X    if (debug > 3)
  1174. X    print("history expanding from "), print_argv(argv);
  1175. X    while (argstart <= argend) {
  1176. X    n += Strcpy(&buf[n], argv[argstart++]);
  1177. X    buf[n++] = ' ';
  1178. X    }
  1179. X    buf[--n] = 0;
  1180. X    if (rb) /* { */
  1181. X    *rb = '}';
  1182. X    return (rb ? rb + 1 : str);
  1183. X}
  1184. X
  1185. X/* find a history command that contains the string "str"
  1186. X * place that history number in "hist" and return the end of the string
  1187. X * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
  1188. X * in the second example, return the pointer to "bar"
  1189. X */
  1190. Xchar *
  1191. Xhist_from_str(str, hist_number)
  1192. Xregister char *str;
  1193. Xregister int *hist_number;
  1194. X{
  1195. X    register char *p = NULL, c = 0;
  1196. X    int       full_search = 0, len, found;
  1197. X    char       buf[BUFSIZ];
  1198. X    struct history *hist;
  1199. X#ifndef REGCMP
  1200. X    extern char   *re_comp();
  1201. X#else
  1202. X    extern char   *regcmp();
  1203. X#endif /* REGCMP */
  1204. X
  1205. X    /* For !{something}, the {} are stripped in reference_hist() */
  1206. X    if (*str == '?') {
  1207. X    if (p = index(++str, '?'))
  1208. X        c = *p, *p = 0;
  1209. X    else
  1210. X        p = str + strlen(str);
  1211. X    full_search = 1;
  1212. X    } else {
  1213. X    p = str;
  1214. X    while (*p && *p != ':' && !isspace(*p))
  1215. X        p++;
  1216. X    c = *p, *p = 0;
  1217. X    }
  1218. X    if (*str) {
  1219. X#ifndef REGCMP
  1220. X    if (re_comp(str))
  1221. X#else
  1222. X    if (!regcmp(str, NULL))
  1223. X#endif /* REGCMP */
  1224. X    {
  1225. X        if (c)
  1226. X        *p = c;
  1227. X        return NULL;
  1228. X    }
  1229. X    } else {
  1230. X    *hist_number = hist_no;
  1231. X    if (c)
  1232. X        *p = c;
  1233. X    return (*p == '?' ? p + 1 : p);
  1234. X    }
  1235. X    len = strlen(str);
  1236. X    /* move thru the history in reverse searching for a string match. */
  1237. X    for (hist = hist_head; hist; hist = hist->prev) {
  1238. X    if (full_search) {
  1239. X        (void) argv_to_string(buf, hist->argv);
  1240. X        Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
  1241. X    }
  1242. X    if (!full_search) {
  1243. X        (void) strcpy(buf, hist->argv[0]);
  1244. X        Debug("Checking for (%s) in (#%d: %*s)\n",
  1245. X        str, hist->histno, len, buf);
  1246. X        found = !strncmp(buf, str, len);
  1247. X    } else
  1248. X        found =
  1249. X#ifndef REGCMP
  1250. X        re_exec(buf)
  1251. X#else
  1252. X        !!regex(str, buf, NULL) /* convert to boolean value */
  1253. X#endif /* REGCMP */
  1254. X                == 1;
  1255. X    if (found) {
  1256. X        *hist_number = hist->histno;
  1257. X        Debug("Found it in history #%d\n", *hist_number);
  1258. X        *p = c;
  1259. X        return (*p == '?' ? p + 1 : p);
  1260. X    }
  1261. X    }
  1262. X    hist_error("%s: event not found\n", str);
  1263. X    *p = c;
  1264. X    return NULL;
  1265. X}
  1266. X
  1267. Xdisp_hist(n, argv)  /* argc not used -- use space for the variable, "n" */
  1268. Xregister int n;
  1269. Xchar **argv;
  1270. X{
  1271. X    register int    list_num = TRUE, num_of_hists = hist_size;
  1272. X    register int    reverse = FALSE;
  1273. X    struct history    *hist = hist_tail;
  1274. X
  1275. X    while (*++argv && *argv[0] == '-') {
  1276. X    n = 1;
  1277. X    do  switch(argv[0][n]) {
  1278. X        case 'h': list_num = FALSE;
  1279. X        when 'r': reverse = TRUE; hist = hist_head;
  1280. X        otherwise: return help(0, "history", cmd_help);
  1281. X        }
  1282. X    while (argv[0][++n]);
  1283. X    }
  1284. X
  1285. X    if (!hist) {
  1286. X    print("No history yet.\n");
  1287. X    return -1;
  1288. X    }
  1289. X    if (*argv)
  1290. X    if (!isdigit(**argv)) {
  1291. X        print("history: badly formed number\n");
  1292. X        return -1;
  1293. X    } else
  1294. X        num_of_hists = atoi(*argv);
  1295. X
  1296. X    if (num_of_hists > hist_size || num_of_hists > hist_no)
  1297. X    num_of_hists = min(hist_size, hist_no);
  1298. X
  1299. X    if (!reverse)
  1300. X    while (hist_no - hist->histno > num_of_hists) {
  1301. X        (void) printf("skipping %d\n", hist->histno);
  1302. X        hist = hist->next;
  1303. X    }
  1304. X
  1305. X    (void) do_pager(NULL, TRUE);
  1306. X    for (n = 0; n < num_of_hists && hist; n++) {
  1307. X    char buf[256];
  1308. X    if (list_num)
  1309. X        (void) do_pager(sprintf(buf, "%4.d  ", hist->histno), FALSE);
  1310. X    (void) argv_to_string(buf, hist->argv);
  1311. X    (void) do_pager(buf, FALSE);
  1312. X    if (do_pager("\n", FALSE) == -1)
  1313. X        break;
  1314. X    hist = (reverse)? hist->prev : hist->next;
  1315. X    }
  1316. X    (void) do_pager(NULL, FALSE);
  1317. X    return 0;
  1318. X}
  1319. X
  1320. Xinit_history(newsize)
  1321. X{
  1322. X    if ((hist_size = newsize) < 1)
  1323. X    hist_size = 1;
  1324. X}
  1325. END_OF_FILE
  1326. if test 34930 -ne `wc -c <'mush/loop.c'`; then
  1327.     echo shar: \"'mush/loop.c'\" unpacked with wrong size!
  1328. fi
  1329. # end of 'mush/loop.c'
  1330. fi
  1331. if test -f 'mush/pick.c' -a "${1}" != "-c" ; then 
  1332.   echo shar: Will not clobber existing file \"'mush/pick.c'\"
  1333. else
  1334. echo shar: Extracting \"'mush/pick.c'\" \(16964 characters\)
  1335. sed "s/^X//" >'mush/pick.c' <<'END_OF_FILE'
  1336. X/* @(#)pick.c    2.4    (c) copyright 10/18/86 (Dan Heller) */
  1337. X
  1338. X#include "mush.h"
  1339. X
  1340. Xstatic int before, after, search_from, search_subj, search_to, xflg, icase;
  1341. Xstatic char search_hdr[64];
  1342. Xstatic int mdy[3];
  1343. Xstatic int pick();
  1344. Xstatic void month_day_year();
  1345. X
  1346. Xdo_pick(n, argv, list)
  1347. Xregister int n;
  1348. Xregister char **argv, list[];
  1349. X{
  1350. X    char ret_list[MAXMSGS_BITS];
  1351. X
  1352. X    if (n > 1 && !strcmp(argv[1], "-?"))
  1353. X    return help(0, "pick", cmd_help);
  1354. X
  1355. X    clear_msg_list(ret_list);
  1356. X    /* if is_pipe, then the messages to search for are already set.
  1357. X     * if not piped, then reverse the bits for all message numbers.
  1358. X     * That is, search EACH message. only those matching will be returned.
  1359. X     */
  1360. X    if (isoff(glob_flags, IS_PIPE))
  1361. X    bitput(ret_list, list, msg_cnt, =~); /* macro, turn on all bits */
  1362. X    /* Use n temporarily as verbosity flag */
  1363. X    n = (!chk_option("quiet", "pick") &&
  1364. X        isoff(glob_flags, DO_PIPE));
  1365. X    if ((n = pick(argv, list, ret_list, n)) == -1)
  1366. X    return -1;
  1367. X    if (istool && isoff(glob_flags, DO_PIPE))
  1368. X    print("%d matches:\n", n);
  1369. X    for (n = 0; n < msg_cnt; n++)
  1370. X    if (msg_bit(ret_list, n)) {
  1371. X        if (isoff(glob_flags, DO_PIPE))
  1372. X        if (istool)
  1373. X            print_more("%d ", n+1);
  1374. X        else
  1375. X            print("%s\n", compose_hdr(n));
  1376. X        set_msg_bit(list, n);
  1377. X    } else
  1378. X        unset_msg_bit(list, n);
  1379. X    return 0;
  1380. X}
  1381. X
  1382. X/*
  1383. X * search for messages.  Return the number of matches.  Errors such
  1384. X * as internal errors or syntax errors, return -1.
  1385. X * "head" and "tail" are specified using +<num> or -<num> as args.
  1386. X * Both can be specified and the order is significant.
  1387. X *    pick +5 -3
  1388. X * returns the last three of the first five matches.
  1389. X *    pick -3 +2
  1390. X * returns the first two of the last three matches.
  1391. X */
  1392. Xstatic int
  1393. Xpick(argv, list, ret_list, verbose)
  1394. Xregister char **argv, list[], ret_list[];
  1395. X{
  1396. X    register char c;
  1397. X    int matches = 0;
  1398. X    char pattern[256];
  1399. X    short head_first, head_cnt, tail_cnt, search = TRUE;
  1400. X    int n;
  1401. X
  1402. X    if (!msg_cnt) {
  1403. X    print("No Messages.\n");
  1404. X    return -1;
  1405. X    }
  1406. X
  1407. X    head_first = TRUE;
  1408. X    head_cnt = tail_cnt = -1;
  1409. X    icase = before = after = search_from = search_subj = search_to = xflg = 0;
  1410. X    mdy[0] = mdy[1] = search_hdr[0] = 0;
  1411. X    while (*argv && *++argv && (**argv == '-' || **argv == '+'))
  1412. X    if (**argv == '+' || isdigit(argv[0][1])) {
  1413. X        if (**argv == '+')
  1414. X        head_cnt = atoi(&argv[0][1]);
  1415. X        else {
  1416. X        tail_cnt = atoi(&argv[0][1]);
  1417. X        if (head_cnt == -1)
  1418. X            head_first = FALSE;
  1419. X        }
  1420. X        if (head_cnt == 0 || tail_cnt == 0) {
  1421. X        print("pick: invalid head/tail number: %s\n", &argv[0][1]);
  1422. X        clear_msg_list(ret_list);
  1423. X        return -1;
  1424. X        }
  1425. X    } else if ((c = argv[0][1]) == 'e') {
  1426. X        if (!*++argv) {
  1427. X        print("use: -e expression...\n");
  1428. X        return -1;
  1429. X        }
  1430. X        break;
  1431. X    } else switch (c) {
  1432. X        /* users specifies a range */
  1433. X        case 'r': {
  1434. X        int X = 2;
  1435. X        /* if not a pipe, then clear all bits cuz we only want
  1436. X         * to search the message specified here...
  1437. X         * If it is a pipe, then add to the messages searched for.
  1438. X         */
  1439. X        if (isoff(glob_flags, IS_PIPE))
  1440. X            clear_msg_list(list);
  1441. X        /*  "-r10-15"
  1442. X         *     ^argv[1][2]  if NULL, then
  1443. X         * list detached from "r" e.g. "-r" "5-20"
  1444. X         */
  1445. X        if (!argv[0][X])
  1446. X            argv++, X = 0;
  1447. X        (*argv) += X;
  1448. X        n = get_msg_list(argv, list);
  1449. X        (*argv) -= X;
  1450. X        if (n == -1)
  1451. X            return -1;
  1452. X        argv += (n-1); /* we're going to increment another up top */
  1453. X        }
  1454. X        when 'a': {
  1455. X        if ((n = ago_date(++argv)) == -1)
  1456. X            return -1;
  1457. X        argv += n;
  1458. X        }
  1459. X        when 'd':
  1460. X        if (!*++argv) {
  1461. X            print("Specify a date for -%c\n", c);
  1462. X            return -1;
  1463. X        }
  1464. X        if (!date1(*argv))
  1465. X            return -1;
  1466. X        when 's' : case 'f': case 't': case 'h':
  1467. X        if (search_subj + search_from + search_to + *search_hdr > 1) {
  1468. X            print("Specify one of `s', `f', `t' or `h' only\n");
  1469. X            return -1;
  1470. X            }
  1471. X            if (c == 's')
  1472. X            search_subj = 1;
  1473. X        else if (c == 'f')
  1474. X            search_from = 1;
  1475. X        else if (c == 'h')
  1476. X            if (!*++argv)
  1477. X            print("Specify header to search for.\n");
  1478. X            else
  1479. X            (void) lcase_strcpy(search_hdr, *argv);
  1480. X        else
  1481. X            search_to = 1;
  1482. X        when 'x' : xflg = 1;
  1483. X        when 'i' : icase = 1;
  1484. X        otherwise:
  1485. X        print("pick: unknown flag: %c\n", argv[0][1]);
  1486. X        clear_msg_list(ret_list);
  1487. X        return -1;
  1488. X    }
  1489. X    if (xflg && head_cnt + tail_cnt >= 0) {
  1490. X    print("Can't specify -x and head/tail options together.\n");
  1491. X    return -1;
  1492. X    }
  1493. X    pattern[0] = 0;
  1494. X    (void) argv_to_string(pattern, argv);
  1495. X    search = (pattern[0] || head_cnt + tail_cnt < 0);
  1496. X    if (verbose) {
  1497. X    if (head_cnt + tail_cnt >= 0) {
  1498. X        print("Finding the ");
  1499. X        if (head_cnt > 0) {
  1500. X        if (head_first)
  1501. X            if (tail_cnt == -1)
  1502. X            print_more("first %d message%s",
  1503. X                head_cnt, head_cnt > 1? "s" : "");
  1504. X            else
  1505. X            print_more("last %d message%s",
  1506. X                tail_cnt, tail_cnt > 1? "s" : "");
  1507. X        else /* there must be a tail_cnt and it comes first */
  1508. X            print_more("first %d message%s",
  1509. X                head_cnt, head_cnt > 1? "s" : "");
  1510. X        } else
  1511. X        print_more("last %d message%s",
  1512. X            tail_cnt, tail_cnt > 1? "s" : "");
  1513. X        if (tail_cnt > 0 && head_cnt > 0)
  1514. X        if (head_first)
  1515. X            print_more(" of the first %d", head_cnt);
  1516. X        else
  1517. X            print_more(" of the last %d", tail_cnt);
  1518. X    } else
  1519. X        print_more("Searching for messages");
  1520. X    if (!search) {
  1521. X        if (tail_cnt > 0 && head_cnt > 0)
  1522. X        print_more(" messages");
  1523. X        if (ison(glob_flags, IS_PIPE))
  1524. X        print_more(" from the input list");
  1525. X    } else if (mdy[1] == 0) {
  1526. X        print(" that %scontain \"%s\"", (xflg)? "do not ": "",
  1527. X                (*pattern)? pattern: "<previous expression>");
  1528. X        if (search_subj)
  1529. X        print_more(" in subject line");
  1530. X        else if (search_from)
  1531. X        print_more(" from author names");
  1532. X        else if (search_to)
  1533. X        print_more(" from the To: field");
  1534. X        else if (search_hdr[0])
  1535. X        print_more(" from the message header \"%s:\"", search_hdr);
  1536. X    } else {
  1537. X        extern char *month_names[]; /* from dates.c */
  1538. X        print_more(" dated ");
  1539. X        if (before || after)
  1540. X        if (xflg)
  1541. X            print_more("%s ", (!before)? "before": "after");
  1542. X        else
  1543. X            print_more("on or %s ", (before)? "before": "after");
  1544. X        print_more("%s. %d, %d",
  1545. X              month_names[mdy[0]], mdy[1], mdy[2] + 1900);
  1546. X    }
  1547. X    print_more(".\n");
  1548. X    }
  1549. X    if (mdy[1] > 0 && icase)
  1550. X    print("using date: -i flag ignored.\n");
  1551. X    if (!search) {
  1552. X    for (n = 0; n < msg_cnt && (!head_first || matches < head_cnt); n++)
  1553. X        if (msg_bit(list, n))
  1554. X        ++matches, set_msg_bit(ret_list, n);
  1555. X    } else
  1556. X    matches = find_pattern(head_first? head_cnt : msg_cnt,
  1557. X               pattern, list, ret_list);
  1558. X    if (xflg && matches >= 0) {
  1559. X    /* invert items in ret_list that also appear in list */
  1560. X    bitput(list, ret_list, msg_cnt, ^=);
  1561. X    /* there should be a faster way to do this count ... */
  1562. X    for (matches = n = 0; n < msg_cnt; n++)
  1563. X        if (msg_bit(ret_list, n))
  1564. X        ++matches;
  1565. X    }
  1566. X    Debug("matches = %d\n", matches);
  1567. X    if (!matches)
  1568. X    return 0;
  1569. X
  1570. X    /* ok, the list we've got is a list of matched messages.  If "tailing"
  1571. X     * is set, reduce the number of matches to at least tail_cnt.
  1572. X     */
  1573. X    if (tail_cnt >= 0)
  1574. X    for (n = 0; n < msg_cnt && matches > tail_cnt; n++)
  1575. X        if (msg_bit(ret_list, n)) {
  1576. X        Debug("tail: dropping %d\n", n+1);
  1577. X        unset_msg_bit(ret_list, n);
  1578. X        matches--;
  1579. X        }
  1580. X
  1581. X    /* if tailing came before heading, we need to do the heading now. */
  1582. X    if (!head_first && head_cnt >= 0)
  1583. X    for (n = 0; n < msg_cnt; n++)
  1584. X        if (msg_bit(ret_list, n))
  1585. X        if (head_cnt > 0)
  1586. X            head_cnt--;
  1587. X        else {
  1588. X            unset_msg_bit(ret_list, n);
  1589. X            matches--;
  1590. X        }
  1591. X    return matches;
  1592. X}
  1593. X
  1594. X/*
  1595. X * find_pattern will search thru all the messages set in the check_list
  1596. X * until the list runs out or "cnt" has been exhasted.  ret_list contains
  1597. X * the list of messages which have matched the pattern.
  1598. X * return -1 for internal error or # of pattern matches.
  1599. X */
  1600. Xfind_pattern(cnt, p, check_list, ret_list)
  1601. Xint cnt;
  1602. Xregister char *p;
  1603. Xchar check_list[], ret_list[];
  1604. X{
  1605. X    register int n, val, i; /* val is return value from regex or re_exec */
  1606. X    int matches = 0;
  1607. X    long bytes = 0;
  1608. X    char buf[HDRSIZ];
  1609. X    static char *err = (char *)-1;
  1610. X#ifdef REGCMP
  1611. X    char *regcmp(), *regex();
  1612. X#else /* REGCMP */
  1613. X    char *re_comp();
  1614. X#endif /* REGCMP */
  1615. X
  1616. X    if (p && *p == '\\')
  1617. X    p++;  /* take care of escaping special cases (`-', `\') */
  1618. X
  1619. X    /* specify what we're looking for */
  1620. X    if (p && *p) {
  1621. X    if (icase)
  1622. X        p = lcase_strcpy(buf, p);
  1623. X#ifdef REGCMP
  1624. X    if (err && p)
  1625. X        xfree(err);
  1626. X    if (p && !(err = regcmp(p, NULL))) {
  1627. X        print("regcmp error: %s\n", p);
  1628. X        clear_msg_list(ret_list);
  1629. X        return -1;
  1630. X    }
  1631. X#else /* REGCMP */
  1632. X    if (err = re_comp(p)) {
  1633. X        print("re_comp error: %s\n", err);
  1634. X        clear_msg_list(ret_list);
  1635. X        return -1;
  1636. X    }
  1637. X#endif /* REGCMP */
  1638. X    } else if (err == (char *)-1 && mdy[1] <= 0) {
  1639. X    print("No previous regular expression\n");
  1640. X    clear_msg_list(ret_list);  /* doesn't matter really */
  1641. X    return -1;
  1642. X    }
  1643. X    /* start searching: set bytes, and message number: n */
  1644. X    for (n = 0; cnt && n < msg_cnt; n++)
  1645. X    if (msg_bit(check_list, n)) {
  1646. X        if (mdy[1] > 0) {
  1647. X        int msg_mdy[3];
  1648. X        if (ison(glob_flags, DATE_RECV))
  1649. X            p = msg[n].m_date_recv;
  1650. X        else
  1651. X            p = msg[n].m_date_sent;
  1652. X        /* Ick -- fix this mdy thing asap */
  1653. X        month_day_year(p, &msg_mdy[0], &msg_mdy[1], &msg_mdy[2]);
  1654. X        Debug("checking %d's date: %d-%d-%d  ",
  1655. X                 n+1, msg_mdy[0]+1, msg_mdy[1], msg_mdy[2]);
  1656. X        /* start at year and wrap around.
  1657. X         * only when match the day (4), check for == (match)
  1658. X         */
  1659. X        for (i = 2; i < 5; i++)
  1660. X            if (before && msg_mdy[i%3] < mdy[i%3]
  1661. X            ||  after  && msg_mdy[i%3] > mdy[i%3]
  1662. X            ||  i == 4 && (msg_mdy[i%3] == mdy[i%3])) {
  1663. X                Debug("matched (%s).\n",
  1664. X                (i == 2)? "year" : (i == 3)? "month" : "day");
  1665. X                set_msg_bit(ret_list, n);
  1666. X                cnt--, matches++;
  1667. X                break;
  1668. X            } else if (msg_mdy[i%3] != mdy[i%3]) {
  1669. X            Debug("failed.\n");
  1670. X            break;
  1671. X            }
  1672. X        continue;
  1673. X        }
  1674. X        /* we must have the right date -- if we're searching for a
  1675. X         * string, find it.
  1676. X         */
  1677. X        (void) msg_get(n, NULL, 0);
  1678. X        bytes = 0;
  1679. X        while (bytes < msg[n].m_size) {
  1680. X        if (!search_subj && !search_from && !search_to &&
  1681. X            !*search_hdr && !(p = fgets(buf, sizeof buf, tmpf)))
  1682. X            break;
  1683. X        else if (search_subj) {
  1684. X            if (!(p = header_field(n, "subject")))
  1685. X            break;
  1686. X        } else if (search_from) {
  1687. X            if (!(p = header_field(n, "from"))) {
  1688. X            /*
  1689. X             * Check for MSG_SEPARATOR here?  Maybe not...
  1690. X             */
  1691. X            register char *p2;
  1692. X            (void) msg_get(n, NULL, 0);
  1693. X            if (!(p2 = fgets(buf, sizeof buf, tmpf)) ||
  1694. X                !(p = index(p2, ' ')))
  1695. X                continue;
  1696. X            p++;
  1697. X            if (p2 = any(p, " \t"))
  1698. X                *p2 = 0;
  1699. X            }
  1700. X        } else if (search_to) {
  1701. X            if (!(p = header_field(n, "to")) &&
  1702. X                !(p = header_field(n, "apparently-to")))
  1703. X            break;
  1704. X        } else if (*search_hdr) {
  1705. X            if (!(p = header_field(n, search_hdr)))
  1706. X            break;
  1707. X        }
  1708. X        if (icase)
  1709. X            p = lcase_strcpy(buf, p);
  1710. X#ifdef REGCMP
  1711. X        val = !!regex(err, p, NULL); /* convert value to a boolean */
  1712. X#else /* REGCMP */
  1713. X        val = re_exec(p);
  1714. X#endif /* REGCMP */
  1715. X        if (val == -1) {   /* doesn't apply in system V */
  1716. X            print("Internal error for pattern search.\n");
  1717. X            clear_msg_list(ret_list); /* it doesn't matter, really */
  1718. X            return -1;
  1719. X        }
  1720. X        if (val) {
  1721. X            set_msg_bit(ret_list, n);
  1722. X            cnt--, matches++;
  1723. X            break;
  1724. X        }
  1725. X        if (search_subj || search_from || search_to || *search_hdr)
  1726. X            break;
  1727. X        else
  1728. X            bytes += strlen(p);
  1729. X        }
  1730. X    }
  1731. X    return matches;
  1732. X}
  1733. X
  1734. X#ifdef CURSES
  1735. X/*
  1736. X * search for a pattern in composed message headers -- also see next function
  1737. X * flags ==  0   forward search (prompt).
  1738. X * flags == -1   continue search (no prompt).
  1739. X * flags ==  1   backward search (prompt).
  1740. X */
  1741. Xsearch(flags)
  1742. Xregister int flags;
  1743. X{
  1744. X    register char   *p;
  1745. X    char           pattern[128];
  1746. X    register int    this_msg = current_msg, val = 0;
  1747. X    static char     *err = (char *)-1, direction;
  1748. X    SIGRET        (*oldint)(), (*oldquit)();
  1749. X#ifdef REGCMP
  1750. X    char *regcmp();
  1751. X#else /* REGCMP */
  1752. X    char *re_comp();
  1753. X#endif /* REGCMP */
  1754. X
  1755. X    if (msg_cnt <= 1) {
  1756. X    print("Not enough messages to invoke a search.\n");
  1757. X    return 0;
  1758. X    }
  1759. X    pattern[0] = '\0';
  1760. X    if (flags == -1)
  1761. X    print("continue %s search...", direction? "forward" : "backward");
  1762. X    else
  1763. X    print("%s search: ", flags? "backward" : "forward");
  1764. X    if (flags > -1)
  1765. X    if (Getstr(pattern, COLS-18, 0) < 0)
  1766. X        return 0;
  1767. X    else
  1768. X        direction = !flags;
  1769. X#ifdef REGCMP
  1770. X    if (err && *pattern)
  1771. X    xfree(err);
  1772. X    else if (err == (char *)-1 && !*pattern) {
  1773. X    print("No previous regular expression.");
  1774. X    return 0;
  1775. X    }
  1776. X    if (*pattern && !(err = regcmp(pattern, NULL))) {
  1777. X    print("Error in regcmp in %s", pattern);
  1778. X    return 0;
  1779. X    }
  1780. X#else /* REGCMP */
  1781. X    if (err = re_comp(pattern)) {
  1782. X    print(err);
  1783. X    return 0;
  1784. X    }
  1785. X#endif /* REGCMP */
  1786. X    move(LINES-1, 0), refresh();
  1787. X    on_intr();
  1788. X
  1789. X    do  {
  1790. X    if (direction)
  1791. X        current_msg = (current_msg+1) % msg_cnt;
  1792. X    else
  1793. X        if (--current_msg < 0)
  1794. X        current_msg = msg_cnt-1;
  1795. X    p = compose_hdr(current_msg);
  1796. X#ifdef REGCMP
  1797. X    val = !!regex(err, p, NULL); /* convert value to a boolean */
  1798. X#else /* REGCMP */
  1799. X    val = re_exec(p);
  1800. X#endif /* REGCMP */
  1801. X    if (val == -1)     /* doesn't apply in system V */
  1802. X        print("Internal error for pattern search.\n");
  1803. X    } while (!val && current_msg != this_msg && isoff(glob_flags, WAS_INTR));
  1804. X
  1805. X    if (ison(glob_flags, WAS_INTR)) {
  1806. X    print("Pattern search interrupted.");
  1807. X    current_msg = this_msg;
  1808. X    } else if (val == 0)
  1809. X    print("Pattern not found.");
  1810. X
  1811. X    off_intr();
  1812. X    return val;
  1813. X}
  1814. X#endif /* CURSES */
  1815. X
  1816. X/*
  1817. X * Get just the month, day, and year from a date.
  1818. X * This is a temporary measure until the date compares in pick()
  1819. X * can be overhauled.  It really should be in dates.c, but ...
  1820. X */
  1821. Xstatic
  1822. Xvoid
  1823. Xmonth_day_year(date, month, day, year)
  1824. Xchar *date;
  1825. Xint *month, *day, *year;
  1826. X{
  1827. X    long gmt;
  1828. X    char unused[4], zone[8];
  1829. X    struct tm *t;
  1830. X    extern long getzoff();
  1831. X
  1832. X    (void) sscanf(date, "%ld%3c%s", &gmt, unused, zone);
  1833. X    gmt += getzoff(zone);
  1834. X    t = gmtime(&gmt);
  1835. X    *month = t->tm_mon;
  1836. X    *day = t->tm_mday;
  1837. X    *year = t->tm_year;
  1838. X}
  1839. X
  1840. X/*
  1841. X * parse a user given date string and set mdy[] array with correct
  1842. X * values.  Return 0 on failure.
  1843. X */
  1844. Xdate1(p)
  1845. Xregister char *p;
  1846. X{
  1847. X    register char *p2;
  1848. X    long      t;
  1849. X    int       i;
  1850. X    struct tm       *today;
  1851. X
  1852. X    if (*p == '-' || *p == '+') {
  1853. X    before = !(after = *p == '+');
  1854. X    skipspaces(1);
  1855. X    }
  1856. X    if (!isdigit(*p) && *p != '/') {
  1857. X    print("syntax error on date: \"%s\"\n", p);
  1858. X    return 0;
  1859. X    }
  1860. X    (void) time (&t);
  1861. X    today = localtime(&t);
  1862. X    for (i = 0; i < 3; i++)
  1863. X    if (!p || !*p || *p == '/') {
  1864. X        switch(i) {   /* default to today's date */
  1865. X        case 0: mdy[0] = today->tm_mon;
  1866. X        when 1: mdy[1] = today->tm_mday;
  1867. X        when 2: mdy[2] = today->tm_year;
  1868. X        }
  1869. X        if (p && *p)
  1870. X        p++;
  1871. X    } else {
  1872. X        p2 = (*p)? index(p+1, '/') : NULL;
  1873. X        mdy[i] = atoi(p); /* atoi will stop at the '/' */
  1874. X        if (i == 0 && (--(mdy[0]) < 0 || mdy[0] > 11)) {
  1875. X        print("Invalid month: %s\n", p);
  1876. X        return 0;
  1877. X        } else if (i == 1 && (mdy[1] < 1 || mdy[1] > 31)) {
  1878. X        print("Invalid day: %s\n", p);
  1879. X        return 0;
  1880. X        }
  1881. X        if (p = p2) /* set p to p2 and check to see if it's valid */
  1882. X        p++;
  1883. X    }
  1884. X    return 1;
  1885. X}
  1886. X
  1887. X/*
  1888. X * Parse arguments specifying days/months/years "ago" (relative to today).
  1889. X * Legal syntax: -ago [+-][args]
  1890. X *    where "args" is defined to be:
  1891. X *    [0-9]+[ ]*[dD][a-Z]*[ ,]*[0-9]+[mM][a-Z]*[ ,]*[0-9]+[ ]*[yY][a-Z]*
  1892. X *    1 or more digits, 0 or more spaces, d or D followed by 0 or more chars,
  1893. X *    0 or more whitespaces or commas, repeat for months and years...
  1894. X * Examples:
  1895. X *    1 day, 2 months, 0 years
  1896. X *    2 weeks 1 year
  1897. X *    10d, 5m
  1898. X *    3w
  1899. X *    1d 1Y
  1900. X *
  1901. X * Return number of args parsed; -1 on error.
  1902. X */
  1903. Xago_date(argv)
  1904. Xchar **argv;
  1905. X{
  1906. X#define SECS_PER_DAY   (60 * 60 * 24)
  1907. X#define SECS_PER_WEEK  (SECS_PER_DAY * 7)
  1908. X#define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
  1909. X#define SECS_PER_YEAR  (SECS_PER_DAY * 365)
  1910. X    register char *p;
  1911. X    char       buf[256];
  1912. X    int           n = 0, value;
  1913. X    long       t;
  1914. X    struct tm       *today;
  1915. X
  1916. X    (void) argv_to_string(buf, argv);
  1917. X    p = buf;
  1918. X    (void) time (&t); /* get current time in seconds and subtract new values */
  1919. X    if (*p == '-')
  1920. X    before = TRUE;
  1921. X    else if (*p == '+')
  1922. X    after = TRUE;
  1923. X    skipspaces(before || after);
  1924. X    while (*p) {
  1925. X    if (!isdigit(*p)) {
  1926. X        p -= 2;
  1927. X        break; /* really a syntax error, but it could be other pick args */
  1928. X    }
  1929. X    p = my_atoi(p, &value); /* get 1 or more digits */
  1930. X    skipspaces(0); /* 0 or more spaces */
  1931. X    switch (lower(*p)) {   /* d, m, or y */
  1932. X        case 'd' : t -= value * SECS_PER_DAY;
  1933. X        when 'w' : t -= value * SECS_PER_WEEK;
  1934. X        when 'm' : t -= value * SECS_PER_MONTH;
  1935. X        when 'y' : t -= value * SECS_PER_YEAR;
  1936. X        otherwise: return -1;
  1937. X    }
  1938. X    for (p++; Lower(*p) >= 'a' && *p <= 'z'; p++)
  1939. X        ; /* skip the rest of this token */
  1940. X    while (*p == ',' || isspace(*p))
  1941. X        ++p; /* 0 or more whitespaces or commas */
  1942. X    }
  1943. X    today = localtime(&t);
  1944. X    mdy[0] = today->tm_mon;
  1945. X    mdy[1] = today->tm_mday;
  1946. X    mdy[2] = today->tm_year;
  1947. X
  1948. X    /* Count the number of args parsed */
  1949. X    for (n = 0; p > buf && *argv; n++)
  1950. X    p -= (strlen(*argv++)+1);
  1951. X    Debug("parsed %d args\n", n);
  1952. X    return n;
  1953. X}
  1954. END_OF_FILE
  1955. if test 16964 -ne `wc -c <'mush/pick.c'`; then
  1956.     echo shar: \"'mush/pick.c'\" unpacked with wrong size!
  1957. fi
  1958. # end of 'mush/pick.c'
  1959. fi
  1960. echo shar: End of archive 5 \(of 19\).
  1961. cp /dev/null ark5isdone
  1962. MISSING=""
  1963. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  1964.     if test ! -f ark${I}isdone ; then
  1965.     MISSING="${MISSING} ${I}"
  1966.     fi
  1967. done
  1968. if test "${MISSING}" = "" ; then
  1969.     echo You have unpacked all 19 archives.
  1970.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1971. else
  1972.     echo You still need to unpack the following archives:
  1973.     echo "        " ${MISSING}
  1974. fi
  1975. ##  End of shell archive.
  1976. exit 0
  1977.  
  1978.  
  1979.