home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / mush.lzh / mush.18 < prev    next >
Encoding:
Text File  |  1990-05-06  |  55.1 KB  |  2,050 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 18 (of 19)."
  10. # Contents:  mush/Mushrc mush/expr.c mush/macros.c mush/main.c
  11. #   mush/options.c mush/print.c mush/sort.c
  12. # Wrapped by argv@turnpike on Wed May  2 13:59:53 1990
  13. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  14. if test -f 'mush/Mushrc' -a "${1}" != "-c" ; then 
  15.   echo shar: Will not clobber existing file \"'mush/Mushrc'\"
  16. else
  17. echo shar: Extracting \"'mush/Mushrc'\" \(5551 characters\)
  18. sed "s/^X//" >'mush/Mushrc' <<'END_OF_FILE'
  19. X# Mushrc -- suggested /usr/lib/Mushrc init file for Mush
  20. X# Copyright (c) 1989 by Bart Schaefer and Dan Heller
  21. X#
  22. X# Set these variables as they are set in config.h
  23. X# (assumes that this file is DEFAULT_RC from config.h)
  24. Xset \
  25. X    MAILRC =    ".mushrc" \
  26. X    ALTERNATE_RC =    ".mailrc" \
  27. X    ALT_DEF_RC =    "/usr/lib/Mail.rc"
  28. X
  29. X# Set up the display early to allow quick exit in headers-only mode.
  30. X# The hdrs_only flag is true if the command line was: "mush -H".
  31. X# The variable hdr_format is set to change the format of the header
  32. X# summaries that are displayed.
  33. Xif hdrs_only
  34. X    set hdr_format='%22a %M %-2N %5T  %.33s'
  35. X    exit    # Quits reading this file
  36. Xelse
  37. X    set hdr_format='%22a %M %-2N %5T (%3.5l li) %.25s'
  38. Xendif
  39. X
  40. X# Set the prompt to show current time, name of the current folder,
  41. X# current message number, and count of total messages.
  42. Xset prompt="(%T) %f: #%m of %t> "
  43. X
  44. X# Hitting <CR> should do nothing (helps make mush more shell-like).  If
  45. X# newline is not set, hitting <CR> prints the next message (like Mail).
  46. X# This variable could be set to any mush command.
  47. Xset newline
  48. X
  49. X# When reading messages, don't bother looking at lengthy, boring headers.
  50. Xignore message-id received via status
  51. X
  52. X# Since mush has csh-like history, you might find it annoying to type
  53. X# things like "mail host\!host1\!host2\!user" from within the mush shell.
  54. X# Setting nonobang will prevent the "unknown event" and allow the !'s to
  55. X# be typed without having to be preceded by backslashes.
  56. Xset nonobang
  57. X
  58. X# By default, mush's history is set to the last command only.  Set it to
  59. X# remember the last 100 commands.
  60. Xset history = 100
  61. X
  62. X# If the variable "unix" is set, then any command that isn't a mush command
  63. X# will execute the command as if you typed it from the shell.  Note, such
  64. X# commands will not go through another shell -- this is it.  This may be
  65. X# considered confusing for new users, so it is commented out by default.
  66. X# set unix
  67. X
  68. X# When you use the -i option to reply, or use the ~i tilde escape in a letter
  69. X# when in compose mode, the current message will be included in your text.
  70. X# Put a nice wrapper around those included messages.  Here, show the author's
  71. X# name and the subject of his letter, label the end, and add a trailing blank
  72. X# to separate each inclusion and make finding the end easier.
  73. Xset pre_indent_str='On %M %N, %T, %.50n wrote:\n} Subject: %.65s'
  74. Xset indent_str='} '    # actual message text is preceded by a "}"
  75. Xset post_indent_str='}-- End of excerpt from %.50n\n'
  76. X
  77. X# Label replies with a header showing the who, what, and when of the
  78. X# message being replied-to.
  79. Xset in_reply_to='%f\n\t"%s" (%d)'
  80. X
  81. X# Mail routing and address-fixing conveniences.  If auto_route is set, then
  82. X# replies to messages take a closer look at the addresses of the recipients.
  83. X# If any redundant paths are present, they are pruned.  Also, the path that
  84. X# precedes any hosts listed in the "known_hosts" list is truncated.  This is
  85. X# useful for uucp sites only, and is therefore commented out by default.
  86. X# set auto_route known_hosts="sun ucbcad well unicom"
  87. X
  88. X# The curses mode allows the screen to be set up like a full screen editor.
  89. X# There are basic "curses commands" which are bound to keyboard key-sequences
  90. X# (usually one character).  The user can rebind these keys to suit his tastes.
  91. X# Note that the binding for R below removes the binding of reply-all.
  92. X#
  93. Xset curses_help        # Unset this to remove help message in curses.
  94. Xbind \n display        # Hit return to display the next message.
  95. Xbind e macro "[line-mode]edit\n"    # Quick edit from curses.
  96. Xbind P macro "[line-mode]Print\n"    # Show me all the headers.
  97. X
  98. X# "cmd" is used to set command line aliases similar to the way "csh"
  99. X# does it.  The only difference is that "alias" is a reserved word in
  100. X# Mush and Mail, so cmd is used.
  101. X#
  102. Xcmd dq 'd \!*; q'        # Delete a message list, then quit.
  103. Xcmd unread 'flags \!* U O'    # Mark messages unread.
  104. Xcmd : curses            # Colon now "toggles" curses mode.
  105. X
  106. X# Read the alternate system init file in addition to this file
  107. Xsource $ALT_DEF_RC
  108. Xunset ALT_DEF_RC
  109. X
  110. X# Mush tries to read ~/.mushrc first, then it tries ~/.mailrc.  If .mushrc
  111. X# is found, .mailrc is normally not read.  Source the contents of .mailrc
  112. X# as well in case there are Mail aliases that are set there.
  113. Xif -e $HOME/$MAILRC
  114. X    if -e $HOME/$ALTERNATE_RC
  115. X    source $HOME/$ALTERNATE_RC
  116. X    endif
  117. Xendif
  118. Xunset ALTERNATE_RC
  119. X
  120. X# The rest of this file demonstrates how sysadmins with novice users
  121. X# might want to set things up.
  122. X
  123. Xif -e $HOME/.mushexpert
  124. X    set quiet
  125. X    exit
  126. Xendif
  127. Xif ! -e $HOME/.mushuser
  128. X    echo "I see you've never used the Mush program before."
  129. X    echo "I'll set some special flags to help you out."
  130. X    echo "After you've used Mush a few times, you can type the command:"
  131. X    echo ""
  132. X    echo "    expert"
  133. X    echo ""
  134. X    echo "and the flags will no longer be set unless you put them"
  135. X    echo "in your $HOME/$MAILRC file."
  136. X    echo ""
  137. X    sh touch $HOME/.mushuser    # Use sh in case $unix is not set.
  138. X    if $?quiet
  139. X    unset quiet        # Show the help message on startup.
  140. X    endif
  141. Xendif
  142. X# At this point some helpful variables should be set.  See the list above,
  143. X# marked as "helpful for new users", for suggestions.
  144. X#
  145. X# Create the "expert" command mentioned in the message above.
  146. Xcmd expert 'sh touch $HOME/.mushexpert'
  147. X
  148. X# These variables are helpful for new users:
  149. X#    ask        -- always prompt for Subject: of mail
  150. X#    ignoreeof    -- ignore end-of-file from keyboard
  151. X#    verify        -- query that all is well before sending mail
  152. X#    warning        -- report miscellaneous possible problems
  153. Xset ask verify warning
  154. Xset ignoreeof="echo 'Use "'"'quit'"'" to quit.'"
  155. END_OF_FILE
  156. if test 5551 -ne `wc -c <'mush/Mushrc'`; then
  157.     echo shar: \"'mush/Mushrc'\" unpacked with wrong size!
  158. fi
  159. # end of 'mush/Mushrc'
  160. fi
  161. if test -f 'mush/expr.c' -a "${1}" != "-c" ; then 
  162.   echo shar: Will not clobber existing file \"'mush/expr.c'\"
  163. else
  164. echo shar: Extracting \"'mush/expr.c'\" \(4685 characters\)
  165. sed "s/^X//" >'mush/expr.c' <<'END_OF_FILE'
  166. X/* @(#)expr.c    2.3    (c) copyright 10/15/86 (Dan Heller) */
  167. X
  168. X#include "mush.h"
  169. X
  170. Xchar *eval_expr();
  171. X
  172. X/* Parse a string (p) to interpret numbers and ranges of numbers (n-m)
  173. X * delimited by whitespace or comma's. Set msg_list bitfields using
  174. X * macros in mush.h.
  175. X * Return the address of the end of whatever we parsed (in case there's
  176. X * more that the calling routine cares to deal with).
  177. X * Finally, remember that user specifies one more than actual message number
  178. X */
  179. Xchar *
  180. Xdo_range(p, list1)
  181. Xregister char *p, *list1;
  182. X{
  183. X    register int num1 = -1, num2 = -1, except = 0;
  184. X    register char *p2;
  185. X    char list2[MAXMSGS_BITS];
  186. X
  187. X    if (!p)
  188. X    return "";
  189. X    while (*p) {
  190. X    if (isdigit(*p) || *p == '$' || *p == '.' || *p == '^') {
  191. X        if (isdigit(*p)) {
  192. X        char c;
  193. X        p2 = p;
  194. X        skipdigits(0);  /* find the end of the digits */
  195. X        c = *p, *p = 0; /* temporarily plug a null */
  196. X        if (!(num2 = chk_msg(p2))) {
  197. X            clear_msg_list(list1);
  198. X            return NULL;
  199. X        }
  200. X        *p = c;
  201. X        } else if (*p == '$')
  202. X        p++, num2 = msg_cnt;
  203. X        else if (*p == '.')
  204. X        p++, num2 = current_msg+1;
  205. X        else if (*p == '^')
  206. X        p++, num2 = 1;
  207. X        if (except)
  208. X        unset_msg_bit(list1, num2-1);
  209. X        else
  210. X        set_msg_bit(list1, num2-1);
  211. X        if (num1 >= 0) {
  212. X        if (num1 > num2) {
  213. X            print("syntax error: range sequence order reversed.\n");
  214. X            clear_msg_list(list1);
  215. X            return NULL;
  216. X        }
  217. X        while (++num1 < num2)
  218. X            if (except)
  219. X            unset_msg_bit(list1, num1-1);
  220. X            else
  221. X            set_msg_bit(list1, num1-1);
  222. X        num1 = num2 = -1;
  223. X        }
  224. X    }
  225. X    /* expressions to evaluate start with a `
  226. X     * p2 points to first char passed the last char parsed.
  227. X     */
  228. X    if (*p == '`') {
  229. X        clear_msg_list(list2);
  230. X        if (!(p = eval_expr(p, list2))) {
  231. X        clear_msg_list(list1);
  232. X        return NULL;
  233. X        } else {
  234. X        if (except)
  235. X            bitput(list2, list1, msg_cnt, &=~); /* MACRO */
  236. X        else
  237. X            bitput(list2, list1, msg_cnt, |=); /* MACRO */
  238. X        }
  239. X    }
  240. X    /* NOT operator: `* {5}' (everything except for 5)
  241. X     * `4-16 {8-10}'  (4 thru 16 except for 8,9,10)
  242. X     */
  243. X    if (*p == '{' || *p == '}') {
  244. X        if (*p == '{' && (except || num1 >= 0))
  245. X        break;
  246. X        if (*p == '}' && !except) {
  247. X        print("syntax error: missing {\n"); /* } */
  248. X        break;
  249. X        }
  250. X        except = !except;
  251. X    } else if (*p == '-')
  252. X        if (num1 >= 0 || num2 < 0
  253. X            || !index(" \t{},.*`$", *(p+1)) && !isdigit(*(p+1)))
  254. X        break;
  255. X        else
  256. X        num1 = num2;
  257. X    else if (*p == ',' || *p == '*') {
  258. X        if (num1 >= 0)
  259. X        break;
  260. X        else if (*p == '*') {
  261. X        if (except)
  262. X            clear_msg_list(list1);
  263. X        else
  264. X            for (num1 = 0; num1 < msg_cnt; num1++)
  265. X            set_msg_bit(list1, num1);
  266. X        num1 = -1;
  267. X        }
  268. X    } else if (!index(" \t`", *p))
  269. X        break;
  270. X    if (*p)
  271. X        skipspaces(1); /* don't make user type stuff squished together */
  272. X    }
  273. X    if (num1 >= 0 || except) {
  274. X    if (except)
  275. X        /* { */ print("syntax error: unmatched }\n");
  276. X    else
  277. X        print("syntax error: unfinished range\n");
  278. X    clear_msg_list(list1);
  279. X    return NULL;
  280. X    }
  281. X    return p;
  282. X}
  283. X
  284. X/*
  285. X * convert a message list to an ascii string.
  286. X */
  287. Xvoid
  288. Xlist_to_str(list, str)
  289. Xchar list[], *str;
  290. X{
  291. X    int n, m = -1;
  292. X
  293. X    for (n = 0; n < msg_cnt; n++) {
  294. X    if (msg_bit(list, n)) {
  295. X        if (m == -1)
  296. X        str += strlen(sprintf(str, "%d", (m = n) + 1 ));
  297. X        continue;
  298. X    }
  299. X    if (m == -1)
  300. X        continue;
  301. X    if (n - m > 2)
  302. X        str += strlen(sprintf(str, "-%d", n));
  303. X    else if (n - m == 2)
  304. X        str += strlen(sprintf(str, " %d", n));
  305. X    *str++ = ' ';
  306. X    m = -1;
  307. X    }
  308. X    if (m > -1 && m != n - 1) {
  309. X    if (n - m > 2)
  310. X        *str++ = '-';
  311. X    else
  312. X        *str++ = ' ';
  313. X    str += Strcpy(str, itoa(msg_cnt));
  314. X    }
  315. X    *str = 0;
  316. X}
  317. X
  318. X/* evaluate expressions:
  319. X * mail> delete `pick -f root`     deletes all messages from root.
  320. X * mail> save * {`pick -s "Re:"`}  save all message that don't have "Re:"
  321. X *                   in the subject header.
  322. X * mail> save `pick -x -s "Re:"`   same
  323. X * args as follows:
  324. X *   p should point to the first ` -- check for it.
  325. X *   on tells whether to turn bits on or off if messages match.
  326. X */
  327. Xchar *
  328. Xeval_expr(p, new_list)
  329. Xregister char *p, new_list[];
  330. X{
  331. X    register char *p2, **argv;
  332. X    int       argc;
  333. X    u_long      save_flags = glob_flags;
  334. X
  335. X    if (!(p2 = index(++p, '`'))) {
  336. X    print("unmatched backquote (`)\n");
  337. X    return NULL;
  338. X    }
  339. X    *p2 = 0;
  340. X
  341. X    skipspaces(0);
  342. X    if (!*p) {
  343. X    print("Invalid null command\n");
  344. X    return NULL;
  345. X    }
  346. X    turnon(glob_flags, DO_PIPE);
  347. X    /* ignore sigs only because if user interrupts the do_command,
  348. X     * the longjmp will corrupt the stack and the program is hosed.
  349. X     * fix is to have layers of jmp_bufs to return to different levels.
  350. X     */
  351. X    turnon(glob_flags, IGN_SIGS);
  352. X    if (*p && (argv = make_command(p, TRPL_NULL, &argc)))
  353. X    (void) do_command(argc, argv, new_list);
  354. X    glob_flags = save_flags;
  355. X    *p2 = '`';
  356. X    return p2+1;
  357. X}
  358. END_OF_FILE
  359. if test 4685 -ne `wc -c <'mush/expr.c'`; then
  360.     echo shar: \"'mush/expr.c'\" unpacked with wrong size!
  361. fi
  362. # end of 'mush/expr.c'
  363. fi
  364. if test -f 'mush/macros.c' -a "${1}" != "-c" ; then 
  365.   echo shar: Will not clobber existing file \"'mush/macros.c'\"
  366. else
  367. echo shar: Extracting \"'mush/macros.c'\" \(9279 characters\)
  368. sed "s/^X//" >'mush/macros.c' <<'END_OF_FILE'
  369. X/* (@)# macros.c    (c) copyright 9/19/88 (Bart Schaefer, Dan Heller) */
  370. X
  371. X#include "bindings.h"
  372. X#include "mush.h"
  373. X
  374. Xextern struct cmd_map map_func_names[];
  375. X
  376. Xstruct cmd_map    *mac_stack, *mac_hide;
  377. X
  378. X/*
  379. X * print current binding to macro mappings if "str" is NULL.
  380. X * else return the string "x_str" which the str is bound to.
  381. X */
  382. Xchar *
  383. Xc_macro(name, str, opts)
  384. Xchar *name;
  385. Xregister char *str;
  386. Xregister struct cmd_map *opts;
  387. X{
  388. X    register int    incurses = iscurses;
  389. X    char buf[MAX_MACRO_LEN], buf2[sizeof buf * 3];
  390. X
  391. X    if (!str) {
  392. X    for (; opts; opts = opts->m_next)
  393. X        if (opts->m_cmd == C_MACRO)
  394. X        break;
  395. X    if (!opts) {
  396. X        print("No %s settings.\n", name);
  397. X        return (char *)(-1);
  398. X    }
  399. X    if (incurses)
  400. X        clr_bot_line(), iscurses = FALSE;
  401. X    (void) do_pager(NULL, TRUE);
  402. X    (void) do_pager(sprintf(buf, "\nCurrent %s settings:\n\n",name), FALSE);
  403. X    }
  404. X
  405. X    if (!opts)
  406. X    return NULL;
  407. X
  408. X    for (; opts; opts = opts->m_next) {
  409. X    if (opts->m_cmd != C_MACRO)
  410. X        continue;
  411. X    if (!str) {
  412. X        (void) do_pager(sprintf(buf, "%-20.20s  ",
  413. X        ctrl_strcpy(buf2, opts->m_str, FALSE)), FALSE);
  414. X        if (do_pager(sprintf(buf, "%s\n",
  415. X        ctrl_strcpy(buf2, opts->x_str, TRUE)), FALSE) == EOF)
  416. X        break;
  417. X    } else {
  418. X        if (strcmp(str, opts->m_str))
  419. X        continue;
  420. X        else
  421. X        return opts->x_str;
  422. X    }
  423. X    }
  424. X    iscurses = incurses;
  425. X    if (str)
  426. X    (void) do_pager(NULL, FALSE);
  427. X    return NULL;
  428. X}
  429. X
  430. Xmac_push(str)
  431. Xregister char *str;
  432. X{
  433. X    register struct cmd_map *tmp;
  434. X
  435. X    /* now make a new macro struct and set fields */
  436. X    if (!(tmp = (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
  437. X    error("calloc");
  438. X    return -1;
  439. X    }
  440. X    tmp->m_next = mac_stack;
  441. X    mac_stack = tmp;
  442. X    tmp->x_str = savestr(str);    /* x_str is the text of the expansion */
  443. X    tmp->m_str = tmp->x_str;    /* m_str is the current read position */
  444. X
  445. X    /*
  446. X     * Save the current state of the glob_flags so
  447. X     * mac_push() can also serve as unget of stdin
  448. X     */
  449. X    tmp->m_cmd = glob_flags;
  450. X    return 0;
  451. X}
  452. X
  453. Xmac_queue(str)
  454. Xregister char *str;
  455. X{
  456. X    register struct cmd_map **tmp; /* NOTE pointer to pointer! */
  457. X
  458. X    /* Find the bottom of the macro stack */
  459. X    for (tmp = &mac_stack; *tmp; tmp = &((*tmp)->m_next))
  460. X    ;
  461. X    /* now make a new macro struct and set fields */
  462. X    if (!(*tmp =
  463. X        (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
  464. X    error("calloc");
  465. X    return -1;
  466. X    }
  467. X    (*tmp)->m_next = (struct cmd_map *)0; /* calloc should do this .... */
  468. X    (*tmp)->x_str = savestr(str); /* x_str is the text of the expansion */
  469. X    (*tmp)->m_str = (*tmp)->x_str; /* m_str is the current read position */
  470. X
  471. X    /*
  472. X     * Save the current state of the glob_flags
  473. X     */
  474. X    (*tmp)->m_cmd = glob_flags;
  475. X    return 0;
  476. X}
  477. X
  478. Xvoid
  479. Xmac_pop()
  480. X{
  481. X    register struct cmd_map *tmp;
  482. X
  483. X    if (mac_stack) {
  484. X    tmp = mac_stack;
  485. X    mac_stack = tmp->m_next;
  486. X    xfree(tmp->x_str);
  487. X    xfree((char *) tmp);
  488. X    }
  489. X    /*
  490. X     * Restore saved MACRO glob_flags only (see mac_push())
  491. X     */
  492. X    if (mac_stack) {
  493. X    if (ison(mac_stack->m_cmd, IN_MACRO))
  494. X        turnon(glob_flags, IN_MACRO);
  495. X    else
  496. X        turnoff(glob_flags, IN_MACRO);
  497. X    if (ison(mac_stack->m_cmd, LINE_MACRO))
  498. X        turnon(glob_flags, LINE_MACRO);
  499. X    else
  500. X        turnoff(glob_flags, LINE_MACRO);
  501. X    if (ison(mac_stack->m_cmd, QUOTE_MACRO))
  502. X        turnon(glob_flags, QUOTE_MACRO);
  503. X    else
  504. X        turnoff(glob_flags, QUOTE_MACRO);
  505. X    }
  506. X}
  507. X
  508. X/* Abandon macro processing */
  509. Xvoid
  510. Xmac_flush()
  511. X{
  512. X    while (mac_stack)
  513. X    mac_pop();
  514. X    if (mac_hide) {
  515. X    mac_stack = mac_hide;
  516. X    mac_hide = NULL_MAP;
  517. X    while (mac_stack)
  518. X        mac_pop();
  519. X    }
  520. X    turnoff(glob_flags, IN_MACRO);
  521. X    turnoff(glob_flags, LINE_MACRO);
  522. X    turnoff(glob_flags, QUOTE_MACRO);
  523. X}
  524. X
  525. X/* Check for pending input from a macro. */
  526. Xmac_pending()
  527. X{
  528. X    register struct cmd_map *msp;
  529. X
  530. X    for (msp = mac_stack; msp && !*(msp->m_str); msp = msp->m_next)
  531. X    ;
  532. X
  533. X    return !!msp;
  534. X}
  535. X
  536. X/* Get input and treat it as a macro.  */
  537. Xget_mac_input(newline)
  538. Xint newline;    /* 1 if newline to be appended, 0 otherwise */
  539. X{
  540. X    register int len;
  541. X    char buf[MAX_MACRO_LEN];
  542. X
  543. X    /* This call cannot be nested */
  544. X    if (mac_hide)
  545. X    return -1;
  546. X
  547. X    /* Hide the mac_stack so input comes from stdin */
  548. X    mac_hide = mac_stack; mac_stack = NULL_MAP;
  549. X
  550. X    if ((len = Getstr(buf, MAX_MACRO_LEN - 1, 0)) < 0)
  551. X    return len;
  552. X    if (newline) {
  553. X    buf[len++] = '\n';
  554. X    buf[len] = 0;
  555. X    }
  556. X
  557. X    /* Restore the mac_stack */
  558. X    if (mac_stack) {
  559. X    /*
  560. X     * Somehow, a push happened even though mac_hide was
  561. X     * nonzero -- maybe by line wrap?  Fix it as best we can.
  562. X     */
  563. X    struct cmd_map *msp;
  564. X    for (msp = mac_stack; msp->m_next; msp = msp->m_next)
  565. X        ;
  566. X    msp->m_next = mac_hide;
  567. X    } else
  568. X    mac_stack = mac_hide;
  569. X    mac_hide = NULL_MAP;
  570. X
  571. X    /* Restore saved flags */
  572. X    if (mac_stack) {
  573. X    if (ison(mac_stack->m_cmd, IN_MACRO))
  574. X        turnon(glob_flags, IN_MACRO);
  575. X    else
  576. X        turnoff(glob_flags, IN_MACRO);
  577. X    if (ison(mac_stack->m_cmd, LINE_MACRO))
  578. X        turnon(glob_flags, LINE_MACRO);
  579. X    else
  580. X        turnoff(glob_flags, LINE_MACRO);
  581. X    }
  582. X    if (len > 0)
  583. X    Ungetstr(buf);
  584. X
  585. X    return 1;
  586. X}
  587. X
  588. X/* getchar() substitute -- reads from the current macro if one is active,
  589. X * otherwise does a getchar().
  590. X *
  591. X * NOTE:  In the mac_stack, x_str is the saved text of the current macro,
  592. X *  and m_str is the current read position within the macro.
  593. X */
  594. Xm_getchar()
  595. X{
  596. X    int c;
  597. X
  598. X    while (mac_stack && (! *(mac_stack->m_str)))
  599. X    mac_pop();
  600. X    if (mac_stack) {
  601. X    c = *((mac_stack->m_str)++);
  602. X    return c;
  603. X    } else {
  604. X    turnoff(glob_flags, IN_MACRO);
  605. X    turnoff(glob_flags, LINE_MACRO);
  606. X    turnoff(glob_flags, QUOTE_MACRO);
  607. X    while ((c = getchar()) == 0)    /* Ignore NUL chars from stdin */
  608. X        ;                /* until better solution found */
  609. X    return c;
  610. X    }
  611. X}
  612. X
  613. Xm_ungetc(c)
  614. Xchar c;
  615. X{
  616. X    if (mac_stack && (mac_stack->m_str > mac_stack->x_str))
  617. X    *(--(mac_stack->m_str)) = c;
  618. X    else
  619. X    (void) ungetc(c, stdin);
  620. X}
  621. X
  622. X/*
  623. X * Try to read a long command; assumes MAC_LONG_CMD already seen.
  624. X *  On immediate failure, return 0.
  625. X *  On failure after reading some input, return less than zero.
  626. X *  On success, return greater than 0.
  627. X * The absolute value of the return is the number of chars placed in buf.
  628. X */
  629. Xread_long_cmd (buf)
  630. Xchar *buf;
  631. X{
  632. X    register char c, *p = buf;
  633. X    register int count = 0;
  634. X
  635. X    /*
  636. X     * Test in_macro() in this loop because the _entire_
  637. X     * long command _must_ be in the macro -- if we run
  638. X     * out of macro in mid-long-command, it is an error.
  639. X     */
  640. X    while (in_macro() && (count < MAX_LONG_CMD - 1)
  641. X        && ((c = m_getchar()) != MAC_LONG_END)) {
  642. X    *p++ = c; ++count;
  643. X    }
  644. X    *p = '\0';
  645. X    if (c != MAC_LONG_END)
  646. X    return (-count);
  647. X    return count;
  648. X}
  649. X
  650. X/*
  651. X * Identify and possibly execute a reserved long macro command
  652. X * Executes if do_exec is true.  Otherwise, just parse.
  653. X */
  654. Xreserved_cmd (buf, do_exec)
  655. Xchar *buf;
  656. X{
  657. X    int ret = 1;
  658. X
  659. X    if (!strcmp(buf, MAC_GET_STR)) {
  660. X    if (do_exec)
  661. X        ret = get_mac_input(0);
  662. X    } else if (!strcmp(buf, MAC_GET_LINE)) {
  663. X    if (do_exec)
  664. X        ret = get_mac_input(1);
  665. X    } else
  666. X    ret = 0;
  667. X    return ret;
  668. X}
  669. X
  670. X#ifdef CURSES
  671. X
  672. X/*
  673. X * Identify (and possibly execute, if reserved) curses mode commands
  674. X *  that appear in macro strings enclosed by MAC_LONG_CMD and
  675. X *  MAC_LONG_END.  Return the binding of the command.
  676. X */
  677. Xlong_mac_cmd (c, do_exec)
  678. Xint c;
  679. X{
  680. X    char buf[MAX_LONG_CMD];
  681. X    register int count, binding;
  682. X    int y, x;
  683. X
  684. X    if (c != MAC_LONG_CMD)
  685. X    return C_ERROR;
  686. X
  687. X    if ((count = read_long_cmd(buf)) <= 0) {
  688. X    print("Invalid long macro command");
  689. X    if (ison(glob_flags, CNTD_CMD))
  690. X        putchar('\n');
  691. X    if (do_exec)
  692. X        mac_flush();
  693. X    return C_ERROR;
  694. X    }
  695. X
  696. X    if (do_exec) {
  697. X    if (ison(glob_flags, CNTD_CMD))
  698. X        clr_bot_line();
  699. X    getyx(stdscr, y, x);
  700. X    move(LINES - 1, 0);
  701. X    }
  702. X    if (reserved_cmd(buf, do_exec)) {
  703. X    if (do_exec) {
  704. X        if (isoff(glob_flags, CNTD_CMD))
  705. X        move(y, x);
  706. X        return getcmd();
  707. X    } else
  708. X        return C_NULL;
  709. X    } else if (do_exec)
  710. X    move(y, x);
  711. X
  712. X    /* Can start at C_NULL because of "no-op" command */
  713. X    for (count = 0; count <= C_HELP; count++) {
  714. X    if (!strcmp(buf, map_func_names[count].m_str)) {
  715. X        binding = (int)map_func_names[count].m_cmd;
  716. X        break;
  717. X    }
  718. X    }
  719. X    /* Don't allow C_MACRO to be called directly */
  720. X    if (count > C_HELP || binding == C_MACRO) {
  721. X    print("Invalid long macro command");
  722. X    if (ison(glob_flags, CNTD_CMD))
  723. X        putchar('\n');
  724. X    return C_ERROR;
  725. X    } else
  726. X    return binding;
  727. X}
  728. X
  729. X#endif /* CURSES */
  730. X
  731. X/*
  732. X * Check the validity of a macro binding as far as possible
  733. X */
  734. Xcheck_mac_bindings(buf)
  735. Xchar *buf;
  736. X{
  737. X    int ok = TRUE;
  738. X
  739. X    while (ok && buf && *buf) {
  740. X    if (*buf == MAC_LONG_CMD) {
  741. X        char *i;
  742. X#ifdef CURSES
  743. X        int count;
  744. X#endif /* CURSES */
  745. X
  746. X        if (ok)
  747. X        ok = ((i = index(++buf, MAC_LONG_END)) != NULL);
  748. X        if (i)
  749. X            *i = '\0';      /* Don't worry, we'll fix it */
  750. X        else
  751. X            return ok;
  752. X#ifdef CURSES
  753. X        /* OK to start at C_NULL because of "no-op" command */
  754. X        for (count = 0; count <= C_HELP; count++)
  755. X            if (! strcmp(buf, map_func_names[count].m_str))
  756. X                break;
  757. X        /* Don't allow C_MACRO to be called directly */
  758. X        if (count == C_MACRO)
  759. X            ok = FALSE;
  760. X        else if (count > C_HELP)
  761. X#endif /* CURSES */
  762. X        if (ok && !(ok = reserved_cmd(buf, FALSE)))
  763. X        wprint("Warning: unrecognized curses command: \"%s\"\n", buf);
  764. X        buf = i;
  765. X        *buf++ = MAC_LONG_END;
  766. X    } else if (*buf++ == '\\' && *buf)
  767. X        ++buf;
  768. X    }
  769. X    return ok;
  770. X}
  771. END_OF_FILE
  772. if test 9279 -ne `wc -c <'mush/macros.c'`; then
  773.     echo shar: \"'mush/macros.c'\" unpacked with wrong size!
  774. fi
  775. # end of 'mush/macros.c'
  776. fi
  777. if test -f 'mush/main.c' -a "${1}" != "-c" ; then 
  778.   echo shar: Will not clobber existing file \"'mush/main.c'\"
  779. else
  780. echo shar: Extracting \"'mush/main.c'\" \(8938 characters\)
  781. sed "s/^X//" >'mush/main.c' <<'END_OF_FILE'
  782. X/* @(#)main.c    (c) copyright 10/18/86 (Dan Heller) */
  783. X
  784. X#include "mush.h"
  785. X#include "options.h"
  786. X
  787. X#if defined(sun) && defined(M_DEBUG)
  788. Xcpu()
  789. X{
  790. X    print("CPU time limit exceeded!\n");
  791. X}
  792. X#endif /* sun && DEBUG */
  793. X
  794. X#ifdef LCKDFLDIR
  795. Xextern char *lckdfldir;
  796. X#endif /* LCKDFLDIR */
  797. X
  798. X#ifdef DOT_LOCK
  799. Xint sgid;
  800. X#ifdef BSD
  801. Xint rgid;
  802. X#endif /* BSD */
  803. X#endif /* DOT_LOCK */
  804. X
  805. X/*ARGSUSED*/   /* we ignore envp */
  806. Xmain(argc, argv)
  807. Xint argc;
  808. Xchar *argv[];
  809. X{
  810. X    int              n;
  811. X    char           buf[MAXPATHLEN];
  812. X    register char    *p;
  813. X    struct mush_flags Flags;
  814. X
  815. X#ifndef INTERNAL_MALLOC
  816. X    extern char *stackbottom;    /* used by xfree() */
  817. X
  818. X    stackbottom = (char *) &argc;
  819. X#endif /* INTERNAL_MALLOC */
  820. X
  821. X#ifdef LCKDFLDIR
  822. X    lckdfldir = LCKDFLDIR;
  823. X#endif /* LCKDFLDIR */
  824. X    if (prog_name = rindex(*argv, '/'))
  825. X    prog_name++;
  826. X    else
  827. X    prog_name = *argv;
  828. X
  829. X    (void) signal(SIGBUS,  bus_n_seg);
  830. X    (void) signal(SIGSEGV, bus_n_seg);
  831. X    (void) signal(SIGPIPE, SIG_IGN); /* if pager is terminated before end */
  832. X
  833. X#if defined(sun) && defined(M_DEBUG)
  834. X    (void) signal(SIGXCPU, cpu);
  835. X
  836. X    if (p = getenv("MALLOC_DEBUG"))
  837. X    malloc_debug(atoi(p));
  838. X    else
  839. X    malloc_debug(0);
  840. X#endif /* sun && debug */
  841. X
  842. X    if (!isatty(0))
  843. X    turnon(glob_flags, REDIRECT);
  844. X    else
  845. X    (void) setbuf(stdin, NULL);
  846. X
  847. X    (void) cmd_line(strcpy(buf, "set cmd_help"), NULL);
  848. X
  849. X    init(); /* must be done before checking mail since "login" is set here */
  850. X    mailfile = "";
  851. X#ifdef HOMEMAIL
  852. X    {
  853. X    char *home = do_set(set_options, "home");
  854. X    if (!home)
  855. X        home = ALTERNATE_HOME;
  856. X    strdup(spoolfile, sprintf(buf, "%s/%s", home, MAILFILE));
  857. X    }
  858. X#else /* HOMEMAIL */
  859. X    strdup(spoolfile, sprintf(buf, "%s/%s", MAILDIR, login));
  860. X#endif /* HOMEMAIL */
  861. X
  862. X    n = preparse_opts(&argc,argv);
  863. X
  864. X    /* check for any mail at all and exit if we're not continuing */
  865. X    if (!n) {
  866. X    struct stat statb;
  867. X    if (stat(spoolfile, &statb) || statb.st_size == 0) {
  868. X        (void) printf("No mail for %s.\n", login);
  869. X        exit(0);
  870. X    }
  871. X    }
  872. X
  873. X#ifdef DOT_LOCK
  874. X    sgid = getegid();
  875. X#ifdef BSD
  876. X    rgid = getgid();
  877. X    setregid(sgid, rgid);
  878. X#else
  879. X    setgid(getgid());
  880. X#endif /* BSD */
  881. X#endif /* DOT_LOCK */
  882. X
  883. X    parse_options(&argv, &Flags);
  884. X
  885. X    set_cwd();
  886. X
  887. X    if (Flags.init_file)
  888. X    (void) cmd_line(sprintf(buf, "source %s", Flags.init_file), msg_list);
  889. X    if (Flags.source_rc > 0) {
  890. X    /* use cmd_line() in case DEFAULT_RC has expandable chars */
  891. X    (void) cmd_line(sprintf(buf, "source %s", DEFAULT_RC), msg_list);
  892. X    }
  893. X    if (Flags.source_rc > -1)
  894. X    (void) source(0, DUBL_NULL);
  895. X    mailfile = Flags.folder;
  896. X
  897. X    if (*spoolfile != '/') {
  898. X    n = 1;
  899. X    p = getpath(spoolfile, &n);
  900. X    if (n == -1)
  901. X        (void) fputs(p, stderr), exit(1);
  902. X    else if (n)
  903. X        (void) fprintf(stderr, "\"%s\" is a directory.\n", p), exit(1);
  904. X    else if (*p != '/') {
  905. X        /* if it still isn't a full path, make it one */
  906. X        char *wd = do_set(set_options, "cwd");
  907. X        if (*wd) {
  908. X        (void) sprintf(buf, "%s/%s", wd, p);
  909. X        strdup(spoolfile, buf);
  910. X        } else
  911. X        strdup(spoolfile, p);
  912. X    } else
  913. X        strdup(spoolfile, p);
  914. X    }
  915. X
  916. X#ifdef SUNTOOL
  917. X    if (istool) {
  918. X    make_tool();
  919. X    turnon(glob_flags, DO_SHELL);
  920. X    turnoff(glob_flags, REDIRECT); /* -- SunOS-4.0 has a problem here */
  921. X    }
  922. X#endif /* SUNTOOL */
  923. X
  924. X    /* now we're ready for I/O */
  925. X    if (isoff(glob_flags, REDIRECT)) {
  926. X    /* make sure we can always recover from no echo mode */
  927. X    (void) signal(SIGINT, catch);
  928. X    (void) signal(SIGQUIT, catch);
  929. X    (void) signal(SIGHUP, catch);
  930. X    if (istool)
  931. X        turnon(glob_flags, ECHO_FLAG);
  932. X    tty_settings();
  933. X#ifdef SIGCONT
  934. X    (void) signal(SIGTSTP, stop_start); /* this will take care of SIGCONT */
  935. X#endif /* SIGCONT */
  936. X    /* echo_off() checks to see if echo_flg is set, so don't worry */
  937. X    echo_off();
  938. X    }
  939. X
  940. X    if (!istool && ison(glob_flags, IS_SENDING)) {
  941. X    char recipients[BUFSIZ], *mailv[16];
  942. X    (void) argv_to_string(recipients, argv);
  943. X    fix_up_addr(recipients);
  944. X    mailv[0] = "mail";
  945. X    n = 1;
  946. X    if (ison(Flags.flg, VERBOSE))
  947. X        mailv[n++] = "-v";
  948. X    if (Flags.Subj && *(Flags.Subj)) {
  949. X        mailv[n++] = "-s";
  950. X        mailv[n++] = Flags.Subj;
  951. X    }
  952. X    if (Flags.Cc && *(Flags.Cc)) {
  953. X        fix_up_addr(Flags.Cc);
  954. X        mailv[n++] = "-c";
  955. X        mailv[n++] = Flags.Cc;
  956. X    }
  957. X    if (Flags.Bcc && *(Flags.Bcc)) {
  958. X        fix_up_addr(Flags.Bcc);
  959. X        mailv[n++] = "-b";
  960. X        mailv[n++] = Flags.Bcc;
  961. X    }
  962. X    if (ison(Flags.flg, NO_SIGN))
  963. X        mailv[n++] = "-u";
  964. X    if (ison(Flags.flg, SEND_NOW))
  965. X        mailv[n++] = "-U";
  966. X    if (Flags.draft) {
  967. X        if (isoff(Flags.flg, SEND_NOW))
  968. X        mailv[n++] = "-E";
  969. X        mailv[n++] = "-h";
  970. X        mailv[n++] = Flags.draft;
  971. X    }
  972. X    mailv[n++] = recipients;
  973. X    mailv[n] = NULL;
  974. X    /* set now in case user is not running shell, but is running debug */
  975. X#ifndef SUNTOOL
  976. X    (void) signal(SIGCHLD, sigchldcatcher);
  977. X#endif /* SUNTOOL */
  978. X    if (!setjmp(jmpbuf))
  979. X        (void) do_mail(n, mailv, msg_list);
  980. X    /* do shell set from above: "mush -S user" perhaps */
  981. X    if (isoff(glob_flags, DO_SHELL) && !*mailfile) {
  982. X        if (isoff(glob_flags, REDIRECT))
  983. X        echo_on();
  984. X        exit(0);
  985. X    }
  986. X    }
  987. X    turnoff(glob_flags, IS_SENDING); /* no longer sending mail; running shell */
  988. X
  989. X    if (ison(glob_flags, REDIRECT)
  990. X        && (!Flags.src_file || !Flags.src_n_exit)) {
  991. X    puts("You can't redirect input unless you're sending mail.");
  992. X    puts("If you want to run a shell with redirection, use \"-i\"");
  993. X    cleanup(0);
  994. X    }
  995. X    if (!*mailfile) {
  996. X    strdup(mailfile, spoolfile);
  997. X    if (!mail_size() && isoff(glob_flags, DO_SHELL)) {
  998. X        /* we know it's not the spool file here */
  999. X        (void) printf("No mail in %s.\n", mailfile);
  1000. X        echo_on(), exit(0);
  1001. X    }
  1002. X    }
  1003. X
  1004. X    if (!hdrs_only) {
  1005. X    /* catch will test DO_SHELL and try to longjmp if set.  this is a
  1006. X     * transition state from no-shell to do-shell to ignore sigs to
  1007. X     * avoid a longjmp botch.  Note setjmp isn't called until do_loop().
  1008. X     */
  1009. X    turnon(glob_flags, IGN_SIGS);
  1010. X#ifdef CURSES
  1011. X    if (ison(glob_flags, PRE_CURSES))
  1012. X        (void) curses_init(0, DUBL_NULL);
  1013. X    turnoff(glob_flags, PRE_CURSES);
  1014. X#endif /* CURSES */
  1015. X    }
  1016. X
  1017. X    /* find a free tmpfile */
  1018. X    if (!(p = getdir(do_set(set_options, "tmpdir"))))
  1019. Xalted:
  1020. X    p = ALTERNATE_HOME;
  1021. X    {
  1022. X    int pid = getpid();
  1023. X    while (!Access(sprintf(tempfile, "%s/.%s%d", p, prog_name, pid++), F_OK))
  1024. X    ;
  1025. X    }
  1026. X    /* just create the file, make sure it's empty.  It'll close later and
  1027. X     * be reopened for reading only.
  1028. X     */
  1029. X    if (!(tmpf = mask_fopen(tempfile, "w"))) {
  1030. X    if (strcmp(p, ALTERNATE_HOME))
  1031. X        goto alted;
  1032. X    error("Can't create tempfile %s", tempfile);
  1033. X    cleanup(0);
  1034. X    }
  1035. X
  1036. X    /* do pseudo-intelligent stuff with certain signals */
  1037. X    (void) signal(SIGINT,  catch);
  1038. X    (void) signal(SIGQUIT, catch);
  1039. X    (void) signal(SIGHUP,  catch);
  1040. X
  1041. X    if (!hdrs_only && !istool && (!Flags.src_file || !Flags.src_n_exit) &&
  1042. X    !glob(do_set(set_options, "quiet"), "{,{,*[ \\,]}startup{,[ \\,]*}}"))
  1043. X    (void) printf("%s: Type '?' for help.\n", check_internal("version"));
  1044. X
  1045. X    (void) sprintf(buf, "folder %s %s", Flags.f_flags, mailfile);
  1046. X    if ((argv = mk_argv(buf, &argc, TRUE)) && argc > 0) {
  1047. X    if (folder(argc, argv, NULL) == -1 && isoff(glob_flags, DO_SHELL)) {
  1048. X        if (iscurses)
  1049. X        putchar('\n');
  1050. X        turnoff(glob_flags, IGN_SIGS), cleanup(0);
  1051. X    }
  1052. X#ifdef CURSES
  1053. X    if (iscurses)
  1054. X        (void) curses_help_msg(TRUE);
  1055. X#endif /* CURSES */
  1056. X    free_vec(argv);
  1057. X    }
  1058. X
  1059. X    if (hdrs_only) {
  1060. X    (void) sprintf(buf, "headers %s", hdrs_only);
  1061. X    if (argv = make_command(buf, TRPL_NULL, &argc))
  1062. X        (void) do_hdrs(argc, argv, NULL);
  1063. X    cleanup(0);
  1064. X    }
  1065. X
  1066. X    turnon(glob_flags, DO_SHELL);
  1067. X    if (istool && msg_cnt)
  1068. X    set_isread(current_msg);
  1069. X
  1070. X    /* finally, if the user wanted to source a file to execute, do it now */
  1071. X    if (Flags.src_file) {
  1072. X    char *s_argv[2];
  1073. X    s_argv[1] = Flags.src_file;
  1074. X    (void) source(2, s_argv);
  1075. X    if (!istool && Flags.src_n_exit)
  1076. X        cleanup(0);
  1077. X    }
  1078. X
  1079. X#ifdef SUNTOOL
  1080. X    if (istool) {
  1081. X    char buf[16];
  1082. X    n = 0;
  1083. X    (void) cmd_line(strcpy(buf, "set tool_help"), NULL);
  1084. X    if (time_out < 30)
  1085. X        time_out = 30;
  1086. X    turnoff(glob_flags, IGN_SIGS);
  1087. X    (void) do_hdrs(0, DUBL_NULL, NULL);
  1088. X    timerclear(&(mail_timer.it_interval));
  1089. X    timerclear(&(mail_timer.it_value));
  1090. X
  1091. X    /*  Reload time with value of timeout upon timer expiration. */
  1092. X    mail_timer.it_interval.tv_sec = time_out;
  1093. X
  1094. X    mail_timer.it_value.tv_sec = time_out;
  1095. X    (void) notify_set_itimer_func(tool, do_check,
  1096. X        ITIMER_REAL, &mail_timer, (struct itimerval *) 0);
  1097. X    timeout_cursors(FALSE);
  1098. X    turnoff(glob_flags, DO_UPDATE);
  1099. X    window_main_loop(tool);
  1100. X    cleanup(0);
  1101. X    }
  1102. X#endif /* SUNTOOL */
  1103. X    do_loop();
  1104. X}
  1105. X
  1106. Xdo_version()
  1107. X{
  1108. X    print("%s\n", check_internal("version"));
  1109. X    return -1;
  1110. X}
  1111. X
  1112. X/* set the current working directory */
  1113. Xset_cwd()
  1114. X{
  1115. X    char cwd[MAXPATHLEN];
  1116. X#ifndef SYSV
  1117. X    extern char *getwd();
  1118. X#else /* SYSV */
  1119. X    extern char *getcwd();
  1120. X#endif /* SYSV */
  1121. X
  1122. X#ifndef SYSV
  1123. X    if (getwd(cwd) == NULL)
  1124. X#else
  1125. X    if (getcwd(cwd, MAXPATHLEN) == NULL)
  1126. X#endif /* SYSV */
  1127. X    {
  1128. X    error("set_cwd: %s", cwd);
  1129. X    (void) un_set(&set_options, "cwd");
  1130. X    } else {
  1131. X    char *argv[4];
  1132. X    argv[0] = "cwd";
  1133. X    argv[1] = "=";
  1134. X    argv[2] = cwd;
  1135. X    argv[3] = NULL;
  1136. X    (void) add_option(&set_options, argv);
  1137. X    }
  1138. X}
  1139. END_OF_FILE
  1140. if test 8938 -ne `wc -c <'mush/main.c'`; then
  1141.     echo shar: \"'mush/main.c'\" unpacked with wrong size!
  1142. fi
  1143. # end of 'mush/main.c'
  1144. fi
  1145. if test -f 'mush/options.c' -a "${1}" != "-c" ; then 
  1146.   echo shar: Will not clobber existing file \"'mush/options.c'\"
  1147. else
  1148. echo shar: Extracting \"'mush/options.c'\" \(8816 characters\)
  1149. sed "s/^X//" >'mush/options.c' <<'END_OF_FILE'
  1150. X/* @(#)options.c    (c) copyright 10/10/88 (Dan Heller, Bart Schaefer) */
  1151. X
  1152. X#include "mush.h"
  1153. X#include "options.h"
  1154. X
  1155. X/*
  1156. X * NOTE:  Any word flag which is a prefix of another word flag must be
  1157. X *  listed AFTER the flag it prefixes in the list below
  1158. X */
  1159. X
  1160. Xchar *word_flags[][2] = {
  1161. X    { "-bcc",        "-b" },
  1162. X    { "-blindcarbon",    "-b" },
  1163. X    { "-blind",        "-b" },
  1164. X    { "-carbon",    "-c" },
  1165. X    { "-cc",        "-c" },
  1166. X    { "-copy",        "-c" },
  1167. X    { "-curses",    "-C" },
  1168. X    { "-debug",        "-d" },
  1169. X    { "-draft",        "-h" },
  1170. X    { "-echo",        "-e" },
  1171. X    { "-folder",    "-f" },    /* Maybe -file should become -f too? */
  1172. X    { "-file",        "-F" },    /* Don't really like -file for -F */
  1173. X    { "-headerfile",    "-h" },
  1174. X    { "-headers",    "-H" },
  1175. X    { "-initialize",    "-I" },
  1176. X    { "-init",        "-I" },
  1177. X    { "-interactive",    "-i" },
  1178. X    { "-interact",    "-i" },
  1179. X    { "-mailbox",    "-m" },
  1180. X    { "-message",    "-h" },
  1181. X    { "-noheaders",    "-N" },
  1182. X    { "-noinit",    "-n" },
  1183. X    { "-readonly",    "-r" },
  1184. X    { "-send",        "-U" },
  1185. X    { "-shell",        "-S" },
  1186. X    { "-source",    "-F" },    /* This is better for -F */
  1187. X    { "-subject",    "-s" },
  1188. X    { "-timeout",    "-T" },
  1189. X    { "-toolhelp",    "-2" },
  1190. X    { "-tool",        "-t" },
  1191. X    { "-user",        "-u" },
  1192. X    { "-verbose",    "-v" },
  1193. X    { "-visual",    "-C" },
  1194. X    { NULL,        NULL }    /* This must be the last entry */
  1195. X};
  1196. X
  1197. Xfix_word_flag(argp)
  1198. Xregister char **argp;
  1199. X{
  1200. X    int i;
  1201. X
  1202. X    Debug("%s --> ", *argp);
  1203. X    for (i = 0; word_flags[i][0]; i++) {
  1204. X    int len = strlen(word_flags[i][0]);
  1205. X    if (! strncmp(*argp, word_flags[i][0], len)) {
  1206. X        char buf[BUFSIZ], *p = buf;
  1207. X        p += Strcpy(buf, word_flags[i][1]);
  1208. X        (void) strcpy(p, *argp + len);
  1209. X        (void) strcpy(*argp, buf);
  1210. X    }
  1211. X    }
  1212. X    Debug("%s\n", *argp);
  1213. X}
  1214. X
  1215. X/*
  1216. X * preparse the command line to determine whether or not we're going
  1217. X * to bail out after checking that the user has no mail.  Also, check
  1218. X * to see if we're going to run a tool because it must be built first.
  1219. X */
  1220. Xpreparse_opts(argcp, argv)
  1221. Xregister int *argcp;    /* Pointer to argument count */
  1222. Xregister char **argv;    /* Argument vector */
  1223. X{
  1224. X    int n = FALSE;
  1225. X    char **args;
  1226. X
  1227. X#ifdef SUNTOOL
  1228. X    /* Note: we are assigning a boolean result to n and istool here */
  1229. X    if (n = istool = (strlen(prog_name) > 3 &&
  1230. X         (!strcmp(prog_name+strlen(prog_name)-4, "tool") ||
  1231. X          !strcmp(prog_name+strlen(prog_name)-4, "view")))) {
  1232. X    turnon(glob_flags, DO_SHELL);
  1233. X    parse_tool_opts(argcp, argv);
  1234. X    }
  1235. X#endif /* SUNTOOL */
  1236. X
  1237. X    if (!istool && *argcp > 1) {
  1238. X    for (args = argv+1; *args && args[0][0] == '-'; args++) {
  1239. X        int next = 1;
  1240. X        fix_word_flag(&args[0]);
  1241. XDoNext:
  1242. X        switch (args[0][next]) {
  1243. X#ifdef SUNTOOL
  1244. X        case 'T' :
  1245. X            if (args[1])
  1246. X            args++;
  1247. X        case 't' :
  1248. X            /* Note: we won't ever get here if started as
  1249. X             * "mushtool" or "mushview" because istool is true.
  1250. X             */
  1251. X            istool = 1;
  1252. X            parse_tool_opts(argcp, argv);
  1253. X            turnon(glob_flags, DO_SHELL);
  1254. X            return TRUE;
  1255. X            /* break; */
  1256. X#endif /* SUNTOOL */
  1257. X        case 'S' :
  1258. X            turnon(glob_flags, DO_SHELL);
  1259. X            n = TRUE;
  1260. X            break;
  1261. X        case 'f' :
  1262. X        case 'F' :
  1263. X        case 'h' :
  1264. X        case 'm' :
  1265. X        case 'u' :
  1266. X            n = TRUE;
  1267. X        case 'b' :
  1268. X        case 'c' :
  1269. X        case 'I' :
  1270. X        case 's' :
  1271. X            if (args[1]) {
  1272. X            args++;
  1273. X            next = 0;
  1274. X            }
  1275. X            break;
  1276. X        case 'H' :
  1277. X            if (args[0][next+1] == ':')
  1278. X            next = 0;
  1279. X            break;
  1280. X        case '\0':
  1281. X            next = 0;
  1282. X        default : ;
  1283. X        }
  1284. X        if (next) {
  1285. X        ++next;
  1286. X        goto DoNext;
  1287. X        }
  1288. X    }
  1289. X    if (*args) {  /* unused args indicates sending mail to someone */
  1290. X        n = TRUE;
  1291. X        if (!istool)
  1292. X        turnon(glob_flags, IS_SENDING);
  1293. X    }
  1294. X    }
  1295. X
  1296. X    return n;
  1297. X}
  1298. X
  1299. Xstatic char *usage_str =
  1300. X#ifdef SUNTOOL
  1301. X    "usage: %s [-t] [-C] [-i] [-f [folder] ] [-v] [-S] [-s subject] [users]\n";
  1302. X#else
  1303. X#ifdef CURSES
  1304. X    "usage: %s [-C] [-i] [-f [folder] ] [-v] [-S] [-s subject] [user list]\n";
  1305. X#else
  1306. X    "usage: %s [-i] [-f [folder] ] [-v] [-S] [-s subject] [user list]\n";
  1307. X#endif /* CURSES */
  1308. X#endif /* SUNTOOL */
  1309. X
  1310. Xparse_options(argvp, flags)
  1311. Xregister char ***argvp;
  1312. Xstruct mush_flags *flags;
  1313. X{
  1314. X    char buf[256];
  1315. X
  1316. X    bzero((char *) flags, sizeof (struct mush_flags));
  1317. X    flags->source_rc = TRUE;
  1318. X    flags->folder = "";
  1319. X
  1320. X    for (++(*argvp); **argvp && ***argvp == '-'; (*argvp)++) {
  1321. X    int look_again;
  1322. XDoLookAgain:
  1323. X    look_again = TRUE;
  1324. X    switch ((*argvp)[0][1]) {
  1325. X        case 'e':
  1326. X        /*
  1327. X         * don't set tty modes -- e.g. echo and cbreak modes aren't
  1328. X         * changed.
  1329. X         */
  1330. X        turnon(glob_flags, ECHO_FLAG);
  1331. X#ifdef CURSES
  1332. X        when 'C':
  1333. X        /* don't init curses -- don't even set iscurses.   */
  1334. X        if (istool) {
  1335. X            puts("-C: You are already running in tool mode");
  1336. X            turnoff(glob_flags, PRE_CURSES);
  1337. X        } else if (hdrs_only)
  1338. X            puts("headers only: ignoring -C flag");
  1339. X        else
  1340. X            turnon(glob_flags, PRE_CURSES);
  1341. X#endif /* CURSES */
  1342. X        when 'F':
  1343. X        flags->src_n_exit = ((*argvp)[0][2] == '!');
  1344. X        if (!(flags->src_file = *++(*argvp)))
  1345. X            puts("specify filename to source"), exit(1);
  1346. X        look_again = FALSE;
  1347. X        /* fall thru! */
  1348. X        case 'N':
  1349. X        (void) strcat(flags->f_flags, "-N ");
  1350. X        when 'r':
  1351. X        (void) strcat(flags->f_flags, "-r "); /* folder() argument */
  1352. X        when 'H':
  1353. X        if (istool) {
  1354. X            puts("running in tool-mode; -H option ignored.");
  1355. X            break;
  1356. X        }
  1357. X        turnoff(glob_flags, PRE_CURSES);
  1358. X        if (*(hdrs_only = (*(*argvp))+2) != ':')
  1359. X            hdrs_only = ":a";
  1360. X        else
  1361. X            look_again = FALSE;
  1362. X        /* read only cuz no updates */
  1363. X        (void) strcat(flags->f_flags, "-N -r ");
  1364. X        when 'i':
  1365. X        /* force interactive even if !isatty(0) */
  1366. X        turnoff(glob_flags, REDIRECT);
  1367. X        when 'u': /* specify a user's mailbox */
  1368. X        if (*(flags->folder))
  1369. X            puts("You can't specify more than one mailbox"), exit(1);
  1370. X#ifdef HOMEMAIL
  1371. X        {
  1372. X            char *p;
  1373. X            int isdir = 1;
  1374. X            (void) sprintf(buf, "%%%s",
  1375. X                (*argvp)[1] ? (*argvp)[1] : "root");
  1376. X            if ((p = getpath(buf, &isdir)) && !isdir)
  1377. X            strdup(flags->folder, p);
  1378. X            else if (isdir < 0)
  1379. X            puts(p), exit(1);
  1380. X            else if (isdir)
  1381. X            (void) printf("\"%s\" is a directory\n", p), exit(1);
  1382. X        }
  1383. X#else /* HOMEMAIL */
  1384. X        strdup(flags->folder, sprintf(buf, "%s/%s",
  1385. X                   MAILDIR, ((*argvp)[1])? (*argvp)[1] : "root"));
  1386. X#endif /* HOMEMAIL */
  1387. X        if ((*argvp)[1])
  1388. X            ++(*argvp);
  1389. X        look_again = FALSE;
  1390. X        when 'h':
  1391. X        if (istool)
  1392. X            puts("bad option when run as a tool"), exit(1);
  1393. X        if ((*argvp)[1])
  1394. X            flags->draft = *++(*argvp);
  1395. X        else
  1396. X            (void) printf("-h: missing file name.\n"), exit(1);
  1397. X        look_again = FALSE;
  1398. X        turnon(glob_flags, IS_SENDING);
  1399. X        when 'U':
  1400. X        if (istool)
  1401. X            puts("bad option when run as a tool"), exit(1);
  1402. X        turnon(flags->flg, SEND_NOW);
  1403. X        if ((*argvp)[0][2] == '!') {
  1404. X            turnon(flags->flg, NO_SIGN);
  1405. X            ++(**argvp);
  1406. X        }
  1407. X        when 'm':
  1408. X        if ((*argvp)[1])
  1409. X            strdup(spoolfile, *++(*argvp));
  1410. X        else
  1411. X            (void) printf("-m: missing mailbox name.\n"), exit(1);
  1412. X        look_again = FALSE;
  1413. X        when 'f':
  1414. X        if (*(flags->folder))
  1415. X            puts("You can't specify more than one mailbox"), exit(1);
  1416. X        if ((*argvp)[1]) {
  1417. X            strdup(flags->folder, *++(*argvp));
  1418. X            look_again = FALSE;
  1419. X        } else
  1420. X            strdup(flags->folder, "&");
  1421. X        when 's':
  1422. X        if (istool)
  1423. X            puts("bad option when run as a tool"), exit(1);
  1424. X        else if ((*argvp)[1])
  1425. X            flags->Subj = *++(*argvp);
  1426. X        else
  1427. X            puts("-s \"subject\""), exit(1);
  1428. X        look_again = FALSE;
  1429. X        when 'b':
  1430. X        if (istool)
  1431. X            puts("-b: bad option when run as a tool"), exit(1);
  1432. X        else if ((*argvp)[1])
  1433. X            flags->Bcc = *++(*argvp);
  1434. X        else
  1435. X            puts("-b \"bcc list\""), exit(1);
  1436. X        look_again = FALSE;
  1437. X        when 'c':
  1438. X        if (istool)
  1439. X            puts("-c: bad option when run as a tool"), exit(1);
  1440. X        else if ((*argvp)[1])
  1441. X            flags->Cc = *++(*argvp);
  1442. X        else
  1443. X            puts("-c \"cc list\""), exit(1);
  1444. X        look_again = FALSE;
  1445. X        break;
  1446. X#ifdef VERBOSE_ARG
  1447. X        case 'v':
  1448. X        if (istool)
  1449. X            puts("bad option when run as a tool"), exit(1);
  1450. X        turnon(flags->flg, VERBOSE);
  1451. X        break;
  1452. X#endif /* VERBOSE_ARG */
  1453. X#ifdef SUNTOOL
  1454. X        case 'T':
  1455. X        if ((time_out = atoi(*(*argvp))) <= 29)
  1456. X            time_out = 30;
  1457. X        look_again = FALSE;
  1458. X        /* -T implies -t */
  1459. X        case 't': istool = 1;
  1460. X#endif /* SUNTOOL */
  1461. X        case 'S': turnon(glob_flags, DO_SHELL);
  1462. X        when 'n':
  1463. X        if ((*argvp)[0][2] == '!') {
  1464. X            ++(**argvp);
  1465. X            flags->source_rc = -1;    /* No init files sourced */
  1466. X        } else
  1467. X            flags->source_rc = 0;    /* Only ~/.mushrc sourced */
  1468. X        when 'I':
  1469. X        if ((*argvp)[0][2] == '!' && flags->source_rc > 0)
  1470. X            flags->source_rc = 0;    /* Only ~/.mushrc sourced */
  1471. X        if (!(flags->init_file = *++(*argvp)))
  1472. X            puts("specify filename for init"), exit(1);
  1473. X        look_again = FALSE;
  1474. X        when 'd': debug = 1;
  1475. X        when '\0' : look_again = FALSE;
  1476. X        otherwise:
  1477. X        print("%s: unknown option: `%c'\n", prog_name,
  1478. X            (*argvp)[0][1]? (*argvp)[0][1] : '-');
  1479. X        print(usage_str, prog_name);
  1480. X    }
  1481. X    if (look_again && ++(**argvp) != '\0')
  1482. X        goto DoLookAgain;
  1483. X    }
  1484. X
  1485. X    if (ison(flags->flg, SEND_NOW) && !flags->draft) {
  1486. X    print("You must specify a draft file to autosend\n");
  1487. X    exit(1);
  1488. X    }
  1489. X}
  1490. END_OF_FILE
  1491. if test 8816 -ne `wc -c <'mush/options.c'`; then
  1492.     echo shar: \"'mush/options.c'\" unpacked with wrong size!
  1493. fi
  1494. # end of 'mush/options.c'
  1495. fi
  1496. if test -f 'mush/print.c' -a "${1}" != "-c" ; then 
  1497.   echo shar: Will not clobber existing file \"'mush/print.c'\"
  1498. else
  1499. echo shar: Extracting \"'mush/print.c'\" \(5410 characters\)
  1500. sed "s/^X//" >'mush/print.c' <<'END_OF_FILE'
  1501. X/* @(#)print.c    2.4    (c) copyright 10/15/86 (Dan Heller) */
  1502. X
  1503. X#include "mush.h"
  1504. X#include <varargs.h>
  1505. X
  1506. X/*ARGSUSED*/
  1507. X/*VARARGS1*/
  1508. Xvoid
  1509. Xerror(fmt, arg1, arg2, arg3, arg4)
  1510. Xregister char *fmt;
  1511. Xchar *arg1, *arg2, *arg3, *arg4;
  1512. X{
  1513. X    char buf[BUFSIZ];
  1514. X
  1515. X    (void) sprintf(buf, fmt, arg1, arg2, arg3, arg4);
  1516. X    sprintf(buf+strlen(buf), ": %s\n", sys_errlist[errno]);
  1517. X#ifdef SUNTOOL
  1518. X    if (istool > 1)
  1519. X    ok_box(buf);
  1520. X    else
  1521. X#endif /* SUNTOOL */
  1522. X    print(buf);
  1523. X}
  1524. X
  1525. X#if defined(SUNTOOL) || defined(CURSES)
  1526. X/*
  1527. X * print just like printf -- to a window, to curses, or to stdout.  Use vprintf
  1528. X * if available.  msgbuf is the buffer used to print into if necessary.
  1529. X * If you're running SUN3.2 or higher, the typecast (unsigned char *)msgbuf
  1530. X * (where indicated) otherwise, msgbuf is not typecast at all.
  1531. X */
  1532. X/*VARARGS*/
  1533. Xvoid
  1534. Xprint(va_alist)
  1535. Xva_dcl
  1536. X{
  1537. X    static char msgbuf[BUFSIZ];
  1538. X    char *fmt;
  1539. X    va_list args;
  1540. X#ifndef VPRINTF
  1541. X    FILE foo;
  1542. X#endif /* VPRINTF */
  1543. X    static int x; /* position on line saved for continued prints */
  1544. X    char *p; /* same type as struct file _ptr,_buf in stdio.h */
  1545. X
  1546. X#ifdef CURSES
  1547. X    if (iscurses) {
  1548. X    if (isoff(glob_flags, CONT_PRNT))
  1549. X        move(LINES-1, x = 0), refresh();
  1550. X    } else
  1551. X#endif /* CURSES */
  1552. X    if (istool < 2) {
  1553. X        va_start(args);
  1554. X        fmt = va_arg(args, char *);
  1555. X#ifdef VPRINTF
  1556. X        (void) vprintf(fmt, args);
  1557. X#else /* VPRINTF */
  1558. X        _doprnt(fmt, args, stdout);
  1559. X#endif /* VPRINTF */
  1560. X        va_end(args);
  1561. X        (void) fflush(stdout);
  1562. X        return;
  1563. X    }
  1564. X    va_start(args);
  1565. X    fmt = va_arg(args, char *);
  1566. X    if (fmt) {
  1567. X#ifdef VPRINTF
  1568. X    (void) vsprintf(msgbuf, fmt, args); /* NULL in fmt reprints last msg */
  1569. X#else /* VPRINTF */
  1570. X    foo._cnt = BUFSIZ;
  1571. X    foo._base = foo._ptr = msgbuf; /* may have to cast(unsigned char *) */
  1572. X    foo._flag = _IOWRT+_IOSTRG;
  1573. X    (void) _doprnt(fmt, args, &foo);
  1574. X    *foo._ptr = '\0'; /* plant terminating null character */
  1575. X#endif /* VPRINTF */
  1576. X    }
  1577. X    va_end(args);
  1578. X    if (istool) {
  1579. X    wprint("%s", msgbuf);
  1580. X    return;
  1581. X    }
  1582. X    p = msgbuf;
  1583. X    if (iscurses)
  1584. X    while (p = index(p, '\n'))
  1585. X        *p = ' ';
  1586. X#ifdef CURSES
  1587. X    if (iscurses) {
  1588. X    p = msgbuf;
  1589. X    for (;;) {
  1590. X        int len = COLS-1-x; /* space remaining at till the eol */
  1591. X        /* don't wrap the line! Just print it and refresh() */
  1592. X        printw("%-.*s", len, p), clrtoeol(), refresh();
  1593. X        /* if length(p) (remainder of msgbuf) doesn't wrap, break loop */
  1594. X        if ((x += strlen(p)) < COLS-1)
  1595. X        break;
  1596. X        /* the next print will overwrite bottom line, so \n first */
  1597. X        putchar('\n'), move(LINES-1, x = 0); /* reset x */
  1598. X        /* move p forward the number of chars we were able to display */
  1599. X        p += len;
  1600. X        turnon(glob_flags, CNTD_CMD); /* display ...continue... prompt */
  1601. X    }
  1602. X    turnoff(glob_flags, CONT_PRNT);
  1603. X    (void) fflush(stdout); /* some sys-v's aren't fflushing \n's */
  1604. X    return;
  1605. X    }
  1606. X#endif /* CURSES */
  1607. X}
  1608. X
  1609. X#endif /* SUNTOOL || CURSES */
  1610. X
  1611. X/* for curses mode */
  1612. Xclr_bot_line()
  1613. X{
  1614. X    print("");
  1615. X}
  1616. X
  1617. X#ifdef SUNTOOL
  1618. X
  1619. X/*
  1620. X * wprint prints stuff to a scrollable textsw.  This is intended for
  1621. X * print statements that need to be recalled since print() overwrites
  1622. X * its last message.  Messages are printed to whatever wprint_sw
  1623. X * currently points to.  If you set it to null, nothing prints.
  1624. X * For non-suntool mode, wprint() is just like printf().  For curses mode,
  1625. X * wprint() does -not- act like print() -- lines length is not counted
  1626. X * and newlines are not stripped.
  1627. X */
  1628. X/*VARARGS*/
  1629. Xvoid
  1630. Xwprint(va_alist)
  1631. Xva_dcl
  1632. X{
  1633. X#ifndef VPRINTF
  1634. X    FILE foo;
  1635. X#endif /* VPRINTF */
  1636. X    char msgbuf[BUFSIZ]; /* we're not getting huge strings */
  1637. X    char *fmt;
  1638. X    va_list args;
  1639. X
  1640. X    if (istool < 2) {
  1641. X    va_start(args);
  1642. X    fmt = va_arg(args, char *);
  1643. X#ifdef VPRINTF
  1644. X    (void) vprintf(fmt, args);
  1645. X#else /* VPRINTF */
  1646. X    _doprnt(fmt, args, stdout);
  1647. X#endif /* VPRINTF */
  1648. X    va_end(args);
  1649. X    (void) fflush(stdout);
  1650. X    return;
  1651. X    }
  1652. X    if (!compose_frame || !window_get(compose_frame, WIN_SHOW) || !cprint_sw)
  1653. X        wprint_sw = mfprint_sw;
  1654. X    else
  1655. X        wprint_sw = cprint_sw;
  1656. X
  1657. X    if (!wprint_sw)
  1658. X        return;
  1659. X    va_start(args);
  1660. X    fmt = va_arg(args, char *);
  1661. X    if (fmt) {
  1662. X#ifdef VPRINTF
  1663. X    (void) vsprintf(msgbuf, fmt, args); /* NULL in fmt reprints last msg */
  1664. X#else /* VPRINTF */
  1665. X    foo._cnt = BUFSIZ;
  1666. X    foo._base = foo._ptr = msgbuf; /* may have to cast (unsigned char *) */
  1667. X    foo._flag = _IOWRT+_IOSTRG;
  1668. X    _doprnt(fmt, args, &foo); /* format like printf into msgbuf via foo */
  1669. X    *foo._ptr = '\0'; /* plant terminating null character */
  1670. X#endif /* VPRINTF */
  1671. X    textsw_insert(wprint_sw, msgbuf, strlen(msgbuf));
  1672. X    }
  1673. X    va_end(args);
  1674. X}
  1675. X
  1676. X#include <sundev/kbio.h>
  1677. X#include <sundev/kbd.h>
  1678. X
  1679. Xbell()
  1680. X{
  1681. X#ifdef KIOCCMD
  1682. X    if (istool) {
  1683. X    int kbd = open("/dev/kbd", O_WRONLY, 0);
  1684. X    struct timeval timer;
  1685. X    timer.tv_sec = 0;
  1686. X    timer.tv_usec = 100000;
  1687. X    if (kbd != -1) {
  1688. X        int cmd = KBD_CMD_BELL;
  1689. X        (void) ioctl(kbd, KIOCCMD, &cmd);
  1690. X        (void) select(32, (fd_set *) 0,(fd_set *) 0,(fd_set *) 0, &timer);
  1691. X        cmd = KBD_CMD_NOBELL;
  1692. X        (void) ioctl(kbd, KIOCCMD, &cmd);
  1693. X        (void) close(kbd);
  1694. X    }
  1695. X    } else
  1696. X#endif /* KIOCCMD */
  1697. X    (void) fputc('\007', stderr), (void) fflush(stderr);
  1698. X    return 0;
  1699. X}
  1700. X
  1701. X#endif /* SUNTOOL */
  1702. X
  1703. X#ifdef BSD
  1704. X#undef fputs
  1705. X
  1706. X/*
  1707. X * The standard 4.x BSD fputs() does not return any useful value.
  1708. X * For compatibility with Sun and SysV fputs(), we use this wrapper.
  1709. X */
  1710. X
  1711. XFputs(line, fp)
  1712. Xchar *line;
  1713. XFILE *fp;
  1714. X{
  1715. X    clearerr(fp);
  1716. X    (void) fputs(line, fp);
  1717. X    if (ferror(fp))
  1718. X    return EOF;
  1719. X    return 0;
  1720. X}
  1721. X
  1722. X#endif /* BSD */
  1723. END_OF_FILE
  1724. if test 5410 -ne `wc -c <'mush/print.c'`; then
  1725.     echo shar: \"'mush/print.c'\" unpacked with wrong size!
  1726. fi
  1727. # end of 'mush/print.c'
  1728. fi
  1729. if test -f 'mush/sort.c' -a "${1}" != "-c" ; then 
  1730.   echo shar: Will not clobber existing file \"'mush/sort.c'\"
  1731. else
  1732. echo shar: Extracting \"'mush/sort.c'\" \(8063 characters\)
  1733. sed "s/^X//" >'mush/sort.c' <<'END_OF_FILE'
  1734. X/* sort.c 3.0    (c) copyright 1986,1990 (Dan Heller) */
  1735. X
  1736. X#include "mush.h"
  1737. X/* #define MYQSORT */
  1738. X
  1739. X/* The size of this array should really be bounded by
  1740. X * 2 spaces for each possible different sort criteria
  1741. X * (one space for each key letter and one per for 'r'),
  1742. X * but 16 leaves room to add to the current list.
  1743. X */
  1744. Xstatic char subsort[16];
  1745. X
  1746. Xstatic int depth, order, ignore_case;
  1747. Xstatic jmp_buf sortbuf;
  1748. X
  1749. Xsort(argc, argv, list)
  1750. Xregister int argc;
  1751. Xregister char *argv[], list[];
  1752. X{
  1753. X    int msg_cmp();
  1754. X    SIGRET (*oldint)(), (*oldquit)();
  1755. X    int n, offset = -1, range = 0;
  1756. X    long curr_msg_off = msg[current_msg].m_offset;
  1757. X
  1758. X    depth = 0, order = 1, ignore_case = FALSE;
  1759. X
  1760. X    while (argc && *++argv) {
  1761. X    n = (argv[0][0] == '-' && argv[0][1] != 0);
  1762. X    while (argv[0][n]) {
  1763. X        if (depth > sizeof subsort - 2)
  1764. X        break;
  1765. X        switch(argv[0][n]) {
  1766. X        case '-': /* reverse order of next criteria (obsolete) */
  1767. X            argv[0][n] = 'r'; /* fix it and fall through */
  1768. X        case 'r': /* reverse order of next criteria */
  1769. X        case 'd': /* sort by date */
  1770. X        case 'a': /* sort by author (address) */
  1771. X        case 's': /* sort by subject (ignore Re:) */
  1772. X        case 'R': /* sort by subject including Re: */
  1773. X        case 'l': /* sort by length in bytes */
  1774. X        case 'S': /* sort by message status */
  1775. X            /* skip consecutive repeats of the same flag */
  1776. X            if (subsort[depth-1] != argv[0][n])
  1777. X            subsort[depth++] = argv[0][n];
  1778. X        when 'i': ignore_case = TRUE;
  1779. X        otherwise: return help(0, "sort", cmd_help);
  1780. X        }
  1781. X        n++;
  1782. X    }
  1783. X    }
  1784. X    if (depth == 0 || subsort[depth-1] == 'r')
  1785. X    subsort[depth++] = 'S'; /* status sort is the default */
  1786. X    subsort[depth] = 0;
  1787. X    depth = 0;    /* start at the beginning */
  1788. X    if (msg_cnt <= 1) {
  1789. X    print("Not enough messages to sort.\n");
  1790. X    return -1;
  1791. X    }
  1792. X    turnon(glob_flags, IGN_SIGS);
  1793. X    on_intr();
  1794. X
  1795. X    if (list && ison(glob_flags, IS_PIPE)) {
  1796. X    for (n = 0; n < msg_cnt; n++)
  1797. X        if (msg_bit(list, n)) {
  1798. X        if (offset < 0)
  1799. X            offset = n;
  1800. X        range++;
  1801. X        } else if (offset >= 0)
  1802. X        break;
  1803. X    } else
  1804. X    offset = 0, range = msg_cnt;
  1805. X
  1806. X    if (range < 2)
  1807. X    print("Range not broad enough to sort anything\n");
  1808. X    else {
  1809. X    Debug("Sorting %d messages starting at message %d\n", range, offset+1);
  1810. X
  1811. X    if (setjmp(sortbuf) == 0)
  1812. X        qsort((char *)&msg[offset], range, sizeof (struct msg), msg_cmp);
  1813. X    else
  1814. X        print("WARNING: Sorting interrupted: unpredictable order.\n");
  1815. X    turnon(glob_flags, DO_UPDATE);
  1816. X    }
  1817. X    for (n = 0; n < msg_cnt; n++)
  1818. X    if (msg[n].m_offset == curr_msg_off)
  1819. X        break;
  1820. X    current_msg = n;
  1821. X    turnoff(glob_flags, IGN_SIGS);
  1822. X    off_intr();
  1823. X    /* Break pipes because message lists are invalid */
  1824. X    return 0 - in_pipe();
  1825. X}
  1826. X
  1827. X#ifdef MYQSORT
  1828. Xqsort(base, len, siz, compar)
  1829. Xregister struct msg *base;
  1830. Xint (*compar)();
  1831. X{
  1832. X     register int i, swapping;
  1833. X     struct msg temp;
  1834. X
  1835. X     do  {
  1836. X     swapping = 0;
  1837. X     for (i = 0; i < len-1; ++i) {
  1838. X         if (compar(base+i, base+i+1) > 0) {
  1839. X         temp = base[i];
  1840. X         base[i] = base[i+1];
  1841. X         base[i+1] = temp;
  1842. X         swapping = 1;
  1843. X         }
  1844. X     }
  1845. X     } while (swapping);
  1846. X}
  1847. X#endif /* MYSORT */
  1848. X
  1849. Xstatus_cmp(msg1, msg2)
  1850. Xregister struct msg *msg1, *msg2;
  1851. X{
  1852. X    if (msg1->m_flags == msg2->m_flags)
  1853. X    return msg_cmp(msg1, msg2);
  1854. X    if (ison(msg1->m_flags, DELETE) && isoff(msg2->m_flags, DELETE))
  1855. X    return order;
  1856. X    if (isoff(msg1->m_flags, DELETE) && ison(msg2->m_flags, DELETE))
  1857. X    return -order;
  1858. X    if (isoff(msg1->m_flags, OLD) && ison(msg2->m_flags, OLD))
  1859. X    return -order;
  1860. X    if (ison(msg1->m_flags, OLD) && isoff(msg2->m_flags, OLD))
  1861. X    return order;
  1862. X    if (ison(msg1->m_flags, UNREAD) && isoff(msg2->m_flags, UNREAD))
  1863. X    return -order;
  1864. X    if (isoff(msg1->m_flags, UNREAD) && ison(msg2->m_flags, UNREAD))
  1865. X    return order;
  1866. X    if (ison(msg1->m_flags,PRESERVE) && isoff(msg2->m_flags,PRESERVE))
  1867. X    return -order;
  1868. X    if (isoff(msg1->m_flags,PRESERVE) && ison(msg2->m_flags,PRESERVE))
  1869. X    return order;
  1870. X    if (ison(msg1->m_flags,REPLIED) && isoff(msg2->m_flags,REPLIED))
  1871. X    return -order;
  1872. X    if (isoff(msg1->m_flags,REPLIED) && ison(msg2->m_flags,REPLIED))
  1873. X    return order;
  1874. X    if (ison(msg1->m_flags,SAVED) && isoff(msg2->m_flags,SAVED))
  1875. X    return -order;
  1876. X    if (isoff(msg1->m_flags,SAVED) && ison(msg2->m_flags,SAVED))
  1877. X    return order;
  1878. X    if (ison(msg1->m_flags,PRINTED) && isoff(msg2->m_flags,PRINTED))
  1879. X    return -order;
  1880. X    if (isoff(msg1->m_flags,PRINTED) && ison(msg2->m_flags,PRINTED))
  1881. X    return order;
  1882. X    if (ison(msg1->m_flags,FORWARD) && isoff(msg2->m_flags,FORWARD))
  1883. X    return -order;
  1884. X    if (isoff(msg1->m_flags,FORWARD) && ison(msg2->m_flags,FORWARD))
  1885. X    return order;
  1886. X
  1887. X    return order;
  1888. X}
  1889. X
  1890. Xauthor_cmp(msg1, msg2)
  1891. Xregister struct msg *msg1, *msg2;
  1892. X{
  1893. X    char buf1[HDRSIZ], buf2[HDRSIZ];
  1894. X    int retval;
  1895. X
  1896. X    (void) reply_to(msg1 - msg, 0, buf1); /* "0" for "author only" */
  1897. X    (void) reply_to(msg2 - msg, 0, buf2);
  1898. X    Debug("author: msg %d: %s, msg %d: %s\n", msg1-msg, buf1, msg2-msg, buf2);
  1899. X    if (ignore_case)
  1900. X    retval = lcase_strncmp(buf1, buf2, -1) * order;
  1901. X    else
  1902. X    retval = strcmp(buf1, buf2) * order;
  1903. X    return retval ? retval : msg_cmp(msg1, msg2);
  1904. X}
  1905. X
  1906. X/* compare messages according to size (length) */
  1907. Xsize_cmp(msg1, msg2)
  1908. Xregister struct msg *msg1, *msg2;
  1909. X{
  1910. X    int retval;
  1911. X
  1912. X    Debug("sizes: (%d): %d, (%d): %d\"\n",
  1913. X    msg1-msg, msg1->m_size, msg2-msg, msg2->m_size);
  1914. X    if (retval = (msg1->m_size - msg2->m_size) * order) /* assign and test */
  1915. X    return retval;
  1916. X    return msg_cmp(msg1, msg2);
  1917. X}
  1918. X
  1919. X/*
  1920. X * Subject comparison ignoring Re:  subject_to() appends an Re: if there is
  1921. X * any subject whatsoever.
  1922. X */
  1923. Xsubject_cmp(msg1, msg2)
  1924. Xregister struct msg *msg1, *msg2;
  1925. X{
  1926. X    char buf1[HDRSIZ], buf2[HDRSIZ];
  1927. X    register char *p1, *p2;
  1928. X    int retval;
  1929. X
  1930. X    p1 = subject_to(msg1 - msg, buf1);
  1931. X    p2 = subject_to(msg2 - msg, buf2);
  1932. X    if (p1) {
  1933. X    p1 += 4;
  1934. X    while (isspace(*p1))
  1935. X        p1++;
  1936. X    } else
  1937. X    p1 = buf1; /* subject_to() makes it an empty string */
  1938. X    if (p2) {
  1939. X    p2 += 4;
  1940. X    while (isspace(*p2))
  1941. X        p2++;
  1942. X    } else
  1943. X    p2 = buf2; /* subject_to() makes it an empty string */
  1944. X    Debug("subjects: (%d): \"%s\" (%d): \"%s\"\n", msg1-msg, p1, msg2-msg, p2);
  1945. X    if (ignore_case)
  1946. X    retval = lcase_strncmp(p1, p2, -1) * order;
  1947. X    else
  1948. X    retval = strcmp(p1, p2) * order;
  1949. X    return retval ? retval : msg_cmp(msg1, msg2);
  1950. X}
  1951. X
  1952. X/*
  1953. X * compare subject strings from two messages.
  1954. X * If Re is appended, so be it -- if user wants to ignore Re: use 'R' flag.
  1955. X */
  1956. Xsubj_with_re(msg1, msg2)
  1957. Xregister struct msg *msg1, *msg2;
  1958. X{
  1959. X    char buf1[HDRSIZ], buf2[HDRSIZ], *p;
  1960. X    int retval;
  1961. X
  1962. X    if (!(p = header_field(msg1 - msg, "subject")))
  1963. X    p = "";
  1964. X    (void) strcpy(buf1, p);
  1965. X    if (!(p = header_field(msg2 - msg, "subject")))
  1966. X    p = "";
  1967. X    (void) strcpy(buf2, p);
  1968. X    Debug("subjects: (%d): \"%s\" (%d): \"%s\"\n",
  1969. X    msg1-msg, buf1, msg2-msg, buf2);
  1970. X    if (ignore_case)
  1971. X    retval = lcase_strncmp(buf1, buf2, -1) * order;
  1972. X    else
  1973. X    retval = strcmp(buf1, buf2) * order;
  1974. X    return retval ? retval : msg_cmp(msg1, msg2);
  1975. X}
  1976. X
  1977. Xdate_cmp(msg1, msg2)
  1978. Xregister struct msg *msg1, *msg2;
  1979. X{
  1980. X    int retval;
  1981. X    long tm1, tm2;
  1982. X
  1983. X    if (ison(glob_flags, DATE_RECV)) {
  1984. X    (void) sscanf(msg1->m_date_recv, "%ld", &tm1);
  1985. X    (void) sscanf(msg2->m_date_recv, "%ld", &tm2);
  1986. X    } else {
  1987. X    (void) sscanf(msg1->m_date_sent, "%ld", &tm1);
  1988. X    (void) sscanf(msg2->m_date_sent, "%ld", &tm2);
  1989. X    }
  1990. X    return tm1 < tm2 ? -order : (tm1 > tm2) ? order : msg_cmp(msg1, msg2);
  1991. X}
  1992. X
  1993. Xstatic
  1994. Xmsg_cmp(msg1, msg2)
  1995. Xregister struct msg *msg1, *msg2;
  1996. X{
  1997. X    int sv_order = order, sv_depth = depth, retval = 0;
  1998. X
  1999. X    if (ison(glob_flags, WAS_INTR))
  2000. X    longjmp(sortbuf, 1);
  2001. X    if (msg1 < msg || msg2 < msg) {
  2002. X    wprint("sort botch trying to sort %d and %d using %s\n",
  2003. X        msg1-msg, msg2-msg, subsort);
  2004. X    return 0;
  2005. X    }
  2006. X
  2007. X    if (subsort[depth] == 'r') {
  2008. X    order = -1;
  2009. X    depth++;
  2010. X    } else
  2011. X    order = 1;
  2012. X    switch(subsort[depth++]) {
  2013. X    case '\0': retval = 0;
  2014. X    when 'd': retval = date_cmp(msg1, msg2);
  2015. X    when 'a': retval = author_cmp(msg1, msg2);
  2016. X    when 's': retval = subject_cmp(msg1, msg2);
  2017. X    when 'R': retval = subj_with_re(msg1, msg2);
  2018. X    when 'l': retval = size_cmp(msg1, msg2); /* length compare */
  2019. X    otherwise: retval = status_cmp(msg1, msg2);
  2020. X    }
  2021. X    depth = sv_depth;
  2022. X    order = sv_order;
  2023. X    return retval;
  2024. X}
  2025. END_OF_FILE
  2026. if test 8063 -ne `wc -c <'mush/sort.c'`; then
  2027.     echo shar: \"'mush/sort.c'\" unpacked with wrong size!
  2028. fi
  2029. # end of 'mush/sort.c'
  2030. fi
  2031. echo shar: End of archive 18 \(of 19\).
  2032. cp /dev/null ark18isdone
  2033. MISSING=""
  2034. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  2035.     if test ! -f ark${I}isdone ; then
  2036.     MISSING="${MISSING} ${I}"
  2037.     fi
  2038. done
  2039. if test "${MISSING}" = "" ; then
  2040.     echo You have unpacked all 19 archives.
  2041.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2042. else
  2043.     echo You still need to unpack the following archives:
  2044.     echo "        " ${MISSING}
  2045. fi
  2046. ##  End of shell archive.
  2047. exit 0
  2048.  
  2049.  
  2050.