home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume12 / mush / part07 < prev    next >
Encoding:
Text File  |  1990-05-05  |  54.2 KB  |  1,770 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v12i035: Mail User's Shell, Part07/19
  3. from: argv@Eng.Sun.COM (Dan Heller)
  4. Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  5.  
  6. Posting-number: Volume 12, Issue 35
  7. Submitted-by: argv@Eng.Sun.COM (Dan Heller)
  8. Archive-name: mush/part07
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # If this archive is complete, you will see the following message at the end:
  17. #        "End of archive 7 (of 19)."
  18. # Contents:  mush/addrs.c mush/viewopts.c
  19. # Wrapped by argv@turnpike on Wed May  2 13:59:26 1990
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'mush/addrs.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'mush/addrs.c'\"
  23. else
  24. echo shar: Extracting \"'mush/addrs.c'\" \(33359 characters\)
  25. sed "s/^X//" >'mush/addrs.c' <<'END_OF_FILE'
  26. X/* addrs.c -- copyright (c) Dan Heller 1/25/1989 */
  27. X
  28. X#include "mush.h"
  29. X
  30. X/*
  31. X * Check to see if all addressees in list1 is in list2.
  32. X * The lists must be as clean as the driven snow (no comments, aliases
  33. X * must have been expanded, all are separated by whitespace (for mk_argv).
  34. X *
  35. X * "user" matches "user" and "user@localhost"
  36. X * "*user" matches "user" at any address whatsoever."
  37. X * !host matches any user destined for the specified host.
  38. X * !some!path is the same, but can be more specifiec in the path.
  39. X * @dom.ain can match any user destined for any host within the domain.
  40. X *      @berkeley.edu would match: dheller@cory.berkeley.edu
  41. X */
  42. Xcompare_addrs(list1, list2, ret_buf)
  43. Xchar *list1, *list2, ret_buf[];
  44. X{
  45. X    register char    *p;
  46. X    char        **addrv, **listv, buf[256]; /* addrs aren't long */
  47. X    int            addrc, listc, a, l, h, ret_val;
  48. X
  49. X    /* autosign2 list contains non-comment addresses */
  50. X    listv = mk_argv(list1, &listc, FALSE);
  51. X    addrv = mk_argv(list2, &addrc, FALSE);
  52. X
  53. X    /* loop thru both lists and convert addresses to !-format
  54. X     * then remove ourhost names so "user" matches "user!local"
  55. X     * also remove possible trailing commas (from list).
  56. X     */
  57. X    for (a = 0; a < addrc; a++) {
  58. X    if (a != addrc-1 && (p = index(addrv[a], ',')) && !p[1])
  59. X        *p = 0;
  60. X    if (addrv[a][0] == '!' || addrv[a][0] == '@')
  61. X        continue;
  62. X    (void) bang_form(buf, addrv[a]);
  63. X    if (strcmp(addrv[a], buf)) /* if they differ... */
  64. X        (void) strcpy(addrv[a], buf); /* save new version */
  65. X    }
  66. X    for (l = 0; l < listc; l++) {
  67. X    if (l != listc-1 && (p = index(listv[l], ',')) && !p[1])
  68. X        *p = 0;
  69. X    if (listv[l][0] == '!' || listv[l][0] == '@')
  70. X        continue;
  71. X    (void) bang_form(buf, listv[l]);
  72. X    if (strcmp(listv[l], buf)) /* if they differ... */
  73. X        (void) strdup(listv[l], buf); /* save new version */
  74. X    }
  75. X
  76. X    Debug("\nlist1 = "), print_argv(listv);
  77. X    Debug("list2 = "), print_argv(addrv), putchar('\n');
  78. X
  79. X    /* loop thru each list comparing each element with the
  80. X     * other, if necessary.
  81. X     */
  82. X    for (l = 0; l < listc; l++) {
  83. X    ret_val = 0;
  84. X    /* check if local recipient with was specified. */
  85. X    if (!(p = rindex(listv[l], '!')))
  86. X        for (a = 0; a < addrc; a++) {
  87. X        /* we have a local user so far.  If addrv[] is
  88. X         * not remote, then strcmp() immediately.
  89. X         * Note that "!" with no host indicates *all*
  90. X         * local users!!!
  91. X         */
  92. X        if (addrv[a][0] == '*') {
  93. X            /* "*user" == "user" or "*" == login */
  94. X            if (!addrv[a][1] && !lcase_strncmp(listv[l], login, -1) ||
  95. X            !lcase_strncmp(listv[l], addrv[a]+1, -1))
  96. X            ret_val = 1;
  97. X        } else if (addrv[a][0] != '!') {
  98. X           if (!lcase_strncmp(addrv[a], listv[l], -1) || !addrv[a][1])
  99. X            ret_val = 1;
  100. X        } else for (h = 0; ourname && ourname[h]; h++)
  101. X            if (!lcase_strncmp(addrv[a]+1, ourname[h], -1)) {
  102. X            ret_val = 1;
  103. X            break;
  104. X            }
  105. X        if (ret_val)
  106. X            break;
  107. X        }
  108. X    /* else this is a remote user */
  109. X    else {
  110. X        /* check all the addresses for @dom.ain stuff or
  111. X         * !path!name type stuff only.
  112. X         */
  113. X        /* first back up p to the previous '!' */
  114. X        char *start, *user = p + 1;
  115. X        while (p > listv[l] && *--p != '!')
  116. X        ;
  117. X        start = p; /* Where to start for _domain_ addrs */
  118. X        for (a = 0; a < addrc; a++) {
  119. X        int len;
  120. X        char *path;
  121. X
  122. X        /* first check the cases of address unmodified by @ and !
  123. X         * or check to see if  *user  is specified.
  124. X         */ 
  125. X        if (addrv[a][0] != '@' && addrv[a][0] != '!') {
  126. X            if (addrv[a][0] == '*') {
  127. X            /* we saved the username at "user" declaration. */
  128. X            /* if "*" is by itself, check against user's login */
  129. X            if (!addrv[a][1] && !lcase_strncmp(user, login, -1) ||
  130. X                addrv[a][1] && !lcase_strncmp(user,addrv[a]+1,-1)){
  131. X                ret_val = 1;
  132. X                break;
  133. X            }
  134. X            } else if (!lcase_strncmp(addrv[a], listv[l], -1)) {
  135. X            ret_val = 1;
  136. X            break;
  137. X            }
  138. X            continue;
  139. X        }
  140. X        path = addrv[a]+1;
  141. X        while (addrv[a][0] == '@' && *path == '.')
  142. X            path++;
  143. X        if ((len = strlen(path)) == 0)
  144. X            continue; /* localhost stuff only -- can't match */
  145. X        /* first check against specified domains */
  146. X        if (addrv[a][0] == '@') {
  147. X            for (p = start; p; (p = index(p, '.')) && ++p)
  148. X            if (!lcase_strncmp(p, path, len) &&
  149. X                (p[len] == '.' || p[len] == 0 || p[len] == '!')) {
  150. X                ret_val = 1;
  151. X                break;
  152. X            }
  153. X        } else if (addrv[a][0] == '!') {
  154. X            /* for !path style, start at head of addr */
  155. X            for (p = listv[l]; p; (p = index(p, '!')) && ++p)
  156. X            if (!lcase_strncmp(p, path, len) &&
  157. X                (p[len] == '!' || p[len] == 0)) {
  158. X                ret_val = 1;
  159. X                break;
  160. X            }
  161. X        }
  162. X        /* If address is in autosign2, goto next addr */
  163. X        if (ret_val)
  164. X            break;
  165. X        }
  166. X    }
  167. X    if (!ret_val) {
  168. X        /* this address isn't in autosign2 list */
  169. X        if (ret_buf)
  170. X        (void) strcpy(ret_buf, listv[l]);
  171. X        break;
  172. X    }
  173. X    }
  174. X    free_vec(listv);
  175. X    free_vec(addrv);
  176. X
  177. X    return ret_val;
  178. X}
  179. X
  180. X/*
  181. X * Parser for stupidly-formed RFC822 addresses.  It has been tested on
  182. X * several bizzare cases as well as the normal stuff and uucp paths.  It
  183. X * takes a string which is a bunch of addresses and unscrambles the first
  184. X * one in the string.  It returns a pointer to the first char past what it
  185. X * unscrambled and copies the unscrambled address to its second argument.
  186. X * 
  187. X * It does NOT deal with trailing (comment) strings --
  188. X *         <whoever@somewhere> (This is a comment)
  189. X *                            ^unscramble_addr return points here
  190. X * 
  191. X * It also does not deal well with malformed <addresses> --
  192. X *         <whoever@somewhere,nowhere>
  193. X *                           ^unscramble_addr return points here
  194. X * 
  195. X * In each of the above cases, the string "whoever@somewhere" is copied
  196. X * to the second argument.
  197. X * 
  198. X * Nothing is done to un-<>ed route-less RFC822/976 addresses, nor to
  199. X * uucp paths, nor to mixed-mode addresses not containing a route.
  200. X * Hopelessly scrambled addresses are not handled brilliantly --
  201. X *     @some.dumb.place,@any.other.place:sys2!user%sys3@sys1
  202. X * parses to
  203. X *     sys2!user%sys3@sys1
  204. X * i.e., the route is simply dropped.
  205. X *
  206. X * If UUCP is defined, a little more work is done with @: routes.  The
  207. X * mangled address given above will unwind to
  208. X *    some.dumb.place!any.other.place!sys1!sys2!sys3!user
  209. X * thanks to intelligence in bang_form().
  210. X */
  211. Xchar *
  212. Xunscramble_addr(addr, naddr)
  213. Xchar *addr;
  214. Xchar *naddr;
  215. X{
  216. X    char *i, *r, *at;
  217. X    char s[BUFSIZ], t[BUFSIZ];
  218. X    int anglebrace = 0;
  219. X
  220. X    /* Make a copy of the address so we can mangle it freely. */
  221. X    if (addr && *addr) {
  222. X    /* Skip any leading whitespace. */
  223. X    for (i = addr; *i && (r = any(i, " \t")) == i;)
  224. X        if (r)
  225. X        i = r + 1;
  226. X    if (*i == '\0')
  227. X        return NULL;
  228. X    /* Skip any leading double-quoted comment. */
  229. X    if (*i == '"') {
  230. X        if ((i = index(i + 1, '"')) && (*i == '\0' || *(++i) == '\0'))
  231. X            return NULL;
  232. X    }
  233. X    /* Skip any more whitespace. */
  234. X    for (; *i && (r = any(i, " \t")) == i;)
  235. X        if (r)
  236. X        i = r + 1;
  237. X    if (*i == '\0')
  238. X        return NULL;
  239. X    /* Check for angle braces around the address. */
  240. X    if (*i == '<') {
  241. X        if (*(++i) == '\0')
  242. X        return NULL;
  243. X        ++anglebrace;
  244. X    }
  245. X    /*
  246. X     * Look for a route.  A route is a comma-separated set of @-tagged
  247. X     *  domains terminated by a colon.  Later versions might try to use
  248. X     *  the route, but for now it confuses too many mailers.
  249. X     */
  250. X    if ((*i == '@') && (r = any(i, " \t:"))) {
  251. X        if (*r != ':')
  252. X        return NULL;
  253. X        if (*(r + 1) == '\0')
  254. X        return NULL;
  255. X#ifndef UUCP
  256. X        /*
  257. X         * Back up to the rightmost @-tagged domain
  258. X         *  (see note below about unwinding)
  259. X         */
  260. X        *r = '\0';
  261. X        i = rindex(i, '@');
  262. X        *r = ':';
  263. X#endif /* !UUCP */
  264. X    }
  265. X    /* Remember how much we've skipped, and copy the rest. */
  266. X    at = i;
  267. X    (void) strncpy(t, i, sizeof t);
  268. X    t[sizeof t - 1] = 0;
  269. X    /* Strip from a trailing angle brace, if present. */
  270. X    if (anglebrace) {
  271. X        if (r = any(t, "> \t")) {
  272. X        if (r == t || *r != '>')
  273. X            return NULL;
  274. X        else
  275. X            *r = '\0';
  276. X        --anglebrace;
  277. X        } else
  278. X        return NULL;
  279. X    }
  280. X    if (t[0] == '@') {
  281. X        /* Chop off any invalid stuff after the address. */
  282. X        if (r = any(index(t, ':'), " \t,(<"))
  283. X        *r = '\0';
  284. X    }
  285. X    } else
  286. X    return NULL;
  287. X    /* Remember where we are so we can return it. */
  288. X    at += strlen(t) + 1;
  289. X    /*
  290. X     * Unscramble the route, if present.
  291. X     *  NOTE:  We assume that a route is present in only two cases:
  292. X     *   1) addr was taken from the "From " line of a stupid mailer
  293. X     *   2) addr was a well-formed, <> enclosed RFC822 address
  294. X     */
  295. X    if (t[0] == '@') {
  296. X#ifdef UUCP
  297. X    if (!bang_form(s, t))
  298. X        return NULL;
  299. X#else /* UUCP */
  300. X    if (r = index(t, ':'))
  301. X        r++;
  302. X    else
  303. X        return NULL;
  304. X    /* Delete the route if extraneous, otherwise unwind it. */
  305. X    if (i = index(r, '@'))
  306. X        (void) strcpy(s, r);
  307. X    else {
  308. X        /*
  309. X         * NOTE:  Unwinding currently uses only the rightmost domain
  310. X         *  in the route.  This will break for mailers that need the
  311. X         *  entire route.  Complete unwinding would require the use
  312. X         *  of % characters, which are avoided for other reasons.
  313. X         */
  314. X        (void) strcpy(s, r);
  315. X        *(--r) = '\0';
  316. X        (void) strcat(s, t);
  317. X    }
  318. X#endif /* UUCP */
  319. X    } else
  320. X    (void) strcpy(s, t);
  321. X    /*
  322. X     * Ok, now the address should be in the form user@domain and
  323. X     *  is held in buffer s (t[] is not copied directly to naddr
  324. X     *  to allow future additional processing to be added here).
  325. X     */
  326. X    if (debug > 1) /* Don't dump this on trivial debugging */
  327. X    wprint("Converting \"%s\" to \"%s\"\n", addr, s);
  328. X    (void) strcpy(naddr, s);
  329. X    return at;
  330. X}
  331. X
  332. X/*
  333. X * Convert RFC822 or mixed addresses to RFC976 `!' form,
  334. X *  copying the new address to d.  The source address is
  335. X *  translated according to RFC822 rules.
  336. X * Return a pointer to the end (nul terminus) of d.
  337. X */
  338. Xchar *
  339. Xbang_form (d, s)
  340. Xchar *d, *s;
  341. X{
  342. X    char *r, *t, *ab = NULL;
  343. X
  344. X    *d = '\0';
  345. X    /* If nothing to do, quit now */
  346. X    if (!s || !*s) {
  347. X    return d;
  348. X    }
  349. X    /* Avoid any angle braces */
  350. X    if (*s == '<') {
  351. X    if (ab = index(s + 1, '>'))
  352. X        s++, *ab = '\0';
  353. X    else
  354. X        return NULL;
  355. X    }
  356. X    /*
  357. X     * Look backwards for the first `@'; this gives us the
  358. X     * primary domain of the RFC822 address
  359. X     */
  360. X    if (*s == '@') {
  361. X    /* An RFC-822 "@domain1,@domain2:" routing */
  362. X    if (t = any(++s, ",:")) {
  363. X        char c = *t;
  364. X        *t = '\0';
  365. X        d += Strcpy(d, s);
  366. X        *d++ = '!';
  367. X        *t++ = c;
  368. X        r = bang_form(d, t);
  369. X    } else
  370. X        r = NULL;
  371. X    } else if ((t = rindex(s, '@')) && t != s) {
  372. X    /* Copy the RFC822 domain as the UUCP head */
  373. X    d += Strcpy(d, t + 1);
  374. X    *d++ = '!';
  375. X    *t = '\0';
  376. X    r = bang_form(d, s);
  377. X    *t = '@';
  378. X    } else if (t = index(s, '!')) {
  379. X    /* A normal UUCP path */
  380. X    *t = '\0';
  381. X    d += Strcpy(d, s);
  382. X    *t++ = *d++ = '!';
  383. X    r = bang_form(d, t);
  384. X    } else if (t = rindex(s, '%')) {
  385. X    /* An imbedded `%' -- treat as low-priority `@' */
  386. X    *t = '@';
  387. X    r = bang_form(d, s);
  388. X    *t = '%';
  389. X    } else
  390. X    r = d + Strcpy(d, s);  /* No `@', `!', or `%' */
  391. X    if (ab)
  392. X    *ab = '>';
  393. X    return r;
  394. X}
  395. X
  396. X/*
  397. X * Route addresses according to certain criteria.  This function is really
  398. X * just a front end for improve_uucp_paths() which does routing (differently).
  399. X * If "route" is null, this routine is being called incorrectly.
  400. X * If route is an address, just call improve_uucp_paths() and return.
  401. X * If route is the null string, then route all addresses via the sender's
  402. X * which is the first name/address on the To: list. If he's on a remote
  403. X * machine, chances are that the addresses of everyone else he mailed to
  404. X * are addresses from his machine.  Reconstruct those addresses to route
  405. X * thru the senders machine first.
  406. X */
  407. Xroute_addresses(to, cc, route_path)
  408. Xchar *to, *cc, *route_path;
  409. X{
  410. X    char pre_path[256], sender[HDRSIZ], tmp[256];
  411. X    register char *next, *p;
  412. X    int c;
  413. X
  414. X    Debug("route_addresses()\n");
  415. X    if (!route_path)
  416. X    return;
  417. X    if (*route_path) {
  418. X    improve_uucp_paths(to, HDRSIZ, route_path);
  419. X    improve_uucp_paths(cc, HDRSIZ, route_path);
  420. X    return;
  421. X    }
  422. X
  423. X    pre_path[0] = 0;
  424. X    /* Get the address of the sender (which is always listed first) */
  425. X    if (!(next = get_name_n_addr(to, NULL, NULL)))
  426. X    return;
  427. X    c = *next, *next = 0;
  428. X    (void) strcpy(sender, to);
  429. X    *next = c;
  430. X    /* fix up the sender's address; improve_uucp_paths to optimize pre_path */
  431. X    improve_uucp_paths(sender, sizeof sender, NULL);
  432. X
  433. X    /* check to see if there is only one addr on To: line and no Cc: header */
  434. X    if (!*next && (!cc || !*cc)) {
  435. X    (void) strcpy(to, sender);
  436. X    return;
  437. X    }
  438. X    /* otherwise, get the pre_path */
  439. X    if (p = get_name_n_addr(sender, NULL, tmp))
  440. X    c = p - sender; /* save the original length */
  441. X    if (*tmp) {
  442. X    (void) bang_form(pre_path, tmp);
  443. X    if (p = rindex(pre_path, '!')) {
  444. X        *p = 0;
  445. X        Debug("Routing thru \"%s\"\n", pre_path);
  446. X    } else
  447. X        pre_path[0] = 0;
  448. X    } else
  449. X    pre_path[0] = 0;
  450. X
  451. X    while (*next == ',' || isspace(*next))
  452. X    next++;
  453. X    improve_uucp_paths(next, HDRSIZ - (int)(next - to), pre_path);
  454. X    improve_uucp_paths(cc, HDRSIZ, pre_path);
  455. X    p = sender + c;
  456. X    *p++ = ',', *p++ = ' ';
  457. X    (void) strcpy(p, next);
  458. X    (void) strcpy(to, sender);
  459. X}
  460. X
  461. X/*
  462. X * pass a string describing header like, "Subject: ", current value, and
  463. X * whether or not to prompt for it or to just post the information.
  464. X * If do_prompt is true, "type in" the current value so user can either
  465. X * modify it, erase it, or add to it.
  466. X */
  467. Xchar *
  468. Xset_header(str, curstr, do_prompt)
  469. Xregister char *str, *curstr;
  470. X{
  471. X    static char       buf[HDRSIZ];
  472. X    int        offset = 0;
  473. X    register char  *p = curstr;
  474. X
  475. X    if (!str)
  476. X    str = "";
  477. X
  478. X    buf[0] = 0;
  479. X    print(str);
  480. X    (void) fflush(stdout);         /* force str curstr */
  481. X    if (do_prompt) {
  482. X    if (curstr)
  483. X        if (isoff(glob_flags, ECHO_FLAG)) {
  484. X        Ungetstr(curstr);
  485. X        } else
  486. X#ifdef TIOCSTI
  487. X        for (p = curstr; *p; p++)
  488. X            if (ioctl(0, TIOCSTI, p) == -1) {
  489. X            error("ioctl: TIOCSTI");
  490. X            print("You must retype the entire line.\n%s", str);
  491. X            break;
  492. X            }
  493. X#else /* !TIOCSTI */
  494. X        print("WARNING: -e flag! Type the line over.\n%s", str);
  495. X#endif /* TIOCSTI */
  496. X
  497. X    if (istool)
  498. X        return NULL;
  499. X    /* simulate the fact that we're getting input for the letter even tho
  500. X     * we may not be.  set_header is called before IS_GETTING is true,
  501. X     * but if we set it to true temporarily, then signals will return to
  502. X     * the right place (stop/continue).
  503. X     */
  504. X    {
  505. X        u_long getting = ison(glob_flags, IS_GETTING);
  506. X        int wrapping = wrapcolumn;
  507. X        /* Funky trick here.  If the prompt string is empty,
  508. X         * assume that we are allowed to do line wrap;
  509. X         * otherwise, temporarily disable line wrap
  510. X         */
  511. X        if (*str)
  512. X        wrapcolumn = 0;
  513. X        if (!getting)
  514. X        turnon(glob_flags, IS_GETTING);
  515. X        if (Getstr(buf, sizeof(buf), offset) == -1) {
  516. X        putchar('\n');
  517. X        buf[0] = 0;
  518. X        }
  519. X        if (!getting)
  520. X        turnoff(glob_flags, IS_GETTING);
  521. X        wrapcolumn = wrapping;
  522. X    }
  523. X    } else
  524. X    puts(strcpy(buf, curstr));
  525. X    if (debug > 1)
  526. X    print("returning (%s) from set_header\n", buf);
  527. X    return buf;
  528. X}
  529. X
  530. X/*
  531. X * improve uucp paths by looking at the name of each host listed in the
  532. X * path given.
  533. X *    sun!island!pixar!island!argv
  534. X * It's a legal address, but redundant. Also, if we know we talk to particular
  535. X * hosts via uucp, then we can just start with that host and disregard the path
  536. X * preceding it.  So, first get the known hosts and save them. Then start
  537. X * at the end of the original path (at the last ! found), and move backwards
  538. X * saving each hostname.  If we get to a host that we know about, stop there
  539. X * and use that address.  If the system knows about domains, skip all paths
  540. X * that precede a domain hostname.  If we get to a host we've already seen,
  541. X * then delete it and all the hosts since then until the first occurrence of
  542. X * that hostname.  When we get to the beginning, the address will be complete.
  543. X * The route_path is prepended to each address to check make sure this path
  544. X * is used if no known_hosts precede it in that address.
  545. X *
  546. X * Return all results into the original buffer passed to us.  If route_path
  547. X * adds to the length of all the paths, then the original buffer could be
  548. X * overwritten.  someone should check for this!
  549. X */
  550. Ximprove_uucp_paths(original, size, route_path)
  551. Xchar *original, *route_path;
  552. X{
  553. X    char       name[256], addr[256], buf[2 * HDRSIZ], *end;
  554. X    char      *hostnames[32], tmp[sizeof addr], *domain_path;
  555. X    register char *p, *p2, *recipient, *start = original, *b = buf;
  556. X    int           saved_hosts, i, is_domain;
  557. X
  558. X    if (!original || !*original)
  559. X    return;
  560. X
  561. X    /* use domain_path to point to the path for pathnames that have
  562. X     * a fully qualified domain host in them.
  563. X     */
  564. X    domain_path = do_set(set_options, "domain_route");
  565. X    while (end = get_name_n_addr(start, name, tmp)) {
  566. X    /* first copy the route path, then the rest of the address. */
  567. X    p = addr;
  568. X    if (route_path && *route_path) {
  569. X        p += Strcpy(addr, route_path);
  570. X        *p++ = '!';
  571. X    }
  572. X    (void) bang_form(p, tmp);
  573. X    saved_hosts = 0;
  574. X    if (p2 = rindex(p, '!')) {
  575. X        recipient = p2+1;
  576. X        /* save the uucp-style address *without* route_path in tmp */
  577. X        (void) strcpy(tmp, p);
  578. X        for (p = p2; p > addr; p--) {
  579. X        is_domain = 0;
  580. X        /* null the '!' separating the rest of the path from the part
  581. X         * of the path preceding it and move p back to the previous
  582. X         * '!' (or beginning to addr) for hostname to point to.
  583. X         */
  584. X        for (*p-- = 0; p > addr && *p != '!'; p--)
  585. X            if (!is_domain && domain_path && *p == '.' &&
  586. X                lcase_strncmp(p, ".uucp", 5))
  587. X            is_domain++;
  588. X        /* if p is not at the addr, move it forward past the '!' */
  589. X        if (p != addr)
  590. X            ++p; /* now points to a null terminated hostname */
  591. X        /* if host is ourselves, ignore this and preceding hosts */
  592. X        for (i = 0; ourname && ourname[i]; i++)
  593. X            if (!lcase_strncmp(p, ourname[i], -1))
  594. X            break;
  595. X        if (ourname && ourname[i]) {
  596. X            is_domain = 0; /* we've eliminated all domains */
  597. X            break;
  598. X        }
  599. X        /* check already saved hostnames. If host is one of them,
  600. X         * delete remaining hostnames since there is a redundant path.
  601. X         */
  602. X        for (i = 0; i < saved_hosts; i++)
  603. X            if (!lcase_strncmp(hostnames[i], p, -1))
  604. X            saved_hosts = i;
  605. X
  606. X        /* Add the hostname to the path being constructed */
  607. X        hostnames[saved_hosts++] = p;
  608. X
  609. X        /* If the original path or the address is a fully qualified
  610. X         * hostname (domain info is included), then break here
  611. X         */
  612. X        if (p == addr || is_domain && domain_path)
  613. X            break;
  614. X        /* If we know that we call this host, break */
  615. X        for (i = 0; known_hosts && known_hosts[i]; i++)
  616. X            if (!lcase_strncmp(p, known_hosts[i], -1))
  617. X            break;
  618. X        if (known_hosts && known_hosts[i])
  619. X            break;
  620. X        }
  621. X        /* temporary holder for where we are in buffer (save address) */
  622. X        p2 = b;
  623. X        if (is_domain && domain_path && *domain_path)
  624. X        b += Strcpy(b, domain_path), *b++ = '!';
  625. X        while (saved_hosts-- > 0) {
  626. X        b += Strcpy(b, hostnames[saved_hosts]);
  627. X        *b++ = '!';
  628. X        }
  629. X        b += Strcpy(b, recipient);
  630. X        if (!strcmp(p2, tmp)) { /* if the same, address was unmodified */
  631. X        b = p2; /* reset offset in buf (b) to where we were (p2) */
  632. X        goto unmodified;
  633. X        }
  634. X        if (*name)
  635. X        b += strlen(sprintf(b, " (%s)", name));
  636. X    } else {
  637. X        char c;
  638. Xunmodified:
  639. X        c = *end;
  640. X        *end = 0;
  641. X        b += Strcpy(b, start); /* copy the entire address with comments */
  642. X        *end = c;
  643. X    }
  644. X    if (b - buf > size) {
  645. X        wprint("Warning: address list truncated!\n");
  646. X        /* Use a very poor heuristic to find the last complete address */
  647. X        for (b = buf+size - 1; *b != ','; b--)
  648. X        ;
  649. X        wprint("Lost addresses: %s%s\n", b, end); /* end = not yet parsed */
  650. X        while (isspace(*b) || *b == ',')
  651. X        b--;
  652. X        break;
  653. X    }
  654. X    for (start = end; *start == ',' || isspace(*start); start++)
  655. X        ;
  656. X    if (!*start)
  657. X        break;
  658. X    *b++ = ',', *b++ = ' ', *b = '\0';
  659. X    }
  660. X    (void) strcpy(original, buf);
  661. X}
  662. X
  663. X/*
  664. X * rm_cmts_in_addr() removes the comment lines in addresses that result from
  665. X * sendmail or other mailers which append the user's "real name" on the
  666. X * from lines.  See get_name_n_addr().
  667. X */
  668. Xrm_cmts_in_addr(str)
  669. Xregister char *str;
  670. X{
  671. X    char addr[BUFSIZ], buf[HDRSIZ], *start = str;
  672. X    register char *b = buf;
  673. X
  674. X    *b = 0;
  675. X    do  {
  676. X    if (!(str = get_name_n_addr(str, NULL, addr)))
  677. X        break;
  678. X    b += Strcpy(b, addr);
  679. X    while (*str == ',' || isspace(*str))
  680. X        str++;
  681. X    if (*str)
  682. X        *b++ = ',', *b++ = ' ', *b = '\0';
  683. X    } while (*str);
  684. X    for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  685. X    *b = 0;
  686. X    (void) strcpy(start, buf);
  687. X}
  688. X
  689. X/*
  690. X * take_me_off() is intended to search for the user's login name in an
  691. X * address string and remove it.  If "metoo" is set, return without change.
  692. X * determine which addresses are the "user'"'s addresses by comparing them
  693. X * against the host/path names in alternates.  If the "*" is used, then
  694. X * this matches the address against the user's current login and -any- path.
  695. X *
  696. X * Note that the alternates list is an array of addresses stored *reversed*!
  697. X */
  698. Xtake_me_off(str)
  699. Xchar *str;
  700. X{
  701. X    int i = 0, rm_me;
  702. X    char tmp[256], addr[256], buf[HDRSIZ], *start = str;
  703. X    register char *p, *p2, *b = buf;
  704. X
  705. X    if (!str || !*str)
  706. X    return;
  707. X
  708. X    Debug("take_me_off()\n");
  709. X    *b = 0;
  710. X    do  {
  711. X    rm_me = FALSE;
  712. X    /* get the first "address" and advance p to next addr (ignore name) */
  713. X    if (!(p = get_name_n_addr(str, NULL, tmp)))
  714. X        break; /* we've reached the end of the address list */
  715. X    /* see if user's login is in the address */
  716. X    if (!strcmp(login, tmp))
  717. X        rm_me = TRUE;
  718. X    else {
  719. X        int len;
  720. X        /* put address in !-format and store in "addr" */
  721. X        (void) bang_form(addr, tmp);
  722. X        (void) reverse(addr);
  723. X        for (i = 0; alternates && alternates[i] && !rm_me; i++) {
  724. X        if (alternates[i][0] == '*') {
  725. X            if (alternates[i][1] == '\0')
  726. X            p2 = reverse(strcpy(tmp, login));
  727. X            else
  728. X            p2 = reverse(strcpy(tmp, &alternates[i][1]));
  729. X        } else
  730. X            p2 = alternates[i];
  731. X        if (!lcase_strncmp(p2, addr, (len = strlen(p2))) &&
  732. X            (!addr[len] || addr[len] == '!')) {
  733. X            Debug("\t%s\n", reverse(addr));
  734. X            rm_me = TRUE;
  735. X        }
  736. X        }
  737. X        for (i = 0; !rm_me && ourname && ourname[i]; i++) {
  738. X        p2 = tmp + Strcpy(tmp, ourname[i]);
  739. X        *p2++ = '!';
  740. X        (void) strcpy(p2, login);
  741. X        (void) reverse(tmp);
  742. X        if (!lcase_strncmp(tmp, addr, (len = strlen(tmp))) &&
  743. X            (!addr[len] || addr[len] == '!')) {
  744. X            Debug("\t%s\n", reverse(addr));
  745. X            rm_me = TRUE;
  746. X        }
  747. X        }
  748. X    }
  749. X    /* The address is not the user's -- put it into the returned list */
  750. X    if (!rm_me) {
  751. X        char c = *p;
  752. X        *p = 0;
  753. X        b += Strcpy(b, str);
  754. X        *p = c;
  755. X    }
  756. X    while (*p == ',' || isspace(*p))
  757. X        p++;
  758. X    if (*p && !rm_me)
  759. X        *b++ = ',', *b++ = ' ', *b = '\0';
  760. X    } while (*(str = p));
  761. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  762. X    *b = 0;
  763. X    (void) strcpy(start, buf);
  764. X}
  765. X
  766. X/*
  767. X * Place commas in between all addresses that don't already have
  768. X * them.  Addresses which use comments which are in parens or _not_
  769. X * within angle brackets *must* already have commas around them or
  770. X * you can't determine what is a comment and what is an address.
  771. X */
  772. Xfix_up_addr(str)
  773. Xchar *str;
  774. X{
  775. X    char buf[HDRSIZ], *start = str;
  776. X    register char c, *p, *b = buf;
  777. X
  778. X    *b = 0;
  779. X    do  {
  780. X    /* get_name returns a pointer to the next address */
  781. X    if (!(p = get_name_n_addr(str, NULL, NULL)))
  782. X        break;
  783. X    c = *p, *p = 0;
  784. X    if (strlen(str) + (b - buf) >= sizeof(buf) - 2) {
  785. X        /* wprint("Address too long! Lost address: \"%s\"\n", str); */
  786. X        *p = c;
  787. X        break;
  788. X    }
  789. X    for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  790. X        *b = 0;
  791. X    for (*p = c; *p == ',' || isspace(*p); p++)
  792. X        ;
  793. X    if (*p)
  794. X        *b++ = ',', *b++ = ' ', *b = '\0';
  795. X    } while (*(str = p));
  796. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  797. X    *b = 0;
  798. X    (void) strcpy(start, buf);
  799. X}
  800. X
  801. X/*
  802. X * Remove redundant addresses.
  803. X * Assume improve_uucp_paths, fix_up_addr or whatever have already been called.
  804. X */
  805. Xrm_redundant_addrs(to, cc)
  806. Xchar *to, *cc;
  807. X{
  808. X    char tmp[256], addr[256], buf[HDRSIZ];
  809. X    char **list; /* a list of addresses for comparison */
  810. X    int list_cnt = 0, l;
  811. X    register char c, *p, *b, *start;
  812. X
  813. X    Debug("rm_redundant_addrs()\n");
  814. X    list = (char **) calloc(256, sizeof(char *));
  815. X    if (!list) {
  816. X    error("out of memory in rm_redundant_addrs");
  817. X    return;
  818. X    }
  819. X    start = to;
  820. X    b = buf, *b = 0;
  821. X    /* first do the To header */
  822. X    do  {
  823. X    /* get_name returns a pointer to the next address */
  824. X    if (!(p = get_name_n_addr(to, NULL, tmp)))
  825. X        break;
  826. X    c = *p, *p = 0;
  827. X    (void) bang_form(addr, tmp);
  828. X    for (l = 0; l < list_cnt; l++)
  829. X        if (!lcase_strncmp(addr, list[l], -1))
  830. X        break;
  831. X    /* if l == list_cnt, we got a new address, store it and add to buf */
  832. X    if (l == list_cnt) {
  833. X        /* Don't overwrite buffer. */
  834. X        if (list_cnt < 256)
  835. X        list[list_cnt++] = savestr(addr);
  836. X        if (b > buf)
  837. X        *b++ = ',', *b++ = ' ', *b = '\0';
  838. X        for (b += Strcpy(b, to); b > buf && isspace(*(b-1)); b--)
  839. X        *b = 0;
  840. X    } else
  841. X        Debug("\t%s\n", tmp); /* already specified (removed from list) */
  842. X    for (*p = c; *p == ',' || isspace(*p); p++)
  843. X        ;
  844. X    } while (*(to = p));
  845. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  846. X    *b = 0;
  847. X    (void) strcpy(start, buf);
  848. X    b = buf, *b = 0;
  849. X    /* Now do the Cc header.  If addr is listed in the To field, rm it in cc */
  850. X    start = cc;
  851. X    do  {
  852. X    /* get_name returns a pointer to the next address */
  853. X    if (!(p = get_name_n_addr(cc, NULL, tmp)))
  854. X        break;
  855. X    c = *p, *p = 0;
  856. X    (void) bang_form(addr, tmp);
  857. X    for (l = 0; l < list_cnt; l++)
  858. X        if (!lcase_strncmp(addr, list[l], -1))
  859. X        break;
  860. X    if (l == list_cnt) {
  861. X        /* Don't overwrite buffer. */
  862. X        if (list_cnt < sizeof(list)/sizeof(char *))
  863. X        list[list_cnt++] = savestr(addr);
  864. X        if (b > buf)
  865. X        *b++ = ',', *b++ = ' ', *b = '\0';
  866. X        for (b += Strcpy(b, cc); b > buf && isspace(*(b-1)); b--)
  867. X        *b = 0;
  868. X    } else
  869. X        Debug("\t%s\n", tmp); /* already specified (removed from list) */
  870. X    for (*p = c; *p == ',' || isspace(*p); p++)
  871. X        ;
  872. X    } while (*(cc = p));
  873. X    list[list_cnt] = NULL; /* for free_vec */
  874. X    free_vec(list);
  875. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  876. X    *b = 0;
  877. X    (void) strcpy(start, buf);
  878. X}
  879. X
  880. X/*
  881. X * Get address and name from a string (str) which came from an address header
  882. X * in a message or typed by the user.  The string may contain one or more
  883. X * well-formed addresses.  Each must be separated by a comma.
  884. X *
  885. X * address, address, address
  886. X * address (comment or name here)
  887. X * comment or name <address>
  888. X * "Comment, even those with comma's!" <address>
  889. X * address (comma, (more parens), etc...)
  890. X *
  891. X * This does *not* handle cases like:
  892. X *    comment <address (comment)>
  893. X *
  894. X * find the *first* address here and return a pointer to the end of the
  895. X * address (usually a comma).  Return NULL on error: non-matching parens,
  896. X * brackets, quotes...
  897. X */
  898. Xchar *
  899. Xget_name_n_addr(str, name, addr)
  900. Xregister char *str, *name, *addr;
  901. X{
  902. X    register char *p, *p2, *beg_addr = addr, *beg_name = name, c;
  903. X
  904. X    if (addr)
  905. X    *addr = 0;
  906. X    if (name)
  907. X    *name = 0;
  908. X    if (!str || !*str)
  909. X    return NULL;
  910. X
  911. X    while (isspace(*str))
  912. X    str++;
  913. X
  914. X    /* first check to see if there's something to look for */
  915. X    if (!(p = any(str, ",(<\""))) {
  916. X    /* no comma or indication of a quote character. Find a space and
  917. X     * return that.  If nothing, the entire string is a complete address
  918. X     */
  919. X    if (p = any(str, " \t"))
  920. X        c = *p, *p = 0;
  921. X    if (addr)
  922. X        (void) strcpy(addr, str);
  923. X    if (p)
  924. X        *p = c;
  925. X    return p? p : str + strlen(str);
  926. X    }
  927. X
  928. X    /* comma terminated before any comment stuff.  If so, check for whitespace
  929. X     * before-hand cuz it's possible that strings aren't comma separated yet
  930. X     * and they need to be.
  931. X     *
  932. X     * address address address, address
  933. X     *                        ^p  <- p points here.
  934. X     *        ^p2 <- should point here.
  935. X     */
  936. X    if (*p == ',') {
  937. X    c = *p, *p = 0;
  938. X    if (p2 = any(str, " \t"))
  939. X        *p = ',', c = *p2, p = p2, *p = 0;
  940. X    if (addr)
  941. X        (void) strcpy(addr, str);
  942. X    *p = c;
  943. X    return p;
  944. X    }
  945. X
  946. X    /* starting to get hairy -- we found an angle bracket. This means that
  947. X     * everything outside of those brackets are comments until we find that
  948. X     * all important comma.  A comment AFTER the <addr> :
  949. X     *  <address> John Doe
  950. X     * can't call this function recursively or it'll think that "John Doe"
  951. X     * is a string with two legal address on it (each name being an address).
  952. X     */
  953. X    if (*p == '<') { /* note that "str" still points to comment stuff! */
  954. X    if (name && *str) {
  955. X        *p = 0;
  956. X        name += Strcpy(name, str);
  957. X        *p = '<';
  958. X    }
  959. X    if (!(p2 = index(p+1, '>'))) {
  960. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  961. X        return NULL;
  962. X    }
  963. X    if (addr) {
  964. X        /* to support <addr (comment)> style addresses, add code here */
  965. X        *p2 = 0;
  966. X        skipspaces(1);
  967. X        addr += Strcpy(addr, p);
  968. X        while (addr > beg_addr && isspace(*(addr-1)))
  969. X        *--addr = 0;
  970. X        *p2 = '>';
  971. X    }
  972. X    /* take care of the case "... <addr> com (ment)" */
  973. X    {
  974. X        int p_cnt = 0; /* parenthesis counter */
  975. X        p = p2;
  976. X        /* don't recurse yet -- scan till null, comma or '<'(add to name) */
  977. X        for (p = p2; p[1] && (p_cnt || p[1] != ',' && p[1] != '<'); p++) {
  978. X        if (p[1] == '(')
  979. X            p_cnt++;
  980. X        else if (p[1] == ')')
  981. X            p_cnt--;
  982. X        if (name)
  983. X            *name++ = p[1];
  984. X        }
  985. X        if (p_cnt) {
  986. X        wprint("Warning! Malformed name: \"%s\"\n", name);
  987. X        return NULL;
  988. X        }
  989. X    }
  990. X    if (name && name > beg_name) {
  991. X        while (isspace(*(name-1)))
  992. X        --name;
  993. X        *name = 0;
  994. X    }
  995. X    }
  996. X
  997. X    /* this is the worst -- now we have parentheses/quotes.  These guys can
  998. X     * recurse pretty badly and contain commas within them.
  999. X     */
  1000. X    if (*p == '(' || *p == '"') {
  1001. X    char *start = p;
  1002. X    int comment = 1;
  1003. X    c = *p;
  1004. X    /* "str" points to address while p points to comments */
  1005. X    if (addr && *str) {
  1006. X        *p = 0;
  1007. X        while (isspace(*str))
  1008. X        str++;
  1009. X        addr += Strcpy(addr, str);
  1010. X        while (addr > beg_addr && isspace(*(addr-1)))
  1011. X        *--addr = 0;
  1012. X        *p = c;
  1013. X    }
  1014. X    while (comment) {
  1015. X        if (c == '"' && !(p = index(p+1, '"')) ||
  1016. X        c == '(' && !(p = any(p+1, "()"))) {
  1017. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  1018. X        return NULL;
  1019. X        }
  1020. X        if (*p == '(') /* loop again on parenthesis. quote ends loop */
  1021. X        comment++;
  1022. X        else
  1023. X        comment--;
  1024. X    }
  1025. X    /* Something like ``Comment (Comment) <addr>''.  In this case
  1026. X     * the name should include both comment parts with the
  1027. X     * parenthesis.   We have to redo addr.
  1028. X     */
  1029. X    if ((p2 = any(p+1, "<,")) && *p2 == '<') {
  1030. X        if (!(p = index(p2, '>'))) {
  1031. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  1032. X        return NULL;
  1033. X        }
  1034. X        if (addr = beg_addr) { /* reassign addr and compare to null */
  1035. X        c = *p; *p = 0;
  1036. X        addr += Strcpy(addr, p2+1);
  1037. X        while (addr > beg_addr && isspace(*(addr-1)))
  1038. X            *--addr = 0;
  1039. X        *p = c;
  1040. X        }
  1041. X        if (name) {
  1042. X        c = *p2; *p2 = 0;
  1043. X        name += Strcpy(name, str);
  1044. X        while (name > beg_name && isspace(*(name-1)))
  1045. X            *--name = 0;
  1046. X        *p2 = c;
  1047. X        }
  1048. X    } else if (name && start[1]) {
  1049. X        c = *p, *p = 0; /* c may be ')' instead of '(' now */
  1050. X        name += Strcpy(name, start+1);
  1051. X        while (name > beg_name && isspace(*(name-1)))
  1052. X        *--name = 0;
  1053. X        *p = c;
  1054. X    }
  1055. X    }
  1056. X    skipspaces(1);
  1057. X    /* this is so common, save time by returning now */
  1058. X    if (!*p || *p == ',')
  1059. X    return p;
  1060. X    return get_name_n_addr(p, name, addr);
  1061. X}
  1062. X
  1063. X/* takes string 's' which can be a name or list of names separated by
  1064. X * commas and checks to see if each is aliased to something else.
  1065. X * return address of the static buf.
  1066. X */
  1067. Xchar *
  1068. Xalias_to_address(s)
  1069. Xregister char *s;
  1070. X{
  1071. X    static char buf[HDRSIZ];
  1072. X    register char *p, *p2, *tmp;
  1073. X    char newbuf[HDRSIZ], c;
  1074. X    static int recursive;
  1075. X
  1076. X    if (!aliases)
  1077. X    return strcpy(buf, s);
  1078. X    if (!s || !*s)
  1079. X    return NULL;
  1080. X    if (!recursive) {
  1081. X    bzero(buf, sizeof buf);
  1082. X    p2 = buf;  /* if we're starting all this, p2 starts at &buf[0] */
  1083. X    } else
  1084. X    p2 = buf+strlen(buf);   /* else, pick up where we left off */
  1085. X
  1086. X    if (++recursive == 30) {
  1087. X    wprint("alias references too many addresses!\n");
  1088. X    recursive = 0;
  1089. X    return NULL;
  1090. X    }
  1091. X    do  {
  1092. X    char addr[256];
  1093. X    if (!(p = get_name_n_addr(s, NULL, addr)))
  1094. X        break;
  1095. X    c = *p, *p = 0;
  1096. X
  1097. X    /* On recursive calls, compare against the entire
  1098. X     * previous expansion, not just the address part.
  1099. X     */
  1100. X    if (recursive > 1)
  1101. X        (void) strcpy(addr, s);
  1102. X
  1103. X    /* if this is an alias, recurse this routine to expand it out */
  1104. X    if ((tmp = do_set(aliases, addr)) && *tmp) {
  1105. X        if (!alias_to_address(strcpy(newbuf, tmp))) {
  1106. X        *p = c;
  1107. X        return NULL;
  1108. X        } else
  1109. X        p2 = buf+strlen(buf);
  1110. X    /* Now, make sure the buffer doesn't overflow */
  1111. X    } else if (strlen(s) + (p2-buf) + 2 > sizeof buf) {  /* add ", " */
  1112. X        wprint("address length too long.\n");
  1113. X        recursive = 0;
  1114. X        *p = c;
  1115. X        return NULL;
  1116. X    } else {
  1117. X        /* append the new alias (or unchanged address) onto the buffer */
  1118. X        p2 += Strcpy(p2, s);
  1119. X        *p2++ = ',', *p2++ = ' ', *p2 = '\0';
  1120. X    }
  1121. X    for (*p = c; *p == ',' || isspace(*p); p++)
  1122. X        ;
  1123. X    } while (*(s = p));
  1124. X    if (recursive)
  1125. X    recursive--;
  1126. X    if (!recursive)
  1127. X    *(p2-2) = 0;  /* get rid of last ", " if end of recursion */
  1128. X    return buf;
  1129. X}
  1130. X
  1131. X/*
  1132. X * Wrap addresses so that the headers don't exceed n chars (typically 80).
  1133. X */
  1134. Xchar *
  1135. Xwrap_addrs(str, n)
  1136. Xchar *str;
  1137. X{
  1138. X    char buf[HDRSIZ * 2], *start = str;
  1139. X    register char *b = buf, *p, c, *line_start = buf;
  1140. X
  1141. X    *b = 0;
  1142. X    do  {
  1143. X    /* get_name returns a pointer to the next address */
  1144. X    if (!(p = get_name_n_addr(str, NULL, NULL)))
  1145. X        break;
  1146. X    c = *p, *p = 0;
  1147. X    if (b > buf) {
  1148. X        *b++ = ',', *b++ = ' ', *b = '\0';
  1149. X        if (b - line_start + strlen(str) + 8 /* \t = 8 */ >= n)
  1150. X        *b++ = '\n', *b++ = '\t', line_start = b;
  1151. X    }
  1152. X    for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  1153. X        *b = 0;
  1154. X    for (*p = c; *p == ',' || isspace(*p); p++)
  1155. X        ;
  1156. X    } while (*(str = p));
  1157. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  1158. X    *b = 0;
  1159. X    return strcpy(start, buf);
  1160. X}
  1161. END_OF_FILE
  1162. if test 33359 -ne `wc -c <'mush/addrs.c'`; then
  1163.     echo shar: \"'mush/addrs.c'\" unpacked with wrong size!
  1164. fi
  1165. # end of 'mush/addrs.c'
  1166. fi
  1167. if test -f 'mush/viewopts.c' -a "${1}" != "-c" ; then 
  1168.   echo shar: Will not clobber existing file \"'mush/viewopts.c'\"
  1169. else
  1170. echo shar: Extracting \"'mush/viewopts.c'\" \(18292 characters\)
  1171. sed "s/^X//" >'mush/viewopts.c' <<'END_OF_FILE'
  1172. X/* @(#)viewopts.c    (c) copyright    10/18/86 (Dan Heller) */
  1173. X
  1174. X#include "mush.h"
  1175. X
  1176. Xstruct viewopts {
  1177. X    char *v_opt;
  1178. X    char *v_prompt;
  1179. X    char *v_description;
  1180. X#ifdef SUNTOOL
  1181. X    Panel_item v_choice;
  1182. X    Panel_item v_text;
  1183. X#endif /* SUNTOOL */
  1184. X};
  1185. X
  1186. X#ifdef SUNTOOL
  1187. Xshort dat_cycle_cursor[] = {
  1188. X    0x07C0, 0x0FE0, 0x1834, 0x301C, 0x601C, 0x203C, 0x0000, 0x0000,
  1189. X    0x7808, 0x700C, 0x7018, 0x5830, 0x0FE0, 0x07C0, 0x0000, 0x0000
  1190. X
  1191. X};
  1192. Xmpr_static(cycle,           16, 16, 1, dat_cycle_cursor);
  1193. X#endif /* SUNTOOL */
  1194. X
  1195. X/*
  1196. X * struct contains the option, a prompt if it has a string value, whether
  1197. X * or not it applies to non suntools, line mode, or both, and a
  1198. X * string describing what the option does. If the prompt string starts
  1199. X * with a minus sign, then the value can be set without a value. This
  1200. X * is there to indicate to option_line to print a toggle (cycle) pixrect
  1201. X * and to print TRUE/FALSE telling whether the value is on or off regardless
  1202. X * of it's "string" value.
  1203. X */
  1204. Xstruct viewopts viewopts[] = {
  1205. X    { "alwaysignore", NULL,
  1206. X    "Always ignore the message headers on the 'ignored' list." },
  1207. X    { "ask", NULL,
  1208. X    "Prompts for a subject on outgoing mail." },
  1209. X    { "askcc", NULL,
  1210. X    "Ask for list of Carbon Copy recipients whenever sending mail." },
  1211. X    { "autodelete", NULL,
  1212. X    "Automatically delete ALL READ messages whenever you update mail." },
  1213. X    { "autoedit", NULL,
  1214. X    "Automatically enter editor for REPLIES only (not toolmode)." },
  1215. X    { "autoinclude", NULL,
  1216. X    "Include a copy of author's message each time you reply to mail." },
  1217. X    { "autoprint", NULL,
  1218. X    "Display the next message on the list when you delete a message." },
  1219. X    { "auto_route", "-Host/Path:",
  1220. X    "Remove redundant uucp addresses when replying to messages." },
  1221. X    { "autosign", "-Filename:",
  1222. X    "Add file (~/.signature if set but no value) at end of all letters." },
  1223. X    { "autosign2", "Addr:File:",
  1224. X    "Signature to use for specific addresses. \"addr, ... : <signature>\""},
  1225. X    { "cdpath", "Path:",
  1226. X    "Path to search for directories when the \"cd\" command is issued." },
  1227. X    { "cmd_help", "Path:",
  1228. X    "Location of the general help file for line and curses modes." },
  1229. X    { "complete", "Character:",
  1230. X    "The character typed to cause a word completion to occur." },
  1231. X    { "crt", "Lines:",
  1232. X    "The number of lines a message must have for 'pager' to be invoked." },
  1233. X    { "crt_win", "Lines:",
  1234. X    "Lines in the tool mode text subwindow for paging messages." },
  1235. X    { "curses_help", "-Commands:",
  1236. X    "List of curses commands whose bindings appear in the help display." },
  1237. X    { "date_received", NULL,
  1238. X    "Time displayed for message headers shows date received (or sent)." },
  1239. X    { "dead", "Filename:",
  1240. X    "The name of the file to store dead mail (default = ~/dead.letter)." },
  1241. X    { "domain_route", "-Host/Path:",
  1242. X    "Cause short-circuiting of domain addresses when auto-routing." },
  1243. X    { "dot", NULL,
  1244. X    "Allow \".\" on a line by itself to send letter." },
  1245. X    { "edit_hdrs", NULL,
  1246. X    "Allow headers of messages to be edited using your editor." },
  1247. X    { "editor", "Editor:",
  1248. X    "Editor for message editing (default = env EDITOR or \"vi\")." },
  1249. X    { "escape", "Character:",
  1250. X    "Escape character for extended editing commands (default = ~)." },
  1251. X    { "fignore", "Patterns:",
  1252. X    "Filename extensions or patterns ignored in completions." },
  1253. X    { "folder", "Pathname:",
  1254. X    "Full pathname to the directory where personal folders are kept." },
  1255. X    { "fortune", "-Flag:",
  1256. X    "Add fortune to end of letters.  Flag to \"fortune\" is optional." },
  1257. X    { "fortunates", "Users:",
  1258. X    "Those who will receive fortunes if fortune is set (default: All)." },
  1259. X    { "hdr_format", "Format:",
  1260. X    "Formatting string for headers.  \"headers -?\" or help hdr_format." },
  1261. X    { "history", "Number:",
  1262. X    "How many commands to remember (like csh)." },
  1263. X    { "hold", NULL,
  1264. X    "Read but not deleted messages are saved in spool -- not mbox." },
  1265. X    { "home", "Directory:",
  1266. X    "The user's home directory." },
  1267. X    { "hostname", "Hostname:",
  1268. X    "User-definable name for the name of your machine." },
  1269. X    { "ignore_bang", NULL,
  1270. X    "Ignore '!' as a history reference.  Otherwise, escape by: \\!" },
  1271. X    { "ignoreeof", "-Command:",
  1272. X    "Ignores ^D as exit, or (if set), execute \"command\"." },
  1273. X    { "indent_str", "String:",
  1274. X    "String to offset included messages within your letters." },
  1275. X    { "in_reply_to", "-String:",
  1276. X    "When responding to mail, add In-Reply-To: to message headers." },
  1277. X    { "keepsave", NULL,
  1278. X    "Prevents messages from being marked as `deleted' when you `save'." },
  1279. X    { "known_hosts", "Host list:",
  1280. X    "List of hosts that your site is known to uucp mail to." },
  1281. X    { "logfile", "Filename:",
  1282. X    "Log outgoing mail headers only.  Message text not logged." },
  1283. X    { "mail_icon", "Filename:",
  1284. X    "Alternate pixmap to use when tool is closed to an icon." },
  1285. X    { "mbox", "Filename:",
  1286. X    "Filename to use instead of ~/mbox for default mailbox." },
  1287. X    { "metoo", NULL,
  1288. X    "When replying to mail, metoo preserves your name on mailing list." },
  1289. X    { "mil_time", NULL,
  1290. X    "24-hour military time format is used whenever a time is printed." },
  1291. X    { "msg_win", "Lines:",
  1292. X    "Number of lines in the message composition window for tool mode." },
  1293. X    { "newline", "-Command:",
  1294. X    "Ignore RETURN.  If set to a command, execute that command." },
  1295. X    { "newmail_icon", "Filename:",
  1296. X    "Alternate icon shown when new mail is available." },
  1297. X    { "no_expand", NULL,
  1298. X    "Prevents expansion of Mush aliases in outgoing mail headers." },
  1299. X    { "no_hdrs", NULL,
  1300. X    "If set, personalized headers are NOT inserted to outgoing mail." },
  1301. X    { "no_reverse", NULL,
  1302. X    "Disables reverse video in curses mode -- uses \"bold\" in tool mode."},
  1303. X    { "nonobang", NULL,
  1304. X    "Suppresses errors from unsuccessful history references." },
  1305. X    { "nosave", NULL,
  1306. X    "Prevents aborted mail from being saved in $dead." },
  1307. X    { "output", NULL,
  1308. X    "The message list produced as output of the last command." },
  1309. X    { "pager", "Program:",
  1310. X    "Program name to be used as a pager for messages longer than crt." },
  1311. X    { "pre_indent_str", "String:",
  1312. X    "String to precede message text interpolated into message body." },
  1313. X    { "post_indent_str", "String:",
  1314. X    "String to succeed message text interpolated into message body." },
  1315. X    { "print_cmd", "Program:",
  1316. X    "Alternate program to use to send messages to the printer." },
  1317. X    { "printer", "Printer:",
  1318. X    "Printer to send messages to (default = environment PRINTER)." },
  1319. X    { "prompt", "String:",
  1320. X    "Your prompt.  \"help prompt\" for more information." },
  1321. X    { "quiet", "-Conditions:",
  1322. X    "Turn off verbose messages and error bells in various conditions." },
  1323. X    { "realname", "Name:",
  1324. X    "Your real name." },
  1325. X    { "record", "Filename:",
  1326. X    "Save all outgoing mail in specified filename." },
  1327. X    { "reply_to_hdr", "Headers:",
  1328. X    "List of headers use to construct reply addresses from a message." },
  1329. X    { "save_empty", NULL,
  1330. X    "Folders which have all messages deleted are NOT removed on updates." },
  1331. X    { "screen", "# of Headers:",
  1332. X    "Number of headers to print in non-suntools (text) mode." },
  1333. X    { "screen_win", "# of Headers:",
  1334. X    "Set the size of the header window for the tool mode only." },
  1335. X    { "show_deleted", NULL,
  1336. X    "Show deleted messages in headers listings (unused in curses mode)." },
  1337. X    { "show_hdrs", "Headers:",
  1338. X    "When displaying a message, show list of \"headers\" only." },
  1339. X    { "sendmail", "Program:",
  1340. X    "Program to use to deliver mail instead of using the default."},
  1341. X    { "sort", "-Option:",
  1342. X    "Pre-sorting of messages on mush startup (set to valid sort option)." },
  1343. X    { "squeeze", NULL,
  1344. X    "When reading messages, squeeze all blank lines into one." },
  1345. X    { "status", NULL,
  1346. X    "The success or failure status of the most recent command." },
  1347. X    { "thisfolder", "Folder:",
  1348. X    "This read-only variable gives the current folder name." },
  1349. X    { "tool_help", "Path:",
  1350. X    "Location of the help file for tool mode."  },
  1351. X    { "toplines", "Lines:",
  1352. X    "Number of lines to print of a message for the 'top' command."  },
  1353. X    { "tmpdir", "Directory:",
  1354. X    "Directory to use for temporary files used by Mush." },
  1355. X    { "unix", NULL,
  1356. X    "Non-mush commands are considered to be UNIX commands." },
  1357. X    { "verify", NULL,
  1358. X    "Verify to send, re-edit, or abort letter after editing." },
  1359. X    { "visual", "Visual editor:",
  1360. X    "Visual editor for messages (default = $editor or env VISUAL)."},
  1361. X    { "warning", NULL,
  1362. X    "Print warning messages for non-fatal errors." },
  1363. X    { "wrap", NULL,
  1364. X    "After referencing last message, message pointer wraps to start." },
  1365. X    { "wrapcolumn", "-Column to wrap [78]:",
  1366. X    "Column at which to wrap lines when composing messages." },
  1367. X};
  1368. X
  1369. X#ifdef SUNTOOL
  1370. X
  1371. X#define OPTIONS_PANEL_WIDTH    550
  1372. X
  1373. Xint set_value(), toggle_value(), help_opt();
  1374. X
  1375. XFrame opts_frame;
  1376. XPanel opts_panel;
  1377. XPanel_item desc_msg;
  1378. XPanel_item file_text_item;
  1379. X
  1380. Xstatic void
  1381. Xframe_done()
  1382. X{
  1383. X#ifdef SUN_4_0 /* SunOS 4.0+ */
  1384. X    window_set(opts_frame, WIN_SHOW, FALSE, NULL);
  1385. X#else /* SUN_4_0 */
  1386. X    /* not enough fd's to keep it lying around for SunOS 3.X */
  1387. X    window_destroy(opts_frame);
  1388. X    opts_frame = (Frame) 0;
  1389. X#endif /* SUN_4_0 */
  1390. X}
  1391. X
  1392. Xstatic void
  1393. Xopts_help()
  1394. X{
  1395. X    help(0, "options", tool_help);
  1396. X}
  1397. X
  1398. Xstatic void
  1399. Xopts_save_load(item)
  1400. XPanel_item item;
  1401. X{
  1402. X    int (*func)() = (int (*)())panel_get(item, PANEL_CLIENT_DATA);
  1403. X    int result;
  1404. X    char buf[MAXPATHLEN];
  1405. X    char *argv[3], *file = panel_get_value(file_text_item);
  1406. X
  1407. X    if (!*file) {
  1408. X    result = (*func)(0, DUBL_NULL);
  1409. X    file = ".mushrc";
  1410. X    } else {
  1411. X    argv[1] = file;
  1412. X    argv[2] = NULL;
  1413. X    result = (*func)(2, argv);
  1414. X    }
  1415. X    switch (result) {
  1416. X    case 0:
  1417. X        sprintf(buf, "%s %s",
  1418. X        (func == source)? "Loaded options from" : "Saved options to",
  1419. X        file);
  1420. X    when -1:
  1421. X        sprintf(buf, "%s: %s", file, sys_errlist[errno]);
  1422. X    when -2:
  1423. X        sprintf(buf, "%s is a directory.", file);
  1424. X    when -3:
  1425. X        /* save_opts() returns -3 if user doesn't confirm overwrite */
  1426. X        strcpy(buf, "Save operation aborted.");
  1427. X    }
  1428. X    panel_set(desc_msg, PANEL_LABEL_STRING, buf, NULL);
  1429. X}
  1430. X
  1431. Xstatic void
  1432. Xunset_opts()
  1433. X{
  1434. X    cmd_line("unset *", NULL);
  1435. X}
  1436. X
  1437. Xstatic void
  1438. Xreset_opts()
  1439. X{
  1440. X    source(0, DUBL_NULL);
  1441. X}
  1442. X
  1443. X/*
  1444. X * Public routine which creates a subframe which contains two panels.
  1445. X * The first contains options for loading and saving options from a
  1446. X * file (text item) and so on... the second panel contains all the items
  1447. X * which correspond to each mush variable that exists.
  1448. X */
  1449. Xvoid
  1450. Xview_options()
  1451. X{
  1452. X    extern Notify_value fkey_interposer();
  1453. X    register char *p;
  1454. X    int count;
  1455. X
  1456. X    if (opts_frame) {
  1457. X    window_set(opts_frame, WIN_SHOW, TRUE, NULL);
  1458. X    opts_panel_item(NULL);
  1459. X    return;
  1460. X    }
  1461. X#ifdef SUN_3_5
  1462. X    if (nopenfiles(0) < 3) {
  1463. X    ok_box("Too many frames; close one first!\n");
  1464. X    return;
  1465. X    }
  1466. X#endif /* SUN_3_5 */
  1467. X
  1468. X    opts_frame = window_create(tool, FRAME,
  1469. X    FRAME_DONE_PROC,    frame_done,
  1470. X    FRAME_LABEL,        "Mush Options",
  1471. X    FRAME_NO_CONFIRM,    TRUE,
  1472. X    FRAME_SHOW_LABEL,    TRUE,
  1473. X    WIN_WIDTH,        OPTIONS_PANEL_WIDTH,
  1474. X    NULL);
  1475. X
  1476. X    opts_panel = window_create(opts_frame, PANEL,
  1477. X    WIN_WIDTH,        OPTIONS_PANEL_WIDTH,
  1478. X    NULL);
  1479. X    (void) notify_interpose_event_func(opts_panel,
  1480. X    fkey_interposer, NOTIFY_SAFE);
  1481. X    panel_create_item(opts_panel, PANEL_BUTTON,
  1482. X    PANEL_LABEL_IMAGE,
  1483. X        panel_button_image(opts_panel, "Done", 4, mush_font),
  1484. X    PANEL_NOTIFY_PROC,    frame_done,
  1485. X    NULL);
  1486. X    panel_create_item(opts_panel, PANEL_BUTTON,
  1487. X    PANEL_LABEL_IMAGE,
  1488. X        panel_button_image(opts_panel, "Help", 4, mush_font),
  1489. X    PANEL_NOTIFY_PROC,    opts_help,
  1490. X    NULL);
  1491. X    panel_create_item(opts_panel, PANEL_BUTTON,
  1492. X    PANEL_LABEL_IMAGE,
  1493. X        panel_button_image(opts_panel, "Save", 4, mush_font),
  1494. X    PANEL_NOTIFY_PROC,    opts_save_load,
  1495. X    PANEL_CLIENT_DATA,    save_opts,
  1496. X    NULL);
  1497. X    panel_create_item(opts_panel, PANEL_BUTTON,
  1498. X    PANEL_LABEL_IMAGE,
  1499. X        panel_button_image(opts_panel, "Load", 4, mush_font),
  1500. X    PANEL_NOTIFY_PROC,    opts_save_load,
  1501. X    PANEL_CLIENT_DATA,    source,
  1502. X    NULL);
  1503. X    panel_create_item(opts_panel, PANEL_BUTTON,
  1504. X    PANEL_LABEL_IMAGE,
  1505. X        panel_button_image(opts_panel, "Clear", 5, mush_font),
  1506. X    PANEL_NOTIFY_PROC,    unset_opts,
  1507. X    NULL);
  1508. X    panel_create_item(opts_panel, PANEL_BUTTON,
  1509. X    PANEL_LABEL_IMAGE,
  1510. X        panel_button_image(opts_panel, "Restart", 7, mush_font),
  1511. X    PANEL_NOTIFY_PROC,    reset_opts,
  1512. X    NULL);
  1513. X    file_text_item = panel_create_item(opts_panel, PANEL_TEXT,
  1514. X    PANEL_LABEL_STRING,    "Save/Load File:",
  1515. X    PANEL_VALUE_DISPLAY_LENGTH, 30,
  1516. X    NULL);
  1517. X    desc_msg = panel_create_item(opts_panel, PANEL_MESSAGE,
  1518. X    PANEL_LABEL_STRING,    "Help Descriptions -- Click on Variable Name",
  1519. X    NULL);
  1520. X    window_fit_height(opts_panel);
  1521. X
  1522. X    /* reuse opts_panel -- we don't need the other one */
  1523. X    opts_panel = window_create(opts_frame, PANEL,
  1524. X    WIN_BELOW,            opts_panel,
  1525. X    WIN_X,                0,
  1526. X    WIN_COLUMN_GAP,            120,
  1527. X    WIN_TOP_MARGIN,            10,
  1528. X    WIN_LEFT_MARGIN,        10,
  1529. X    WIN_WIDTH,            OPTIONS_PANEL_WIDTH,
  1530. X    PANEL_VERTICAL_SCROLLBAR,    scrollbar_create(NULL),
  1531. X    NULL);
  1532. X    (void) notify_interpose_event_func(opts_panel,
  1533. X    fkey_interposer, NOTIFY_SAFE);
  1534. X
  1535. X    for (count = 0; count < ArraySize(viewopts); count++) {
  1536. X    panel_create_item(opts_panel, PANEL_MESSAGE,
  1537. X        PANEL_ITEM_X,    ATTR_COL(0),
  1538. X        PANEL_ITEM_Y,    ATTR_ROW(count),
  1539. X        PANEL_LABEL_STRING,    viewopts[count].v_opt,
  1540. X        PANEL_NOTIFY_PROC,    help_opt,
  1541. X        PANEL_CLIENT_DATA,    count,
  1542. X        NULL);
  1543. X
  1544. X    if (!(p = viewopts[count].v_prompt) || *p == '-') {
  1545. X        if (p && *p)
  1546. X        p++;
  1547. X        viewopts[count].v_choice = panel_create_item(opts_panel,
  1548. X        PANEL_CHOICE,
  1549. X        PANEL_LABEL_IMAGE,    &cycle,
  1550. X        PANEL_LAYOUT,        PANEL_HORIZONTAL,
  1551. X        PANEL_CHOICE_STRINGS,    "False", "True", NULL,
  1552. X        PANEL_DISPLAY_LEVEL,    PANEL_CURRENT,
  1553. X        PANEL_ITEM_X,        ATTR_COL(1),
  1554. X        PANEL_ITEM_Y,        ATTR_ROW(count),
  1555. X        PANEL_NOTIFY_PROC,    toggle_value,
  1556. X        PANEL_CLIENT_DATA,    count,
  1557. X        NULL);
  1558. X    }
  1559. X    if (p) {
  1560. X        viewopts[count].v_text = panel_create_item(opts_panel, PANEL_TEXT,
  1561. X        PANEL_VALUE_DISPLAY_LENGTH,    10,
  1562. X        PANEL_VALUE_UNDERLINED,        TRUE,
  1563. X        PANEL_LABEL_STRING,        p,
  1564. X        PANEL_ITEM_X,            ATTR_COL(2),
  1565. X        PANEL_ITEM_Y,            ATTR_ROW(count),
  1566. X        PANEL_NOTIFY_PROC,        set_value,
  1567. X        PANEL_CLIENT_DATA,        count,
  1568. X        NULL);
  1569. X    }
  1570. X    }
  1571. X    /* set the panel items' values */
  1572. X    opts_panel_item(NULL);
  1573. X
  1574. X    window_set(opts_panel,
  1575. X    WIN_HEIGHT,    400,
  1576. X    WIN_FIT_HEIGHT,    0,
  1577. X    NULL);
  1578. X    window_set(opts_frame, WIN_SHOW, TRUE, NULL);
  1579. X}
  1580. X
  1581. X/*
  1582. X * Sets the items in the panels to reflect that variable's value.
  1583. X * If "var" is NULL, do it for all the items.
  1584. X */
  1585. Xvoid
  1586. Xopts_panel_item(var)
  1587. Xchar *var;
  1588. X{
  1589. X    int count;
  1590. X    char *value;
  1591. X
  1592. X    if (!opts_frame)
  1593. X    return;
  1594. X
  1595. X    for (count = 0; count < ArraySize(viewopts); count++) {
  1596. X    if (var && strcmp(var, viewopts[count].v_opt))
  1597. X        continue;
  1598. X    value = do_set(set_options, viewopts[count].v_opt);
  1599. X
  1600. X    if (!viewopts[count].v_prompt || *viewopts[count].v_prompt == '-')
  1601. X        panel_set_value(viewopts[count].v_choice, value != NULL);
  1602. X    if (viewopts[count].v_prompt)
  1603. X        panel_set_value(viewopts[count].v_text, value? value : "");
  1604. X    if (var)
  1605. X        break;
  1606. X    }
  1607. X}
  1608. X
  1609. X/*
  1610. X * Callback for choice items -- for variables that have boolean settings.
  1611. X * CLIENT_DATA is the index in the viewopts array.
  1612. X */
  1613. Xstatic
  1614. Xtoggle_value(item, value)
  1615. XPanel_item item;
  1616. Xint value;
  1617. X{
  1618. X    int count = (int) panel_get(item, PANEL_CLIENT_DATA);
  1619. X    char *p, *argv[4];
  1620. X    char *text_value = NULL;
  1621. X
  1622. X    if (check_internal(viewopts[count].v_opt)) {
  1623. X    panel_set(desc_msg, PANEL_LABEL_STRING,
  1624. X        "This is an internal variable which cannot be changed.",
  1625. X        NULL);
  1626. X    return -1;
  1627. X    }
  1628. X
  1629. X    if (p = viewopts[count].v_prompt) /* set equal */
  1630. X    text_value = panel_get_value(viewopts[count].v_text);
  1631. X
  1632. X    if (!value)
  1633. X    (void) un_set(&set_options, viewopts[count].v_opt);
  1634. X    else {
  1635. X    /* Turn it on if it's entirely boolean or bool/str, but no str value */
  1636. X    if (!p || text_value && !*text_value) {
  1637. X        argv[0] = viewopts[count].v_opt; /* it's a boolean */
  1638. X        argv[1] = NULL;
  1639. X    } else {
  1640. X        /* string value -- determine the text from the typed in value */
  1641. X        argv[0] = viewopts[count].v_opt;
  1642. X        argv[1] = "=";
  1643. X        argv[2] = text_value;
  1644. X        argv[3] = NULL;
  1645. X    }
  1646. X    (void) add_option(&set_options, argv);
  1647. X    }
  1648. X
  1649. X    if (!strcmp(viewopts[count].v_opt, "no_reverse") ||
  1650. X    !strcmp(viewopts[count].v_opt, "show_deleted"))
  1651. X    do_hdrs(0, DUBL_NULL, NULL);
  1652. X
  1653. X    return 0;
  1654. X}
  1655. X
  1656. X/* callback for text items -- set vars to the string typed. */
  1657. Xstatic
  1658. Xset_value(item, event)
  1659. XPanel_item item;
  1660. XEvent *event;
  1661. X{
  1662. X    int count = (int)panel_get(item, PANEL_CLIENT_DATA);
  1663. X    char *p, *argv[4], *value;
  1664. X
  1665. X    if (event_id(event) == '\t')
  1666. X    return (int) PANEL_NEXT;
  1667. X
  1668. X    p = viewopts[count].v_prompt;
  1669. X    value = panel_get_value(item);
  1670. X
  1671. X    if (check_internal(viewopts[count].v_opt)) {
  1672. X    panel_set(desc_msg, PANEL_LABEL_STRING,
  1673. X        "This is an internal variable which cannot be changed.",
  1674. X        NULL);
  1675. X    return (int) PANEL_NONE;
  1676. X    }
  1677. X
  1678. X    /*
  1679. X     * You can "unset" string-only values by entering a blank string.
  1680. X     * If the "prompt" starts with a -, then you can only "unset" the
  1681. X     * variable by setting the associated choice item to false.
  1682. X     */
  1683. X    if (*p != '-' && !*value) {
  1684. X    (void) un_set(&set_options, viewopts[count].v_opt);
  1685. X    return (int) PANEL_NONE; /* do not advance caret */
  1686. X    }
  1687. X    /* Turn it on, but not to a value */
  1688. X    if (!*value) {
  1689. X    argv[0] = viewopts[count].v_opt; /* it's a boolean */
  1690. X    argv[1] = NULL;
  1691. X    } else {
  1692. X    /* string value -- determine the text from the typed in value */
  1693. X    argv[0] = viewopts[count].v_opt;
  1694. X    argv[1] = "=";
  1695. X    argv[2] = value;
  1696. X    argv[3] = NULL;
  1697. X    }
  1698. X
  1699. X    (void) add_option(&set_options, argv);
  1700. X    if (p && *p == '-')
  1701. X    panel_set_value(viewopts[count].v_choice, TRUE);
  1702. X
  1703. X    return (int) PANEL_NONE;
  1704. X}
  1705. X
  1706. X/* when user clicks on variable label itself */
  1707. Xstatic
  1708. Xhelp_opt(item, event)
  1709. XPanel_item item;
  1710. XEvent *event;
  1711. X{
  1712. X    int count = (int)panel_get(item, PANEL_CLIENT_DATA);
  1713. X
  1714. X    panel_set(desc_msg,
  1715. X    PANEL_LABEL_STRING, viewopts[count].v_description,
  1716. X    NULL);
  1717. X    return 0;
  1718. X}
  1719. X
  1720. X#endif /* SUNTOOL */
  1721. X
  1722. X/*
  1723. X * return a string describing a variable.
  1724. X * parameters: count, str, buf.
  1725. X * If str != NULL, check str against ALL variables
  1726. X * in viewopts array.  The one that matches, set count to it and 
  1727. X * print up all the stuff from the viewopts[count] into the buffer
  1728. X * space in "buf" and return it.
  1729. X */
  1730. Xchar *
  1731. Xvariable_stuff(count, str, buf)
  1732. Xregister char *str, *buf;
  1733. X{
  1734. X    if (str)
  1735. X    for (count = 0; count < ArraySize(viewopts); count++)
  1736. X        if (!strcmp(str, viewopts[count].v_opt))
  1737. X        break;
  1738. X    if (count >= ArraySize(viewopts)) {
  1739. X    (void) sprintf(buf, "%s: Not a default %s variable.",
  1740. X               str? str : itoa(count), prog_name);
  1741. X    return NULL;
  1742. X    }
  1743. X    return sprintf(buf, "%s: %s",
  1744. X    viewopts[count].v_opt, viewopts[count].v_description);
  1745. X}
  1746. END_OF_FILE
  1747. if test 18292 -ne `wc -c <'mush/viewopts.c'`; then
  1748.     echo shar: \"'mush/viewopts.c'\" unpacked with wrong size!
  1749. fi
  1750. # end of 'mush/viewopts.c'
  1751. fi
  1752. echo shar: End of archive 7 \(of 19\).
  1753. cp /dev/null ark7isdone
  1754. MISSING=""
  1755. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  1756.     if test ! -f ark${I}isdone ; then
  1757.     MISSING="${MISSING} ${I}"
  1758.     fi
  1759. done
  1760. if test "${MISSING}" = "" ; then
  1761.     echo You have unpacked all 19 archives.
  1762.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1763. else
  1764.     echo You still need to unpack the following archives:
  1765.     echo "        " ${MISSING}
  1766. fi
  1767. ##  End of shell archive.
  1768. exit 0
  1769.  
  1770.