home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume18 / mush / part11 < prev    next >
Internet Message Format  |  1991-04-22  |  51KB

  1. From: argv@zipcode.com (Dan Heller)
  2. Newsgroups: comp.sources.misc
  3. Subject: v18i068:  mush - Mail User's Shell, Part11/22
  4. Message-ID: <1991Apr22.000352.18801@sparky.IMD.Sterling.COM>
  5. Date: 22 Apr 91 00:03:52 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: c9edc553 515fddfb a3b7e3e6 e79d52a3
  8.  
  9. Submitted-by: Dan Heller <argv@zipcode.com>
  10. Posting-number: Volume 18, Issue 68
  11. Archive-name: mush/part11
  12. Supersedes: mush: Volume 12, Issue 28-47
  13.  
  14. #!/bin/sh
  15. # do not concatenate these parts, unpack them in order with /bin/sh
  16. # file loop.c continued
  17. #
  18. if test ! -r _shar_seq_.tmp; then
  19.     echo 'Please unpack part 1 first!'
  20.     exit 1
  21. fi
  22. (read Scheck
  23.  if test "$Scheck" != 11; then
  24.     echo Please unpack part "$Scheck" next!
  25.     exit 1
  26.  else
  27.     exit 0
  28.  fi
  29. ) < _shar_seq_.tmp || exit 1
  30. if test ! -f _shar_wnt_.tmp; then
  31.     echo 'x - still skipping loop.c'
  32. else
  33. echo 'x - continuing file loop.c'
  34. sed 's/^X//' << 'SHAR_EOF' >> 'loop.c' &&
  35. X    /* If final is true, do variable expansions first */
  36. X    if (final) {
  37. X    (void) strcpy(buf, str);
  38. X    str = buf;
  39. X    if (!variable_expand(str))
  40. X        return DUBL_NULL;
  41. X    }
  42. X    *argc = 0;
  43. X    while (*str && *argc < MAXARGS) {
  44. X    while (isspace(*str))
  45. X        ++str;
  46. X    /* When we have hit an unquoted `;', final must be true,
  47. X     * so we're finished.  Stuff the rest of the string at the
  48. X     * end of the argv -- do_command will pass it back later,
  49. X     * for further processing -- and break out of the loop.
  50. X     * NOTE: *s is not yet valid the first time through this
  51. X     * loop, so unq_sep should always be initialized to 0.
  52. X     */
  53. X    if (unq_sep && s && *s == ';') {
  54. X        if (*str) { /* Don't bother saving a null string */
  55. X        newargv[*argc] = savestr(str);
  56. X        (*argc)++;
  57. X        }
  58. X        break;
  59. X    }
  60. X    if (*str) {        /* found beginning of a word */
  61. X        unq_sep = 0;    /* innocent until proven guilty */
  62. X        s = p = str;
  63. X        do  {
  64. X        if (p - s >= sizeof buf-1) {
  65. X            print("argument list too long.\n");
  66. X            return DUBL_NULL;
  67. X        }
  68. X        if (*str == ';' || *str == '|')
  69. X            unq_sep = final; /* Mark an unquoted separator */
  70. X        if ((*p = *str++) == '\\') {
  71. X            if (final && (*str == ';' || *str == '|'))
  72. X            --p; /* Back up to overwrite the backslash */
  73. X            if (*++p = *str) /* assign and compare to NUL */
  74. X            str++;
  75. X            continue;
  76. X        }
  77. X        if (p2 = index("\"'", *p)) {
  78. X            register char c2 = *p2;
  79. X            /* you can't escape quotes inside quotes of the same type */
  80. X            if (!(p2 = index(str, c2))) {
  81. X            if (final)
  82. X                print("Unmatched %c.\n", c2);
  83. X            err++;
  84. X            p2 = str;
  85. X            }
  86. X            /* This is the intent of the following loop:
  87. X             *  tmp = (int)(p2 - str) + 1;
  88. X             *  (void) strncpy(p + !final, str, tmp);
  89. X             * The strncpy() can't be used directly because
  90. X             * it may be overlapping, which fails sometimes.
  91. X             */
  92. X            /* copy up to and including quote */
  93. X            for (tmp = 0; tmp < (int)(p2 - str) + 1; tmp++)
  94. X            p[tmp+!final] = str[tmp];
  95. X            p += tmp - 2 * !!final; /* change final to a boolean */
  96. X            if (*(str = p2))
  97. X            str++;
  98. X        }
  99. X        } while (++p, *str && (!isdelimeter(*str) || str[-1] == '\\'));
  100. X        if (c = *str) /* set c = *str, check for null */
  101. X        str++;
  102. X        *p = 0;
  103. X        if (*s) {
  104. X        /* To differentiate real separators from quoted or
  105. X         * escaped ones, always store 3 chars:
  106. X         *  1) The separator character
  107. X         *  2) A nul (string terminator)
  108. X         *  3) An additional boolean (0 or 1)
  109. X         * The boolean is checked by do_command.  Note that this
  110. X         * applies only to "solitary" separators, i.e. those not
  111. X         * part of a larger word.
  112. X         */
  113. X        if (final && (!strcmp(s, ";") || !strcmp(s, "|"))) {
  114. X            char *sep = savestr("xx"); /* get 3 char slots */
  115. X            sep[0] = *s, sep[1] = '\0', sep[2] = unq_sep;
  116. X            newargv[*argc] = sep;
  117. X        } else
  118. X            newargv[*argc] = savestr(s);
  119. X        (*argc)++;
  120. X        }
  121. X        *p = c;
  122. X    }
  123. X    }
  124. X    if (!*argc)
  125. X    return DUBL_NULL;
  126. X    /* newargv[*argc] = NULL; */
  127. X    if (!(argv = (char **)calloc((unsigned)((*argc)+1), sizeof(char *)))) {
  128. X    perror("mk_argv: calloc");
  129. X    return DUBL_NULL;
  130. X    }
  131. X    for (tmp = 0; tmp < *argc; tmp++)
  132. X    argv[tmp] = newargv[tmp];
  133. X    if (err)
  134. X    *argc = -1;
  135. X    else if (debug > 3)
  136. X    (void) printf("Made argv: "), print_argv(argv);
  137. X    return argv;
  138. }
  139. X
  140. /*
  141. X * Report a history parsing error.
  142. X * Suppress the message if nonobang is true.
  143. X */
  144. #define hist_error    if (nonobang) {;} else print
  145. X
  146. /*
  147. X * reference previous history from syntax of str and place result into buf
  148. X * We know we've got a history reference -- we're passed the string starting
  149. X * the first char AFTER the '!' (which indicates history reference)
  150. X */
  151. char *
  152. reference_hist(str, buf, hist_ref)
  153. register char *str, **hist_ref;
  154. char buf[];
  155. {
  156. X    int        relative; /* relative from current hist_no */
  157. X    int        old_hist, argstart = 0, lastarg, argend = 0, n = 0;
  158. X    register char  *p, *rb = NULL, **argv = hist_ref;
  159. X    struct history *hist;
  160. X
  161. X    buf[0] = 0;
  162. X    if (*str == '{')
  163. X    if (!(rb = index(str, '}'))) {   /* { */
  164. X        hist_error("Unmatched '}'");
  165. X        return NULL;
  166. X    } else
  167. X        *rb = 0, ++str;
  168. X    relative = *str == '-';
  169. X    if (index("!:$*", *str)) {
  170. X    old_hist = hist_no;
  171. X    if (*str == '!')
  172. X        str++;
  173. X    } else if (isdigit(*(str + relative)))
  174. X    str = my_atoi(str + relative, &old_hist);
  175. X    else if (!(p = hist_from_str(str, &old_hist))) {
  176. X    if (rb) /* { */
  177. X        *rb = '}';
  178. X    return NULL;
  179. X    } else
  180. X    str = p;
  181. X    if (relative)
  182. X    old_hist = (hist_no - old_hist) + 1;
  183. X    if (old_hist == hist_no) {
  184. X    if (!(argv = hist_ref))
  185. X        hist_error("You haven't done anything yet!\n");
  186. X    } else {
  187. X    if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
  188. X        old_hist <= 0) {
  189. X        if (old_hist <= 0)
  190. X        hist_error("You haven't done that many commands, yet.\n");
  191. X        else
  192. X        hist_error("Event %d %s.\n", old_hist,
  193. X            (old_hist > hist_no)? "hasn't happened yet": "expired");
  194. X        if (rb) /* { */
  195. X        *rb = '}';
  196. X        return NULL;
  197. X    }
  198. X    hist = hist_head;
  199. X    while (hist && hist->histno != old_hist)
  200. X        hist = hist->prev;
  201. X    if (hist)
  202. X        argv = hist->argv;
  203. X    }
  204. X    if (!argv) {
  205. X    if (rb) /* { */
  206. X        *rb = '}';
  207. X    return NULL;
  208. X    }
  209. X    while (argv[argend+1])
  210. X    argend++;
  211. X    lastarg = argend;
  212. X    if (*str && index(":$*-", *str)) {
  213. X    int isrange;
  214. X    if (*str == ':' && isdigit(*++str))
  215. X        str = my_atoi(str, &argstart);
  216. X    if (isrange = (*str == '-'))
  217. X        str++;
  218. X    if (!isdigit(*str)) {
  219. X        if (*str == 'p')
  220. X        str++, print_only = 1;
  221. X        else if (*str == '*') {
  222. X        str++;
  223. X        if (!isrange) {
  224. X            if (argv[0]) {
  225. X            if (argv[1])
  226. X                argstart = 1;
  227. X            else {
  228. X                if (rb) /* { */
  229. X                *rb = '}';
  230. X                return (rb ? rb + 1 : str);
  231. X            }
  232. X            } else
  233. X            argstart = 0;
  234. X        }
  235. X        } else if (*str == '$') {
  236. X        if (!isrange)
  237. X            argstart = argend;
  238. X        str++;
  239. X        } else if (isrange && argend > argstart)
  240. X        argend--; /* unspecified end of range implies last-1 arg */
  241. X        else 
  242. X        argend = argstart; /* no range specified; use arg given */
  243. X    } else
  244. X        str = my_atoi(str, &argend);
  245. X    }
  246. X    if (argstart > lastarg || argend > lastarg || argstart > argend) {
  247. X    hist_error("Bad argument selector.\n");
  248. X    if (rb) /* { */
  249. X        *rb = '}';
  250. X    return (nonobang ? rb ? rb + 1 : str : NULL);
  251. X    }
  252. X    if (debug > 3)
  253. X    print("history expanding from "), print_argv(argv);
  254. X    while (argstart <= argend) {
  255. X    n += Strcpy(&buf[n], argv[argstart++]);
  256. X    buf[n++] = ' ';
  257. X    }
  258. X    buf[--n] = 0;
  259. X    if (rb) /* { */
  260. X    *rb = '}';
  261. X    return (rb ? rb + 1 : str);
  262. }
  263. X
  264. /* find a history command that contains the string "str"
  265. X * place that history number in "hist" and return the end of the string
  266. X * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
  267. X * in the second example, return the pointer to "bar"
  268. X */
  269. char *
  270. hist_from_str(str, hist_number)
  271. register char *str;
  272. register int *hist_number;
  273. {
  274. X    register char *p = NULL, c = 0;
  275. X    int       full_search = 0, len, found;
  276. X    char       buf[BUFSIZ];
  277. X    struct history *hist;
  278. #ifndef REGCMP
  279. X    extern char   *re_comp();
  280. #else
  281. X    char *rex = NULL;
  282. X    extern char   *regcmp();
  283. #endif /* REGCMP */
  284. X
  285. X    /* For !{something}, the {} are stripped in reference_hist() */
  286. X    if (*str == '?') {
  287. X    if (p = index(++str, '?'))
  288. X        c = *p, *p = 0;
  289. X    else
  290. X        p = str + strlen(str);
  291. X    full_search = 1;
  292. X    } else {
  293. X    p = str;
  294. X    while (*p && *p != ':' && !isspace(*p))
  295. X        p++;
  296. X    c = *p, *p = 0;
  297. X    }
  298. X    if (*str) {
  299. #ifndef REGCMP
  300. X    if (re_comp(str))
  301. #else
  302. X    if (!(rex = regcmp(str, NULL)))    /* Assign and test */
  303. #endif /* REGCMP */
  304. X    {
  305. X        if (c)
  306. X        *p = c;
  307. X        return NULL;
  308. X    }
  309. X    } else {
  310. X    *hist_number = hist_no;
  311. X    if (c)
  312. X        *p = c;
  313. X    return (*p == '?' ? p + 1 : p);
  314. X    }
  315. X    len = strlen(str);
  316. X    /* move thru the history in reverse searching for a string match. */
  317. X    for (hist = hist_head; hist; hist = hist->prev) {
  318. X    if (full_search) {
  319. X        (void) argv_to_string(buf, hist->argv);
  320. X        Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
  321. X    }
  322. X    if (!full_search) {
  323. X        (void) strcpy(buf, hist->argv[0]);
  324. X        Debug("Checking for (%s) in (#%d: %*s)\n",
  325. X        str, hist->histno, len, buf);
  326. X        found = !strncmp(buf, str, len);
  327. X    } else
  328. X        found =
  329. #ifndef REGCMP
  330. X        re_exec(buf)
  331. #else
  332. X        !!regex(rex, buf, NULL) /* convert to boolean value */
  333. #endif /* REGCMP */
  334. X                == 1;
  335. X    if (found) {
  336. X        *hist_number = hist->histno;
  337. X        Debug("Found it in history #%d\n", *hist_number);
  338. X        *p = c;
  339. X        return (*p == '?' ? p + 1 : p);
  340. X    }
  341. X    }
  342. X    hist_error("%s: event not found\n", str);
  343. X    *p = c;
  344. X    return NULL;
  345. }
  346. X
  347. disp_hist(n, argv)  /* argc not used -- use space for the variable, "n" */
  348. register int n;
  349. char **argv;
  350. {
  351. X    register int    list_num = TRUE, num_of_hists = hist_size;
  352. X    register int    reverse = FALSE;
  353. X    struct history    *hist = hist_tail;
  354. X
  355. X    while (*++argv && *argv[0] == '-') {
  356. X    n = 1;
  357. X    do  switch(argv[0][n]) {
  358. X        case 'h': list_num = FALSE;
  359. X        when 'r': reverse = TRUE; hist = hist_head;
  360. X        otherwise: return help(0, "history", cmd_help);
  361. X        }
  362. X    while (argv[0][++n]);
  363. X    }
  364. X
  365. X    if (!hist) {
  366. X    print("No history yet.\n");
  367. X    return -1;
  368. X    }
  369. X    if (*argv)
  370. X    if (!isdigit(**argv)) {
  371. X        print("history: badly formed number\n");
  372. X        return -1;
  373. X    } else
  374. X        num_of_hists = atoi(*argv);
  375. X
  376. X    if (num_of_hists > hist_size || num_of_hists > hist_no)
  377. X    num_of_hists = min(hist_size, hist_no);
  378. X
  379. X    if (!reverse)
  380. X    while (hist_no - hist->histno >= num_of_hists) {
  381. X        Debug("skipping %d\n", hist->histno);
  382. X        hist = hist->next;
  383. X    }
  384. X
  385. X    (void) do_pager(NULL, TRUE);
  386. X    for (n = 0; n < num_of_hists && hist; n++) {
  387. X    char buf[256];
  388. X    if (list_num)
  389. X        (void) do_pager(sprintf(buf, "%4.d  ", hist->histno), FALSE);
  390. X    (void) argv_to_string(buf, hist->argv);
  391. X    (void) do_pager(buf, FALSE);
  392. X    if (do_pager("\n", FALSE) == -1)
  393. X        break;
  394. X    hist = (reverse)? hist->prev : hist->next;
  395. X    }
  396. X    (void) do_pager(NULL, FALSE);
  397. X    return 0;
  398. }
  399. X
  400. init_history(newsize)
  401. {
  402. X    if ((hist_size = newsize) < 1)
  403. X    hist_size = 1;
  404. }
  405. SHAR_EOF
  406. echo 'File loop.c is complete' &&
  407. chmod 0644 loop.c ||
  408. echo 'restore of loop.c failed'
  409. Wc_c="`wc -c < 'loop.c'`"
  410. test 34934 -eq "$Wc_c" ||
  411.     echo 'loop.c: original size 34934, current size' "$Wc_c"
  412. rm -f _shar_wnt_.tmp
  413. fi
  414. # ============= macros.c ==============
  415. if test -f 'macros.c' -a X"$1" != X"-c"; then
  416.     echo 'x - skipping macros.c (File already exists)'
  417.     rm -f _shar_wnt_.tmp
  418. else
  419. > _shar_wnt_.tmp
  420. echo 'x - extracting macros.c (Text)'
  421. sed 's/^X//' << 'SHAR_EOF' > 'macros.c' &&
  422. /* (@)# macros.c    (c) copyright 9/19/88 (Bart Schaefer, Dan Heller) */
  423. X
  424. #include "bindings.h"
  425. #include "mush.h"
  426. X
  427. extern struct cmd_map map_func_names[];
  428. X
  429. struct cmd_map    *mac_stack, *mac_hide;
  430. X
  431. /*
  432. X * print current binding to macro mappings if "str" is NULL.
  433. X * else return the string "x_str" which the str is bound to.
  434. X */
  435. char *
  436. c_macro(name, str, opts)
  437. char *name;
  438. register char *str;
  439. register struct cmd_map *opts;
  440. {
  441. X    register int    incurses = iscurses;
  442. X    char buf[MAX_MACRO_LEN], buf2[sizeof buf * 3];
  443. X
  444. X    if (!str) {
  445. X    for (; opts; opts = opts->m_next)
  446. X        if (opts->m_cmd == C_MACRO)
  447. X        break;
  448. X    if (!opts) {
  449. X        print("No %s settings.\n", name);
  450. X        return (char *)(-1);
  451. X    }
  452. X    if (incurses)
  453. X        clr_bot_line(), iscurses = FALSE;
  454. X    (void) do_pager(NULL, TRUE);
  455. X    (void) do_pager(sprintf(buf, "\nCurrent %s settings:\n\n",name), FALSE);
  456. X    }
  457. X
  458. X    if (!opts)
  459. X    return NULL;
  460. X
  461. X    for (; opts; opts = opts->m_next) {
  462. X    if (opts->m_cmd != C_MACRO)
  463. X        continue;
  464. X    if (!str) {
  465. X        (void) do_pager(sprintf(buf, "%-20.20s  ",
  466. X        ctrl_strcpy(buf2, opts->m_str, FALSE)), FALSE);
  467. X        if (do_pager(sprintf(buf, "%s\n",
  468. X        ctrl_strcpy(buf2, opts->x_str, TRUE)), FALSE) == EOF)
  469. X        break;
  470. X    } else {
  471. X        if (strcmp(str, opts->m_str))
  472. X        continue;
  473. X        else
  474. X        return opts->x_str;
  475. X    }
  476. X    }
  477. X    iscurses = incurses;
  478. X    if (str)
  479. X    (void) do_pager(NULL, FALSE);
  480. X    return NULL;
  481. }
  482. X
  483. mac_push(str)
  484. register char *str;
  485. {
  486. X    register struct cmd_map *tmp;
  487. X
  488. X    /* now make a new macro struct and set fields */
  489. X    if (!(tmp = (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
  490. X    error("calloc");
  491. X    return -1;
  492. X    }
  493. X    tmp->m_next = mac_stack;
  494. X    mac_stack = tmp;
  495. X    tmp->x_str = savestr(str);    /* x_str is the text of the expansion */
  496. X    tmp->m_str = tmp->x_str;    /* m_str is the current read position */
  497. X
  498. X    /*
  499. X     * Save the current state of the glob_flags so
  500. X     * mac_push() can also serve as unget of stdin
  501. X     */
  502. X    tmp->m_cmd = glob_flags;
  503. X    return 0;
  504. }
  505. X
  506. mac_queue(str)
  507. register char *str;
  508. {
  509. X    register struct cmd_map **tmp; /* NOTE pointer to pointer! */
  510. X
  511. X    /* Find the bottom of the macro stack */
  512. X    for (tmp = &mac_stack; *tmp; tmp = &((*tmp)->m_next))
  513. X    ;
  514. X    /* now make a new macro struct and set fields */
  515. X    if (!(*tmp =
  516. X        (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
  517. X    error("calloc");
  518. X    return -1;
  519. X    }
  520. X    (*tmp)->m_next = (struct cmd_map *)0; /* calloc should do this .... */
  521. X    (*tmp)->x_str = savestr(str); /* x_str is the text of the expansion */
  522. X    (*tmp)->m_str = (*tmp)->x_str; /* m_str is the current read position */
  523. X
  524. X    /*
  525. X     * Save the current state of the glob_flags
  526. X     */
  527. X    (*tmp)->m_cmd = glob_flags;
  528. X    return 0;
  529. }
  530. X
  531. void
  532. mac_pop()
  533. {
  534. X    register struct cmd_map *tmp;
  535. X
  536. X    if (mac_stack) {
  537. X    tmp = mac_stack;
  538. X    mac_stack = tmp->m_next;
  539. X    xfree(tmp->x_str);
  540. X    xfree((char *) tmp);
  541. X    }
  542. X    /*
  543. X     * Restore saved MACRO glob_flags only (see mac_push())
  544. X     */
  545. X    if (mac_stack) {
  546. X    if (ison(mac_stack->m_cmd, IN_MACRO))
  547. X        turnon(glob_flags, IN_MACRO);
  548. X    else
  549. X        turnoff(glob_flags, IN_MACRO);
  550. X    if (ison(mac_stack->m_cmd, LINE_MACRO))
  551. X        turnon(glob_flags, LINE_MACRO);
  552. X    else
  553. X        turnoff(glob_flags, LINE_MACRO);
  554. X    if (ison(mac_stack->m_cmd, QUOTE_MACRO))
  555. X        turnon(glob_flags, QUOTE_MACRO);
  556. X    else
  557. X        turnoff(glob_flags, QUOTE_MACRO);
  558. X    }
  559. }
  560. X
  561. /* Abandon macro processing */
  562. void
  563. mac_flush()
  564. {
  565. X    while (mac_stack)
  566. X    mac_pop();
  567. X    if (mac_hide) {
  568. X    mac_stack = mac_hide;
  569. X    mac_hide = NULL_MAP;
  570. X    while (mac_stack)
  571. X        mac_pop();
  572. X    }
  573. X    turnoff(glob_flags, IN_MACRO);
  574. X    turnoff(glob_flags, LINE_MACRO);
  575. X    turnoff(glob_flags, QUOTE_MACRO);
  576. }
  577. X
  578. /* Check for pending input from a macro. */
  579. mac_pending()
  580. {
  581. X    register struct cmd_map *msp;
  582. X
  583. X    for (msp = mac_stack; msp && !*(msp->m_str); msp = msp->m_next)
  584. X    ;
  585. X
  586. X    return !!msp;
  587. }
  588. X
  589. /* Get input and treat it as a macro.  */
  590. get_mac_input(newline)
  591. int newline;    /* 1 if newline to be appended, 0 otherwise */
  592. {
  593. X    register int len;
  594. X    char buf[MAX_MACRO_LEN];
  595. X
  596. X    /* This call cannot be nested */
  597. X    if (mac_hide)
  598. X    return -1;
  599. X
  600. X    /* Hide the mac_stack so input comes from stdin */
  601. X    mac_hide = mac_stack; mac_stack = NULL_MAP;
  602. X
  603. X    if ((len = Getstr(buf, MAX_MACRO_LEN - 1, 0)) < 0)
  604. X    return len;
  605. X    if (newline) {
  606. X    buf[len++] = '\n';
  607. X    buf[len] = 0;
  608. X    }
  609. X
  610. X    /* Restore the mac_stack */
  611. X    if (mac_stack) {
  612. X    /*
  613. X     * Somehow, a push happened even though mac_hide was
  614. X     * nonzero -- maybe by line wrap?  Fix it as best we can.
  615. X     */
  616. X    struct cmd_map *msp;
  617. X    for (msp = mac_stack; msp->m_next; msp = msp->m_next)
  618. X        ;
  619. X    msp->m_next = mac_hide;
  620. X    } else
  621. X    mac_stack = mac_hide;
  622. X    mac_hide = NULL_MAP;
  623. X
  624. X    /* Restore saved flags */
  625. X    if (mac_stack) {
  626. X    if (ison(mac_stack->m_cmd, IN_MACRO))
  627. X        turnon(glob_flags, IN_MACRO);
  628. X    else
  629. X        turnoff(glob_flags, IN_MACRO);
  630. X    if (ison(mac_stack->m_cmd, LINE_MACRO))
  631. X        turnon(glob_flags, LINE_MACRO);
  632. X    else
  633. X        turnoff(glob_flags, LINE_MACRO);
  634. X    }
  635. X    if (len > 0)
  636. X    Ungetstr(buf);
  637. X
  638. X    return 1;
  639. }
  640. X
  641. /* getchar() substitute -- reads from the current macro if one is active,
  642. X * otherwise does a getchar().
  643. X *
  644. X * NOTE:  In the mac_stack, x_str is the saved text of the current macro,
  645. X *  and m_str is the current read position within the macro.
  646. X */
  647. m_getchar()
  648. {
  649. X    int c;
  650. X
  651. X    while (mac_stack && (! *(mac_stack->m_str)))
  652. X    mac_pop();
  653. X    if (mac_stack) {
  654. X    c = *((mac_stack->m_str)++);
  655. X    return c;
  656. X    } else {
  657. X    turnoff(glob_flags, IN_MACRO);
  658. X    turnoff(glob_flags, LINE_MACRO);
  659. X    turnoff(glob_flags, QUOTE_MACRO);
  660. X    while ((c = getchar()) == 0)    /* Ignore NUL chars from stdin */
  661. X        ;                /* until better solution found */
  662. X    return c;
  663. X    }
  664. }
  665. X
  666. m_ungetc(c)
  667. char c;
  668. {
  669. X    if (mac_stack && (mac_stack->m_str > mac_stack->x_str))
  670. X    *(--(mac_stack->m_str)) = c;
  671. X    else
  672. X    (void) ungetc(c, stdin);
  673. }
  674. X
  675. /*
  676. X * Try to read a long command; assumes MAC_LONG_CMD already seen.
  677. X *  On immediate failure, return 0.
  678. X *  On failure after reading some input, return less than zero.
  679. X *  On success, return greater than 0.
  680. X * The absolute value of the return is the number of chars placed in buf.
  681. X */
  682. read_long_cmd (buf)
  683. char *buf;
  684. {
  685. X    register char c, *p = buf;
  686. X    register int count = 0;
  687. X
  688. X    /*
  689. X     * Test in_macro() in this loop because the _entire_
  690. X     * long command _must_ be in the macro -- if we run
  691. X     * out of macro in mid-long-command, it is an error.
  692. X     */
  693. X    while (in_macro() && (count < MAX_LONG_CMD - 1)
  694. X        && ((c = m_getchar()) != MAC_LONG_END)) {
  695. X    *p++ = c; ++count;
  696. X    }
  697. X    *p = '\0';
  698. X    if (c != MAC_LONG_END)
  699. X    return (-count);
  700. X    return count;
  701. }
  702. X
  703. /*
  704. X * Identify and possibly execute a reserved long macro command
  705. X * Executes if do_exec is true.  Otherwise, just parse.
  706. X */
  707. reserved_cmd (buf, do_exec)
  708. char *buf;
  709. {
  710. X    int ret = 1;
  711. X
  712. X    if (!strcmp(buf, MAC_GET_STR)) {
  713. X    if (do_exec)
  714. X        ret = get_mac_input(0);
  715. X    } else if (!strcmp(buf, MAC_GET_LINE)) {
  716. X    if (do_exec)
  717. X        ret = get_mac_input(1);
  718. X    } else
  719. X    ret = 0;
  720. X    return ret;
  721. }
  722. X
  723. #ifdef CURSES
  724. X
  725. /*
  726. X * Identify (and possibly execute, if reserved) curses mode commands
  727. X *  that appear in macro strings enclosed by MAC_LONG_CMD and
  728. X *  MAC_LONG_END.  Return the binding of the command.
  729. X */
  730. long_mac_cmd (c, do_exec)
  731. int c;
  732. {
  733. X    char buf[MAX_LONG_CMD];
  734. X    register int count, binding;
  735. X    int y, x;
  736. X
  737. X    if (c != MAC_LONG_CMD)
  738. X    return C_ERROR;
  739. X
  740. X    if ((count = read_long_cmd(buf)) <= 0) {
  741. X    print("Invalid long macro command");
  742. X    if (ison(glob_flags, CNTD_CMD))
  743. X        putchar('\n');
  744. X    if (do_exec)
  745. X        mac_flush();
  746. X    return C_ERROR;
  747. X    }
  748. X
  749. X    if (do_exec) {
  750. X    if (ison(glob_flags, CNTD_CMD))
  751. X        clr_bot_line();
  752. X    getyx(stdscr, y, x);
  753. X    move(LINES - 1, 0);
  754. X    }
  755. X    if (reserved_cmd(buf, do_exec)) {
  756. X    if (do_exec) {
  757. X        if (isoff(glob_flags, CNTD_CMD))
  758. X        move(y, x);
  759. X        return getcmd();
  760. X    } else
  761. X        return C_NULL;
  762. X    } else if (do_exec)
  763. X    move(y, x);
  764. X
  765. X    /* Can start at C_NULL because of "no-op" command */
  766. X    for (count = 0; count <= C_HELP; count++) {
  767. X    if (!strcmp(buf, map_func_names[count].m_str)) {
  768. X        binding = (int)map_func_names[count].m_cmd;
  769. X        break;
  770. X    }
  771. X    }
  772. X    /* Don't allow C_MACRO to be called directly */
  773. X    if (count > C_HELP || binding == C_MACRO) {
  774. X    print("Invalid long macro command");
  775. X    if (ison(glob_flags, CNTD_CMD))
  776. X        putchar('\n');
  777. X    return C_ERROR;
  778. X    } else
  779. X    return binding;
  780. }
  781. X
  782. #endif /* CURSES */
  783. X
  784. /*
  785. X * Check the validity of a macro binding as far as possible
  786. X */
  787. check_mac_bindings(buf)
  788. char *buf;
  789. {
  790. X    int ok = TRUE;
  791. X
  792. X    while (ok && buf && *buf) {
  793. X    if (*buf == MAC_LONG_CMD) {
  794. X        char *i;
  795. #ifdef CURSES
  796. X        int count;
  797. #endif /* CURSES */
  798. X
  799. X        if (ok)
  800. X        ok = ((i = index(++buf, MAC_LONG_END)) != NULL);
  801. X        if (i)
  802. X            *i = '\0';      /* Don't worry, we'll fix it */
  803. X        else
  804. X            return ok;
  805. #ifdef CURSES
  806. X        /* OK to start at C_NULL because of "no-op" command */
  807. X        for (count = 0; count <= C_HELP; count++)
  808. X            if (! strcmp(buf, map_func_names[count].m_str))
  809. X                break;
  810. X        /* Don't allow C_MACRO to be called directly */
  811. X        if (count == C_MACRO)
  812. X            ok = FALSE;
  813. X        else if (count > C_HELP)
  814. #endif /* CURSES */
  815. X        if (ok && !(ok = reserved_cmd(buf, FALSE)))
  816. X        wprint("Warning: unrecognized curses command: \"%s\"\n", buf);
  817. X        buf = i;
  818. X        *buf++ = MAC_LONG_END;
  819. X    } else if (*buf++ == '\\' && *buf)
  820. X        ++buf;
  821. X    }
  822. X    return ok;
  823. }
  824. SHAR_EOF
  825. chmod 0644 macros.c ||
  826. echo 'restore of macros.c failed'
  827. Wc_c="`wc -c < 'macros.c'`"
  828. test 9279 -eq "$Wc_c" ||
  829.     echo 'macros.c: original size 9279, current size' "$Wc_c"
  830. rm -f _shar_wnt_.tmp
  831. fi
  832. # ============= mail.c ==============
  833. if test -f 'mail.c' -a X"$1" != X"-c"; then
  834.     echo 'x - skipping mail.c (File already exists)'
  835.     rm -f _shar_wnt_.tmp
  836. else
  837. > _shar_wnt_.tmp
  838. echo 'x - extracting mail.c (Text)'
  839. sed 's/^X//' << 'SHAR_EOF' > 'mail.c' &&
  840. /* @(#)mail.c     (c) copyright 1986 (Dan Heller) */
  841. X
  842. #include "mush.h"
  843. X
  844. /*
  845. X * mail.c --
  846. X *    do_mail()     external interface to these functions; parses options.
  847. X *    mail_someone()    called from do_mail() to begin composing the message.
  848. X *    start_file()      creates the editing file and reset signal catching.
  849. X *    add_to_letter()    adds the next line to letter --determine ~ escapes.
  850. X *    finish_up_letter()  prompts for Cc:, verifies send, adds signatures.
  851. X *    send_it()     expands aliases, invokes mailer, sends to record file.
  852. X *    add_headers()    adds all headers to a FILE *, reads draft files
  853. X *    rm_edfile()    signals are directed here. remove letter, longjmp
  854. X *    dead_letter()     make a dead.letter if mail failed.
  855. X */
  856. X
  857. static char Subject[BUFSIZ],To[HDRSIZ],Cc[HDRSIZ],Bcc[HDRSIZ],in_reply_to[256];
  858. static int killme;
  859. static u_long flags;
  860. static SIGRET (*oldterm)(), (*oldint)(), (*oldquit)();
  861. static int finish_up_letter(), send_it(), start_file();
  862. static long add_headers();
  863. static jmp_buf cntrl_c_buf;
  864. static char *Hfile, *edfile;
  865. FILE *ed_fp;
  866. char *hfile, *mktemp();
  867. X
  868. /* argc and argv could be null if coming from tool mode compose */
  869. do_mail(n, argv, list)
  870. register int n;   /* no need for "argc", so use the space for a variable */
  871. register char **argv, *list;
  872. {
  873. X    char firstchar = (argv)? **argv: 'm';
  874. X    char *to = NULL, *cc = NULL, *addcc = NULL, *bcc = NULL, *subj = NULL;
  875. X    char *route = NULL;
  876. X    char inc_list[MAXMSGS_BITS], buf[HDRSIZ];
  877. X    u_long flgs = 0;
  878. X
  879. X    if (ison(glob_flags, IS_GETTING)) {
  880. X    wprint("You must finish the letter you are editing first.\n");
  881. X    return -1;
  882. X    }
  883. X    if (ison(glob_flags, DO_PIPE)) {
  884. X    wprint("You can't pipe out of the %s command.\n", argv? *argv : "mail");
  885. X    return -1;
  886. X    }
  887. X    clear_msg_list(inc_list);
  888. X    hfile = Hfile = NULL;
  889. X
  890. X    /* If piped to mail, include the messages piped */
  891. X    if (ison(glob_flags, IS_PIPE) ||
  892. X    (lower(firstchar) == 'r' && do_set(set_options, "autoinclude"))) {
  893. X    turnon(flgs, INCLUDE);
  894. X    bitput(list, inc_list, msg_cnt, =);
  895. X    }
  896. X    /* Set SIGN and DO_FORTUNE now so we can turn them off later */
  897. X    if (do_set(set_options, "autosign"))
  898. X    turnon(flgs, SIGN);
  899. X    if (do_set(set_options, "fortune"))
  900. X    turnon(flgs, DO_FORTUNE);
  901. X    while (argv && *argv && *++argv && **argv == '-') {
  902. X    n = 1;
  903. X    while (n && argv[0][n])
  904. X        switch (argv[0][n]) {
  905. #ifdef VERBOSE_ARG
  906. X        case 'v': turnon(flgs, VERBOSE); n++; break;
  907. #endif /* VERBOSE_ARG */
  908. X        case 'H':
  909. X            if (argv[1]) {
  910. X            n = 0;
  911. X            Hfile = *++argv;
  912. X            } else {
  913. X            wprint("Must specify a file\n");
  914. X            return -1;
  915. X            }
  916. X        when 'h':
  917. X            if (argv[1]) {
  918. X            n = -1; /* it gets incremented below */
  919. X            hfile = savestr(*++argv);
  920. X            if (ison(glob_flags, REDIRECT)) {
  921. X                turnoff(glob_flags, REDIRECT);
  922. X                turnon(flgs, SEND_NOW);
  923. X            }
  924. X            } else {
  925. X            wprint("Must specify a file containing headers\n");
  926. X            return -1;
  927. X            }
  928. X            /* Fall through */
  929. X        case 'E': turnon(flgs, EDIT_HDRS); n++;
  930. X        when 'e': turnon(flgs, EDIT); n++;
  931. X        when 'F': turnon(flgs, DO_FORTUNE); n++;
  932. X        when 'b':
  933. X            if (argv[1]) {
  934. X            n = 0, bcc = *++argv;
  935. X            fix_up_addr(bcc);
  936. X            } else {
  937. X            wprint("Must specify blind-carbon list\n");
  938. X            return -1;
  939. X            }
  940. X        when 'c':
  941. X            if (argv[1]) {
  942. X            n = 0, addcc = *++argv;
  943. X            fix_up_addr(addcc);
  944. X            } else {
  945. X            wprint("Must specify carbon-copy list\n");
  946. X            return -1;
  947. X            }
  948. X        when 's':
  949. X            if (argv[1])
  950. X            n = 0, subj = *++argv;
  951. X            else
  952. X            n++, turnon(flgs, NEW_SUBJECT);
  953. X        when 'i': case 'I': case 'f': {
  954. X            int m;
  955. X            if (!msg_cnt) {
  956. X            wprint("No message to include!\n");
  957. X            return -1;
  958. X            }
  959. X            if (argv[0][n] == 'i') {
  960. X            turnon(flgs, INCLUDE);
  961. X            turnoff(flgs, INCLUDE_H);
  962. X            turnoff(flgs, FORWARD);
  963. X            } else if (argv[0][n] == 'I') {
  964. X            turnon(flgs, INCLUDE_H);
  965. X            turnoff(flgs, INCLUDE);
  966. X            turnoff(flgs, FORWARD);
  967. X            } else if (argv[0][n] == 'f') {
  968. X            turnon(flgs, FORWARD);
  969. X            turnon(flgs, SEND_NOW);
  970. X            turnoff(flgs, INCLUDE_H);
  971. X            turnoff(flgs, INCLUDE);
  972. X            }
  973. X            /* "-i 3-5" or "-i3-5"  Consider the latter case first */
  974. X            if (!argv[0][++n])
  975. X            argv++, n = 0;
  976. X            (*argv) += n;
  977. X            m = get_msg_list(argv, inc_list);
  978. X            (*argv) -= n;
  979. X            if (m == -1)
  980. X            return -1;
  981. X            /* if there were args, then go back to the first char
  982. X             * in the next argv
  983. X             */
  984. X            if (m)
  985. X            n = 0;
  986. X            if (!n) /* n may be 0 from above! */
  987. X            argv += (m-1);
  988. X        }
  989. X        when 'U':
  990. X            turnon(flgs, SEND_NOW);
  991. X            n++;
  992. X        when 'u':
  993. X            turnoff(flgs, SIGN);
  994. X            turnoff(flgs, DO_FORTUNE);
  995. X            n++;
  996. X        when 'r':
  997. X            if (lower(firstchar) == 'r') {
  998. X            route = *++argv;
  999. X            n = 0;
  1000. X            break;
  1001. X            }
  1002. X            /* fall thru */
  1003. X        default:
  1004. X            if (argv[0][n] != '?') {
  1005. X            wprint("%c: unknown option\n\n", argv[0][n]);
  1006. X            return -1;
  1007. X            } else
  1008. X            return help(0, "mail", cmd_help);
  1009. X        }
  1010. X    }
  1011. X    if (isoff(flgs, FORWARD)) {
  1012. X    if (ison(flgs, SEND_NOW)) {
  1013. X        if (!hfile && !Hfile) {
  1014. X        wprint("Can't send immediately without draft file.\n");
  1015. X        return -1;
  1016. X        }
  1017. X        turnoff(flgs, EDIT); /* -U overrides -e */
  1018. X    } else if (do_set(set_options, "autoedit"))
  1019. X        turnon(flgs, EDIT);
  1020. X    } else if (ison(flgs, EDIT)) /* -e modifies -f */
  1021. X    turnoff(flgs, SEND_NOW);
  1022. #ifdef VERBOSE_ARG
  1023. X    if (do_set(set_options, "verbose"))
  1024. X    turnon(flgs, VERBOSE);
  1025. #endif /* VERBOSE_ARG */
  1026. X    *in_reply_to = *To = *Subject = *Cc = *Bcc = 0;
  1027. X    if (lower(firstchar) == 'r') {
  1028. X    char *in_reply_fmt, *pcc = NULL;
  1029. X    to = To, cc = Cc;
  1030. X    /*
  1031. X     * Generate a reply to all the messages passed to respond().  This
  1032. X     * list is different than the include-msg list above.  Get info about
  1033. X     * whom the messages were sent to for reply-all.
  1034. X     * BUG: currently, redundant addresses aren't pruned from Bcc list!
  1035. X     */
  1036. X    for (n = 0; n < msg_cnt; n++)
  1037. X        if (msg_bit(list, n)) {
  1038. X        if (to != To)
  1039. X            *to++ = ',', *to++ = ' ';
  1040. X        (void) reply_to(n, (firstchar == 'R'), buf);
  1041. X        if (strlen(buf) + (to - To) > sizeof(To) - 1) {
  1042. X            wprint("# recipients exceeded at msg %d\n", n);
  1043. X            break;
  1044. X        }
  1045. X        to += Strcpy(to, buf);
  1046. X        if (firstchar == 'R') {
  1047. X            if (pcc = cc_to(n, buf)) {
  1048. X            /* if there was a previous cc, append ", " */
  1049. X            if (cc != Cc)
  1050. X                *cc++ = ',', *cc++ = ' ';
  1051. X            if (strlen(pcc) + (cc - Cc) > sizeof(Cc) - 1)
  1052. X                wprint("# Cc's exceeded at msg %d\n", n);
  1053. X            else
  1054. X                cc += Strcpy(cc, pcc);
  1055. X            }
  1056. X        }
  1057. X        /* remove redundant addresses now, or headers could get too
  1058. X         * long before the list runs out (it still might)
  1059. X         */
  1060. X        rm_redundant_addrs(To, Cc);
  1061. X        to = To + strlen(To);
  1062. X        cc = Cc + strlen(Cc);
  1063. X        }
  1064. X    /* clean up end of Cc line for replyall's */
  1065. X    while (*cc == ' ' || *cc == ',')
  1066. X        *cc-- = '\0';
  1067. X    if (firstchar == 'R' && !do_set(set_options, "metoo")) {
  1068. X        /* Each reply_to() call above will leave at least
  1069. X         * one person in To.  If that one person was us,
  1070. X         * we need to get removed from the complete list.
  1071. X         */
  1072. X        (void) take_me_off(to);
  1073. X    }
  1074. X    to = To, cc = Cc;
  1075. X    if (route || (route = do_set(set_options, "auto_route")))
  1076. X        /* careful! This routine could add lots-o-bytes and lose addresses
  1077. X         * to avoid writing out of segment.
  1078. X         */
  1079. X        route_addresses(To, Cc, route);
  1080. X    if (in_reply_fmt = do_set(set_options, "in_reply_to"))
  1081. X        /* "9" here is a magic # --see compose_hdr() */
  1082. X        (void) strcpy(in_reply_to,
  1083. X                format_hdr(current_msg, in_reply_fmt, FALSE)+9);
  1084. X    }
  1085. X    if (ison(flgs, FORWARD) && ison(flgs, EDIT) ||
  1086. X        lower(firstchar) == 'r' && isoff(flgs, NEW_SUBJECT)) {
  1087. X    turnoff(flgs, NEW_SUBJECT);
  1088. X    if (subj && *subj && (isoff(flgs, FORWARD) || ison(flgs, EDIT)))
  1089. X        subj = strcpy(Subject, subj);
  1090. X    else if (subj = subject_to(current_msg, buf))
  1091. X        subj = strcpy(Subject, buf + 4*(lower(firstchar) != 'r'));
  1092. X    } else if (isoff(flgs, NEW_SUBJECT) && isoff(flgs, FORWARD) &&
  1093. X    (do_set(set_options, "ask") || do_set(set_options, "asksub")))
  1094. X    turnon(flgs, NEW_SUBJECT);
  1095. X    if (argv && *argv) {
  1096. X    char buf[HDRSIZ];
  1097. X    (void) argv_to_string(buf, argv);
  1098. X    fix_up_addr(buf);
  1099. X    to = &To[strlen(To)];
  1100. X    if (*To)
  1101. X        *to++ = ',', *to++ = ' ';
  1102. X    (void) strcpy(to, buf);
  1103. X    to = To;
  1104. X    }
  1105. X    if (addcc && *addcc) {
  1106. X    cc = &Cc[strlen(Cc)];
  1107. X    if (*Cc)
  1108. X        *cc++ = ',', *cc++ = ' ';
  1109. X    (void) strcpy(cc, addcc); /* addcc has already been fixed up */
  1110. X    cc = Cc;
  1111. X    }
  1112. X    /* remove any redundant addresses that just got added */
  1113. X    rm_redundant_addrs(To, Cc);
  1114. X    if (bcc && *bcc)
  1115. X    (void) strncpy(Bcc, bcc, sizeof(Bcc)); /* bcc already fixed up */
  1116. X    bcc = Bcc;
  1117. X
  1118. X    return mail_someone(to, subj, cc, bcc, flgs, inc_list);
  1119. }
  1120. X
  1121. static
  1122. mail_someone(to, subject, cc, bcc, flgs, list)
  1123. register char *to, *subject, *cc, *bcc, *list;
  1124. u_long flgs;
  1125. {
  1126. X    register char *p;
  1127. X
  1128. X    flags = flgs;
  1129. X    if (to && *to) {
  1130. X    if (!*To)
  1131. X        (void) strncpy(To, to, sizeof(To));
  1132. X    } else
  1133. X    to = NO_STRING;
  1134. X    if (subject && *subject) {
  1135. X    if (!*Subject)
  1136. X        (void) strncpy(Subject, subject, sizeof(Subject));
  1137. X    } else
  1138. X    subject = NO_STRING;
  1139. X    if (cc && *cc) {
  1140. X    if (!*Cc)
  1141. X        (void) strncpy(Cc, cc, sizeof(Cc));
  1142. X    } else
  1143. X    Cc[0] = '\0';
  1144. X    if (bcc && *bcc) {
  1145. X    if (!*Bcc)
  1146. X        (void) strncpy(Bcc, bcc, sizeof(Bcc));
  1147. X    } else
  1148. X    Bcc[0] = '\0';
  1149. X
  1150. X    if (ison(glob_flags, REDIRECT)) {
  1151. X    /*
  1152. X     * NOTE: Could change this to finish_up_letter() to allow
  1153. X     * signatures to be appended.  The -U! option to mush would
  1154. X     * be extended to suppress signing when redirection is on.
  1155. X     */
  1156. X    int sent = send_it();
  1157. X    if (sent == -1) {
  1158. X        wprint("Message not sent!\n");
  1159. X        rm_edfile(-1);
  1160. X    }
  1161. X    return sent;
  1162. X    }
  1163. X    /* if (!*to) then prompting will be done */
  1164. X    if (!istool && !hfile) {
  1165. X    if (p = set_header("To: ", to, !*to)) {
  1166. X        if (!*to) /* if user typed To-line here, fix up the addresses */
  1167. X        fix_up_addr(p);
  1168. X        (void) strcpy(To, p);
  1169. X    }
  1170. X    if (!*To && ison(flags, FORWARD) && ison(flags, SEND_NOW)) {
  1171. X        turnoff(flags, SEND_NOW); /* user must edit To: line or do again */
  1172. X        print("(You must add a To: address.)\n");
  1173. X    }
  1174. X    /* don't prompt for subject if forwarding mail */
  1175. X    if (isoff(flags, FORWARD) && (*subject || ison(flags, NEW_SUBJECT)) &&
  1176. X        (p = set_header("Subject: ", subject,
  1177. X            !*subject && ison(flags, NEW_SUBJECT))))
  1178. X        (void) strcpy(Subject, p);
  1179. X    if (*Cc || ison(flags, EDIT_HDRS) && do_set(set_options, "askcc")) {
  1180. X        if ((p = set_header("Cc: ", cc, !*Cc)) && *p) {
  1181. X        fix_up_addr(p);
  1182. X        (void) strcpy(Cc, p);
  1183. X        }
  1184. X    }
  1185. X    if (*Bcc)
  1186. X        print("Bcc: %s\n", Bcc);
  1187. X    putchar('\n');
  1188. X    }
  1189. X
  1190. X    /* If forwarding w/o editing, start a new file for each. */
  1191. X    if (ison(flags, FORWARD) && ison(flags, SEND_NOW)) {
  1192. X    char fwd[MAXMSGS_BITS];
  1193. X    register int i;
  1194. X    clear_msg_list(fwd);
  1195. X    for (i = 0; i < msg_cnt; i++)
  1196. X        if (msg_bit(list, i)) {
  1197. X        set_msg_bit(fwd, i);
  1198. X        if (start_file(fwd) < 0)
  1199. X            return -1;
  1200. X        turnon(msg[i].m_flags, FORWARD);
  1201. X        if (isoff(glob_flags, READ_ONLY))
  1202. X            turnon(glob_flags, DO_UPDATE);
  1203. X        clear_msg_list(fwd);
  1204. X        }
  1205. X    } else
  1206. X    return start_file(list);
  1207. X    return 0;
  1208. }
  1209. X
  1210. static
  1211. start_file(list)
  1212. char *list;
  1213. {
  1214. X    register char  *dir;
  1215. X    register int   i;
  1216. X    char         line[MAXPATHLEN];
  1217. X    int           had_hfile = FALSE;
  1218. X
  1219. X    /* getdir() uses the home directory if no tmpdir */
  1220. X    if (!(dir = getdir(do_set(set_options, "tmpdir"))))
  1221. alted:
  1222. X    dir = ALTERNATE_HOME;
  1223. X    (void) mktemp(sprintf(line, "%s/%s", dir, EDFILE));
  1224. X    strdup(edfile, line);
  1225. X    if (!(ed_fp = mask_fopen(edfile, "w+"))) {
  1226. X    if (strcmp(dir, ALTERNATE_HOME))
  1227. X        goto alted;
  1228. X    error("can't create %s", edfile);
  1229. X    return -1;
  1230. X    }
  1231. X    if (!istool) {
  1232. X    oldint = signal(SIGINT, rm_edfile);
  1233. X    oldquit = signal(SIGQUIT, rm_edfile);
  1234. X    oldterm = signal(SIGTERM, rm_edfile);
  1235. X    }
  1236. X
  1237. X    if (istool && isoff(flags, SEND_NOW) ||
  1238. X        (isoff(flags, FORWARD) || isoff(flags, SEND_NOW)) &&
  1239. X        (ison(flags, EDIT_HDRS) || do_set(set_options, "edit_hdrs"))) {
  1240. X    turnon(flags, EDIT_HDRS);
  1241. X    if (hfile)
  1242. X        had_hfile = TRUE;
  1243. X    if (add_headers(NULL_FILE, &ed_fp, 1, flags) == (long) -1)
  1244. X        return -1;
  1245. X    }
  1246. X    if (Hfile) {
  1247. X    (void) file_to_fp(Hfile, ed_fp, "r");
  1248. X    Hfile = NULL;
  1249. X    had_hfile = TRUE;
  1250. X    }
  1251. X    if (istool && isoff(flags, SEND_NOW))
  1252. X    strdup(hfile, edfile);
  1253. X
  1254. X    /* if flags call for it, include current message (with header?) */
  1255. X    if (ison(flags, INCLUDE|FORWARD|INCLUDE_H)) {
  1256. X    long copy_flgs = 0, is_forw = ison(flags, FORWARD);
  1257. X    char buf[sizeof(To)];
  1258. X    if (!is_forw) {
  1259. X        turnon(copy_flgs, INDENT);
  1260. X        if (ison(flags, INCLUDE_H) &&
  1261. X            !chk_option("alwaysignore", "include"))
  1262. X        turnon(copy_flgs, NO_IGNORE);
  1263. X        else if (ison(flags, INCLUDE))
  1264. X        turnon(copy_flgs, NO_HEADER);
  1265. X    } else if (ison(flags, SEND_NOW) ||
  1266. X        !chk_option("alwaysignore", "forward"))
  1267. X        turnon(copy_flgs, FORWARD);    /* FORWARD implies NO_IGNORE */
  1268. #ifdef MSG_SEPARATOR
  1269. X    turnon(copy_flgs, NO_SEPARATOR);
  1270. #endif /* MSG_SEPARATOR */
  1271. X    for (i = 0; i < msg_cnt; i++)
  1272. X        if (msg_bit(list, i)) {
  1273. X        if (is_forw && isoff(flags, SEND_NOW)) {
  1274. X            (void) reply_to(i, FALSE, buf);
  1275. X            (void) fprintf(ed_fp,"--- Forwarded mail from %s\n\n",buf);
  1276. X        }
  1277. X        wprint("%sing message %d ...",
  1278. X            is_forw? "forward" : "includ", i+1);
  1279. X        wprint("(%d lines)\n",
  1280. X            copy_msg(i, ed_fp, (u_long) copy_flgs, NULL));
  1281. X        set_isread(i); /* if we included it, we read it, right? */
  1282. X        if (is_forw && isoff(flags, SEND_NOW))
  1283. X            (void) fprintf(ed_fp,
  1284. X            "\n--- End of forwarded message from %s\n", buf);
  1285. X        if (!is_forw || isoff(flags, SEND_NOW))
  1286. X            (void) fputc('\n', ed_fp);
  1287. X        }
  1288. X    (void) fflush(ed_fp);
  1289. X    }
  1290. X    if (!istool && ison(glob_flags, WARNING)) {
  1291. X    if (escape && strncmp(escape, DEF_ESCAPE, 1))
  1292. X        print("(escape character is set to `%c')\n", *escape);
  1293. X    if (wrapcolumn && wrapcolumn < 20)
  1294. X        print("(warning: wrapping only %d columns from the left!)\n",
  1295. X            wrapcolumn);
  1296. X    }
  1297. X
  1298. X    /* do an "if" again in case editor not found and EDIT turned off */
  1299. X    if (!istool && ison(flags, EDIT)) {
  1300. X    char **argv, *edit;
  1301. X    int argc;
  1302. X    if ((!(edit = do_set(set_options, "visual")) || !*edit) &&
  1303. X        (!(edit = do_set(set_options, "editor")) || !*edit))
  1304. X        edit = DEF_EDITOR;
  1305. X    (void) sprintf(line, "%s %s", edit, edfile);
  1306. X    if ((argv = mk_argv(line, &argc, FALSE)) && argc > 0) {
  1307. X        print("Starting \"%s\"...\n", argv[0]);
  1308. X        (void) fclose(ed_fp);
  1309. X        ed_fp = NULL_FILE;
  1310. X        execute(argv);
  1311. X        free_vec(argv);
  1312. X        turnoff(flags, EDIT);
  1313. X        turnoff(flags, FORWARD); /* forwarded messages must be unedited */
  1314. X        /* upon exit of editor, user must now type eofc or "." to send */
  1315. X        if (!(ed_fp = fopen(edfile, "r+"))) {
  1316. X        error("can't reopen %s", edfile);
  1317. X        return -1;
  1318. X        }
  1319. X        (void) fseek(ed_fp, 0L, 2);
  1320. X    } else
  1321. X        print("Unable to start \"%s\"\n", edit);
  1322. X    wprint("(continue editing letter or ^%c to send)\n", eofc + '@');
  1323. X    } else if (ison(flags, SEND_NOW)) {
  1324. X    /* if finish_up_letter() was successful, file was successfully sent. */
  1325. X    if (!setjmp(cntrl_c_buf) && finish_up_letter() == 0) {
  1326. X        rm_edfile(0);
  1327. X        return 0;
  1328. X    }
  1329. X    } else if (had_hfile) {
  1330. X    /* it's not obvious what's going on -- enlighten user */
  1331. X    wprint("(continue editing or ^%c to send)\n", eofc + '@');
  1332. X    }
  1333. X
  1334. #ifdef SUNTOOL
  1335. X    if (istool) {
  1336. X    /* toolmode doesn't care if SEND_NOW -- user continues to edit file.
  1337. X     * if SEND_NOW is not set, then the editor file has just been started,
  1338. X     * so again, just return so user can edit file.
  1339. X     */
  1340. X    if (ed_fp)
  1341. X        fclose(ed_fp), ed_fp = NULL_FILE;
  1342. X    turnon(glob_flags, IS_GETTING);
  1343. X    return 0;
  1344. X    }
  1345. #endif /* SUNTOOL */
  1346. X    if (ison(flags, SEND_NOW)) {
  1347. X    /* editing couldn't have been on -- finish_up_letter() failed */
  1348. X    rm_edfile(0 - ison(flags, FORWARD));
  1349. X    return -1;
  1350. X    }
  1351. X
  1352. X    i = 0;
  1353. X    turnon(glob_flags, IS_GETTING);
  1354. X    do  {
  1355. X    /* If the user hits ^C in cbreak mode, mush will return to
  1356. X     * Getstr and not clear the buffer. whatever is typed next will
  1357. X     * be appended to the line.  jumping here will force the line to
  1358. X     * be cleared cuz it's a new call.
  1359. X     */
  1360. X    (void) setjmp(cntrl_c_buf);
  1361. X    while (Getstr(line, sizeof(line), 0) > -1) {
  1362. X        if (!istool) /* toolmode checks on a timer -- don't do it here */
  1363. X        (void) check_new_mail(); /* if new mail comes in, get it */
  1364. X        if ((i = add_to_letter(line)) <= 0)
  1365. X        break;
  1366. X    }
  1367. X    } while (i >= 0 && finish_up_letter() == -1);
  1368. X    turnoff(glob_flags, IS_GETTING);
  1369. X    return i; /* return -1 if ~x or ~q to terminate letter */
  1370. }
  1371. X
  1372. char *tilde_commands[] = {
  1373. X    "commands: [OPTIONAL argument]",
  1374. X    "t [list]\tChange list of recipients",
  1375. X    "s [subject]\tModify [set] subject header",
  1376. X    "c [cc list]\tModify [set] carbon copy recipients",
  1377. X    "b [bcc list]\tModify [set] blind carbon recipients",
  1378. X    "h\t\tModify all message headers",
  1379. X    "e [editor]\tEnter editor. Editor used: \"set editor\", env EDITOR, vi",
  1380. X    "v [editor]\tEnter visual editor. \"set visual\", env VISUAL, vi",
  1381. X    "u\t\tEdit previous (last) line in file.",
  1382. X    "p [pager]\tPage message; pager used: \"set pager\", env. PAGER, more",
  1383. X    "i [msg#'s]\tInclude current msg body [msg#'s] indented by \"indent_str\"",
  1384. X    "I [msg#'s]\tSame, but include the message headers from included messages",
  1385. X    "f [msg#'s]\tForward mail. Not indented, but marked as \"forwarded mail\"",
  1386. X    "S[!]\t\tInclude Signature file [suppress file]",
  1387. X    "F[!]\t\tAdd a fortune at end of letter [don't add]",
  1388. X    "w file\t\tWrite msg buffer to file name",
  1389. X    "a file\t\tAppend msg buffer to file name",
  1390. X    "r file\t\tRead filename into message buffer",
  1391. X    "q \t\tQuit message; save in dead.letter (unless \"nosave\" is set).",
  1392. X    "x \t\tQuit message; don't save in dead.letter.",
  1393. X    "$variable\tInsert the string value for \"variable\" into message.",
  1394. X    ":cmd\t\tRun the mail command \"cmd\".",
  1395. X    "|cmd\t\tPipe the message through the unix command \"cmd\".",
  1396. X    "E[!]\t\tClear contents of letter after saving to dead.letter [unless !].",
  1397. X    0
  1398. };
  1399. X
  1400. /*
  1401. X * TC_EDIT(tc) returns TRUE if tilde_command[tc] involves message
  1402. X * editing operations.  Used when EDIT_HDRS is active.
  1403. X */
  1404. #define TC_EDIT(tc) ((tc) && ((tc) < 6 || !tilde_commands[(tc)+1]))
  1405. X
  1406. /*
  1407. X * Add the line (char *) parameter to the letter.  Determine tilde
  1408. X * escapes and determine what to do.  This function returns 0 to
  1409. X * indicate user wants to end the letter, -1 if the letter cannot
  1410. X * be sent (~q, ~x no buffer after editor, etc...) or 1 to indicate
  1411. X * successful addition of the line to the letter.
  1412. X * This function may be called by toolmode just to change certain mush
  1413. X * internal variables via tilde escapes.  Thus, ed_fp might be null.
  1414. X */
  1415. add_to_letter(line)
  1416. char line[];
  1417. {
  1418. X    register char *p;
  1419. X    char buf[HDRSIZ > MAXPATHLEN ? HDRSIZ : MAXPATHLEN];
  1420. X
  1421. X    killme = 0;
  1422. X    if (ed_fp) /* may be null if istool */
  1423. X    (void) fseek(ed_fp, 0L, 2);
  1424. X
  1425. X    if (!strcmp(line, ".") && do_set(set_options, "dot"))
  1426. X    return 0;
  1427. X    if (line[0] != *escape || ison(glob_flags, QUOTE_MACRO)) {
  1428. X    (void) fputs(line, ed_fp);
  1429. X    (void) fputc('\n', ed_fp);
  1430. X    (void) fflush(ed_fp);
  1431. X    return 1;
  1432. X    }
  1433. X    /* all commands are "~c" (where 'c' is the command). set p = first
  1434. X     * character after 'c' and skip whitespace
  1435. X     */
  1436. X    p = &line[2];
  1437. X    skipspaces(0);
  1438. X    switch (line[1]) {
  1439. X    case 'v' : case 'p': case 'e' : case '|' : {
  1440. X        if (!*p || *p == 'i')
  1441. X        switch (line[1]) {
  1442. X            case 'p' :
  1443. X            if (!*p && !(p = do_set(set_options, "pager")))
  1444. X                p = DEF_PAGER;
  1445. X            if (!*p || !strcmp(p, "internal"))
  1446. X                p = NULL;
  1447. X            when 'v' :
  1448. X            if (*p && p[1] || (p = do_set(set_options, "visual")))
  1449. X                break;
  1450. X            /* else fall through */
  1451. X            default :
  1452. X            if (!(p = do_set(set_options, "editor")) || !*p)
  1453. X                p = DEF_EDITOR;
  1454. X            when '|' :
  1455. X            print("No command for pipe\n");
  1456. X            return 1;
  1457. X        }
  1458. X        if (line[1] == 'p' || line[1] == '|')
  1459. X        rewind(ed_fp);
  1460. X        if (line[1] == 'p') {
  1461. X        (void) do_pager(p, TRUE); /* start the pager "p" */
  1462. X        if (isoff(flags, EDIT_HDRS)) {
  1463. X            (void) do_pager(sprintf(buf, "To: %s\n", To), FALSE);
  1464. X            if (Subject[0])
  1465. X            (void) do_pager(sprintf(buf, "Subject: %s\n", Subject),
  1466. X                    FALSE);
  1467. X            if (Cc[0])
  1468. X            (void) do_pager(sprintf(buf, "Cc: %s\n", Cc), FALSE);
  1469. X            if (Bcc[0])
  1470. X            (void) do_pager(sprintf(buf, "Bcc: %s\n", Bcc), FALSE);
  1471. X            (void) do_pager(strcpy(buf,
  1472. X                        "--------\nMessage contains:\n"),
  1473. X            FALSE);
  1474. X        }
  1475. X        while (fgets(buf, sizeof(buf), ed_fp))
  1476. X            if (do_pager(buf, FALSE) == EOF)
  1477. X            break;
  1478. X        (void) do_pager(NULL, FALSE); /* end pager */
  1479. X        } else if (line[1] == '|') {
  1480. X        FILE *pipe_fp;
  1481. X        (void) sprintf(buf, "( %s ) > %s", p, edfile);
  1482. X        if (unlink(edfile) < 0) {
  1483. X            error("Can't unlink %s:", edfile);
  1484. X            break; /* Drop out of switch */
  1485. X        }
  1486. X        if ((pipe_fp = popen(buf, "w")) == NULL_FILE) {
  1487. X            error("Can't run \"%s\":", p);
  1488. X            (void) file_to_fp(edfile, ed_fp, "w");
  1489. X        } else {
  1490. X            while (fgets(buf, sizeof(buf), ed_fp))
  1491. X            if (fputs(buf, pipe_fp) == EOF) {
  1492. X                print("Broken pipe\n");
  1493. X                break;
  1494. X            }
  1495. X            (void) pclose(pipe_fp);
  1496. X        }
  1497. X        pipe_fp = ed_fp; /* save ed_fp until we can reopen it */
  1498. X        if (!(ed_fp = fopen(edfile, "r+"))) {
  1499. X            error("can't reopen %s", edfile);
  1500. X            (void) rewind(pipe_fp);
  1501. X            if (file_to_fp(edfile, pipe_fp, "w") < 0 ||
  1502. X                !(ed_fp = fopen(edfile, "r+"))) {
  1503. X            error("can't restore old contents of %s", edfile);
  1504. X            ed_fp = pipe_fp;
  1505. X            dead_letter(0);
  1506. X            return -1;
  1507. X            }
  1508. X        }
  1509. X        (void) fclose(pipe_fp);
  1510. X        } else {
  1511. X        char **argv;
  1512. X        int argc;
  1513. X        (void) sprintf(buf, "%s %s", p, edfile);
  1514. X        if ((argv = mk_argv(buf, &argc, FALSE)) && argc > 0) {
  1515. X            (void) fclose(ed_fp);
  1516. X            ed_fp = NULL_FILE;
  1517. X            execute(argv);
  1518. X            free_vec(argv);
  1519. X            /* tool will return even tho editor isn't done */
  1520. X            if (!(ed_fp = fopen(edfile, "r+"))) {
  1521. X            error("can't reopen %s", edfile);
  1522. X            return -1;
  1523. X            }
  1524. X        } else
  1525. X            print("Unable to start \"%s\"\n", p);
  1526. X        }
  1527. X    }
  1528. X    when '$': {
  1529. X        register char *p2;
  1530. X        if (!(p2 = do_set(set_options, p)))
  1531. X        print("(%s isn't set)\n", p);
  1532. X        else
  1533. X        putstring(p2, ed_fp);
  1534. X    }
  1535. X    when ':': {
  1536. X        char new[MAXMSGS_BITS];
  1537. X        u_long save_flags = glob_flags;
  1538. X
  1539. X        turnon(glob_flags, IGN_SIGS);
  1540. X        turnoff(glob_flags, DO_PIPE);
  1541. X        turnoff(glob_flags, IS_PIPE);
  1542. X        (void) cmd_line(p, new);
  1543. X        glob_flags = save_flags;
  1544. X    }
  1545. X    when 'i': case 'f': case 'I': case 'm': {
  1546. X        int  n;
  1547. X        u_long copy_flgs = 0;
  1548. X        char list[MAXMSGS_BITS];
  1549. X
  1550. X        if (!msg_cnt) {
  1551. X        wprint("No messages.\n");
  1552. X        break;
  1553. X        }
  1554. X        clear_msg_list(list);
  1555. X        if (line[1] != 'f') {
  1556. X        turnon(copy_flgs, INDENT);
  1557. X        if (line[1] == 'i')
  1558. X            turnon(copy_flgs, NO_HEADER);
  1559. X        else if (!chk_option("alwaysignore", "include"))
  1560. X            turnon(copy_flgs, NO_IGNORE);
  1561. X         } else if (!chk_option("alwaysignore", "forward"))
  1562. X        turnon(copy_flgs, NO_IGNORE);
  1563. #ifdef MSG_SEPARATOR
  1564. X        turnon(copy_flgs, NO_SEPARATOR);
  1565. #endif /* MSG_SEPARATOR */
  1566. X        if (!*p)
  1567. X        set_msg_bit(list, current_msg);
  1568. X        else if (!do_range(p, list))
  1569. X        return 1;
  1570. X        for (n = 0; n < msg_cnt; n++)
  1571. X        if (msg_bit(list, n)) {
  1572. X            if (line[1] == 'f') {
  1573. X            (void) reply_to(n, FALSE, buf);
  1574. X            (void) fprintf(ed_fp,
  1575. X                    "--- Forwarded mail from %s\n\n", buf);
  1576. X            }
  1577. X            wprint("Including message %d ... ", n+1);
  1578. X            wprint("(%d lines)\n", copy_msg(n, ed_fp, copy_flgs, NULL));
  1579. X            set_isread(n);
  1580. X            if (line[1] == 'f')
  1581. X            (void) fprintf(ed_fp,
  1582. X                "\n--- End of forwarded message from %s\n\n", buf);
  1583. X            else
  1584. X            (void) fputc('\n', ed_fp);
  1585. X        }
  1586. X    }
  1587. X    /* To: Cc: and Bcc: headers */
  1588. X    when 'b':
  1589. X    case 't':
  1590. X    case 'c': {
  1591. X        char *h = (line[1] == 't')? To : (line[1] == 'c')? Cc : Bcc;
  1592. X        char *Prompt = line[1] == 't'? "To: " :
  1593. X               line[1] == 'c'? "Cc: " : "Bcc: ";
  1594. X        if (ison(flags, EDIT_HDRS)) {
  1595. X        print("You must use an editor to change your headers.\n");
  1596. X        break;
  1597. X        }
  1598. X
  1599. X        if (*p) {
  1600. X        fix_up_addr(p);
  1601. X        if (*h)
  1602. X            (void) sprintf(h+strlen(h), ", %s", p);
  1603. X        else
  1604. X            (void) strcpy(h, p);
  1605. X        } else if (!(p = set_header(Prompt, h, TRUE)) || !*p)
  1606. X        *h = 0;
  1607. X        else {
  1608. X        fix_up_addr(p);
  1609. X        (void) strcpy(h, p);
  1610. X        }
  1611. X    }
  1612. X    when 's':
  1613. X        if (ison(flags, EDIT_HDRS)) {
  1614. X        print("You must use an editor to change your headers.\n");
  1615. X        break;
  1616. X        }
  1617. X        if (*p || (p = set_header("Subject: ", Subject, 1)))
  1618. X        if (!*p)
  1619. X            Subject[0] = 0;
  1620. X        else
  1621. X            (void) strcpy(Subject, p);
  1622. X    when 'h':
  1623. X        if (ison(flags, EDIT_HDRS)) {
  1624. X        print("You must use an editor to change your headers.\n");
  1625. X        break;
  1626. X        }
  1627. X        while ((p = set_header("To: ", To, 1)) && !*p)
  1628. X        print("(There must be a recipient.)\n");
  1629. X        (void) strcpy(To, p);
  1630. X        if (p = set_header("Subject: ", Subject, 1))
  1631. X        if (!*p)
  1632. X            Subject[0] = 0;
  1633. X        else
  1634. X            (void) strcpy(Subject, p);
  1635. X        if (p = set_header("Cc: ", Cc, 1))
  1636. X        if (!*p)
  1637. X            Cc[0] = 0;
  1638. X        else {
  1639. X            fix_up_addr(p);
  1640. X            (void) strcpy(Cc, p);
  1641. X        }
  1642. X        if (p = set_header("Bcc: ", Bcc, 1))
  1643. X        if (!*p)
  1644. X            Bcc[0] = 0;
  1645. X        else {
  1646. X            fix_up_addr(p);
  1647. X            (void) strcpy(Bcc, p);
  1648. X        }
  1649. X    when 'F': case 'S' : {
  1650. X        if (*p == '!')
  1651. X        turnoff(flags, line[1] == 'F'? DO_FORTUNE : SIGN);
  1652. X        else
  1653. X        turnon(flags, line[1] == 'F'? DO_FORTUNE : SIGN);
  1654. X        wprint("%sadding %s at end of message.\n", *p == '!'? "not " : "",
  1655. X        line[1] == 'F'? "fortune" : "signature");
  1656. X    }
  1657. X    when 'w': case 'a': case 'r':
  1658. X        if (!*p) {
  1659. X        print("(you must specify a filename)\n");
  1660. X        return 1;
  1661. X        }
  1662. X        (void) fseek(ed_fp, 0L, 2); /* append */
  1663. X        (void) file_to_fp(p, ed_fp, (line[1] == 'r')? "r":
  1664. X                  (line[1] == 'w')? "w": "a");
  1665. X    /* go up one line in the message file and allow the user to edit it */
  1666. X    when 'u': {
  1667. X        long newpos, pos = ftell(ed_fp);
  1668. X        char oldline[256];
  1669. X        if (pos <= 0L) { /* pos could be -1 if ftell() failed */
  1670. X        print("(No previous line in file.)\n");
  1671. X        return 1;
  1672. X        }
  1673. X        /* get the last 256 bytes written and read backwards from the
  1674. X         * current place until '\n' is found. Start by moving past the
  1675. X         * first \n which is at the end of the line we want to edit
  1676. X         */
  1677. X        newpos = max(0, pos - 256L);
  1678. X        (void) fseek(ed_fp, newpos, L_SET);
  1679. X        /* don't fgets -- it'll stop at a \n */
  1680. X        (void) fread(line, sizeof(char), (int)(pos-newpos), ed_fp);
  1681. X        pos--;
  1682. X        /* the last char in line should be a \n cuz it was last input */
  1683. X        if (line[(int)(pos-newpos)] != '\n')
  1684. X        print("I don't know how, but your last line ended with %c.\n",
  1685. X            line[(int)(pos-newpos)]);
  1686. X        else
  1687. X        line[(int)(pos-newpos)] = 0; /* null terminate \n for ^H-ing */
  1688. X        for (pos--; pos > newpos && line[(int)(pos-newpos)] != '\n'; pos--)
  1689. X        ;
  1690. X        /* we've gone back to the end of the second previous line. Check
  1691. X         * to see if the char we're pointing to is a \n.  It should be, but
  1692. X         * if it's not, we moved back to the first line of the file.
  1693. X         */
  1694. X        if (line[(int)(pos-newpos)] == '\n')
  1695. X        ++pos;
  1696. X        /* save the old line that's there in case the user boo-boos */
  1697. X        (void) strcpy(oldline, &line[(int)(pos-newpos)]);
  1698. X        /* let set header print out the line and get the input */
  1699. X        if (!(p = set_header("", &line[(int)(pos-newpos)], TRUE))) {
  1700. X        print("Something bad happened and I don't know what it is.\n");
  1701. X        p = oldline;
  1702. X        } else if (*p == *escape)
  1703. X        print("(Warning: %c escapes ignored on %cu lines.)\n",
  1704. X                *escape, *escape);
  1705. X        /* seek to to the position where the new line will go */
  1706. X        (void) fseek(ed_fp, pos, L_SET);
  1707. X        /* put the newly typed line */
  1708. X        (void) fputs(p, ed_fp); /* don't add \n. padding may be necessary */
  1709. X        /* if the new line is less than the old line, we're going to do
  1710. X         * one of two things.  The best thing to do is to truncate the
  1711. X         * file to the end of the new line.  Sys-v can't do that, so we
  1712. X         * pad the line with blanks.  May be messy in some cases, but...
  1713. X         */
  1714. X        if ((pos = strlen(p) - strlen(oldline)) < 0) {
  1715. #ifndef SYSV
  1716. X        /* add the \n, flush the file, truncate to the current pos */
  1717. X        (void) fputc('\n', ed_fp);
  1718. X        (void) fflush(ed_fp);
  1719. X        (void) ftruncate(fileno(ed_fp), (off_t) ftell(ed_fp));
  1720. #else /* SYSV */
  1721. X        /* pad with blanks to the length of the old line. add \n */
  1722. X        while (pos++ < 0)
  1723. X            (void) fputc(' ', ed_fp);
  1724. X        (void) fputc('\n', ed_fp), (void) fflush(ed_fp);
  1725. #endif /* SYSV */
  1726. X        } else {
  1727. X        /* the new line is >= the old line, add \n -- no trunc req. */
  1728. X            (void) fputc('\n', ed_fp);
  1729. X        (void) fflush(ed_fp);
  1730. X        }
  1731. X        return 1;
  1732. X     }
  1733. X    /* break;  not here cuz of "return" (lint). */
  1734. X    case 'E':
  1735. X        if (ison(flags, EDIT_HDRS)) {
  1736. X        print("You must use an editor to empty the message buffer.\n");
  1737. X        break;
  1738. X        }
  1739. X        if (*p != '!' && !do_set(set_options, "nosave"))
  1740. X        dead_letter(0);
  1741. X        if (emptyfile(&ed_fp, edfile) == -1) {
  1742. X        error(edfile);
  1743. X        return -1;
  1744. X        } else
  1745. X        print("Message buffer empty\n");
  1746. X    when 'q':
  1747. X        /* save in dead.letter if nosave not set -- rm_edfile(-2). */
  1748. X        rm_edfile(-2); /* doesn't return out of tool mode */
  1749. X        return -1;
  1750. X        /* break; not stated cuz of "return" (lint) */
  1751. SHAR_EOF
  1752. true || echo 'restore of mail.c failed'
  1753. fi
  1754. echo 'End of  part 11'
  1755. echo 'File mail.c is continued in part 12'
  1756. echo 12 > _shar_seq_.tmp
  1757. exit 0
  1758. exit 0 # Just in case...
  1759. -- 
  1760. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1761. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1762. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1763. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1764.