home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume12 / mush / part05 < prev    next >
Encoding:
Text File  |  1990-05-05  |  54.6 KB  |  1,986 lines

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