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

  1. From: argv@zipcode.com (Dan Heller)
  2. Newsgroups: comp.sources.misc
  3. Subject: v18i060:  mush - Mail User's Shell, Part03/22
  4. Message-ID: <1991Apr21.024911.11147@sparky.IMD.Sterling.COM>
  5. Date: 21 Apr 91 02:49:11 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: b176fd44 0a91f5e5 91b5304d ff82ec68
  8.  
  9. Submitted-by: Dan Heller <argv@zipcode.com>
  10. Posting-number: Volume 18, Issue 60
  11. Archive-name: mush/part03
  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 addrs.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" != 3; 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 addrs.c'
  32. else
  33. echo 'x - continuing file addrs.c'
  34. sed 's/^X//' << 'SHAR_EOF' >> 'addrs.c' &&
  35. X    p = sender + c;
  36. X    *p++ = ',', *p++ = ' ';
  37. X    (void) strcpy(p, next);
  38. X    (void) strcpy(to, sender);
  39. }
  40. X
  41. /*
  42. X * pass a string describing header like, "Subject: ", current value, and
  43. X * whether or not to prompt for it or to just post the information.
  44. X * If do_prompt is true, "type in" the current value so user can either
  45. X * modify it, erase it, or add to it.
  46. X */
  47. char *
  48. set_header(str, curstr, do_prompt)
  49. register char *str, *curstr;
  50. {
  51. X    static char       buf[HDRSIZ];
  52. X    int        offset = 0;
  53. X    register char  *p = curstr;
  54. X
  55. X    if (!str)
  56. X    str = "";
  57. X
  58. X    buf[0] = 0;
  59. X    print(str);
  60. X    (void) fflush(stdout);         /* force str curstr */
  61. X    if (do_prompt) {
  62. X    if (curstr)
  63. X        if (isoff(glob_flags, ECHO_FLAG)) {
  64. X        Ungetstr(curstr);
  65. X        } else
  66. #ifdef TIOCSTI
  67. X        for (p = curstr; *p; p++)
  68. X            if (ioctl(0, TIOCSTI, p) == -1) {
  69. X            error("ioctl: TIOCSTI");
  70. X            print("You must retype the entire line.\n%s", str);
  71. X            break;
  72. X            }
  73. #else /* !TIOCSTI */
  74. X        print("WARNING: -e flag! Type the line over.\n%s", str);
  75. #endif /* TIOCSTI */
  76. X
  77. X    if (istool)
  78. X        return NULL;
  79. X    /* simulate the fact that we're getting input for the letter even tho
  80. X     * we may not be.  set_header is called before IS_GETTING is true,
  81. X     * but if we set it to true temporarily, then signals will return to
  82. X     * the right place (stop/continue).
  83. X     */
  84. X    {
  85. X        u_long getting = ison(glob_flags, IS_GETTING);
  86. X        int wrapping = wrapcolumn;
  87. X        /* Funky trick here.  If the prompt string is empty,
  88. X         * assume that we are allowed to do line wrap;
  89. X         * otherwise, temporarily disable line wrap
  90. X         */
  91. X        if (*str)
  92. X        wrapcolumn = 0;
  93. X        if (!getting)
  94. X        turnon(glob_flags, IS_GETTING);
  95. X        if (Getstr(buf, sizeof(buf), offset) == -1) {
  96. X        putchar('\n');
  97. X        buf[0] = 0;
  98. X        }
  99. X        if (!getting)
  100. X        turnoff(glob_flags, IS_GETTING);
  101. X        wrapcolumn = wrapping;
  102. X    }
  103. X    } else
  104. X    puts(strcpy(buf, curstr));
  105. X    if (debug > 1)
  106. X    print("returning (%s) from set_header\n", buf);
  107. X    return buf;
  108. }
  109. X
  110. /*
  111. X * improve uucp paths by looking at the name of each host listed in the
  112. X * path given.
  113. X *    sun!island!pixar!island!argv
  114. X * It's a legal address, but redundant. Also, if we know we talk to particular
  115. X * hosts via uucp, then we can just start with that host and disregard the path
  116. X * preceding it.  So, first get the known hosts and save them. Then start
  117. X * at the end of the original path (at the last ! found), and move backwards
  118. X * saving each hostname.  If we get to a host that we know about, stop there
  119. X * and use that address.  If the system knows about domains, skip all paths
  120. X * that precede a domain hostname.  If we get to a host we've already seen,
  121. X * then delete it and all the hosts since then until the first occurrence of
  122. X * that hostname.  When we get to the beginning, the address will be complete.
  123. X * The route_path is prepended to each address to check make sure this path
  124. X * is used if no known_hosts precede it in that address.
  125. X *
  126. X * Return all results into the original buffer passed to us.  If route_path
  127. X * adds to the length of all the paths, then the original buffer could be
  128. X * overwritten.  someone should check for this!
  129. X */
  130. improve_uucp_paths(original, size, route_path)
  131. char *original, *route_path;
  132. {
  133. X    char       name[256], addr[256], buf[2 * HDRSIZ], *end;
  134. X    char      *hostnames[32], tmp[sizeof addr], *domain_path;
  135. X    register char *p, *p2, *recipient, *start = original, *b = buf;
  136. X    int           saved_hosts, i, is_domain;
  137. X
  138. X    if (!original || !*original)
  139. X    return;
  140. X
  141. X    /* use domain_path to point to the path for pathnames that have
  142. X     * a fully qualified domain host in them.
  143. X     */
  144. X    domain_path = do_set(set_options, "domain_route");
  145. X    while (end = get_name_n_addr(start, name, tmp)) {
  146. X    /* first copy the route path, then the rest of the address. */
  147. X    p = addr;
  148. X    if (route_path && *route_path) {
  149. X        p += Strcpy(addr, route_path);
  150. X        *p++ = '!';
  151. X    }
  152. X    (void) bang_form(p, tmp);
  153. X    saved_hosts = 0;
  154. X    if (p2 = rindex(p, '!')) {
  155. X        recipient = p2+1;
  156. X        /* save the uucp-style address *without* route_path in tmp */
  157. X        (void) strcpy(tmp, p);
  158. X        for (p = p2; p > addr; p--) {
  159. X        is_domain = 0;
  160. X        /* null the '!' separating the rest of the path from the part
  161. X         * of the path preceding it and move p back to the previous
  162. X         * '!' (or beginning to addr) for hostname to point to.
  163. X         */
  164. X        for (*p-- = 0; p > addr && *p != '!'; p--)
  165. X            if (!is_domain && domain_path && *p == '.' &&
  166. X                lcase_strncmp(p, ".uucp", 5))
  167. X            is_domain++;
  168. X        /* if p is not at the addr, move it forward past the '!' */
  169. X        if (p != addr)
  170. X            ++p; /* now points to a null terminated hostname */
  171. X        /* if host is ourselves, ignore this and preceding hosts */
  172. X        for (i = 0; ourname && ourname[i]; i++)
  173. X            if (!lcase_strncmp(p, ourname[i], -1))
  174. X            break;
  175. X        if (ourname && ourname[i]) {
  176. X            is_domain = 0; /* we've eliminated all domains */
  177. X            break;
  178. X        }
  179. X        /* check already saved hostnames. If host is one of them,
  180. X         * delete remaining hostnames since there is a redundant path.
  181. X         */
  182. X        for (i = 0; i < saved_hosts; i++)
  183. X            if (!lcase_strncmp(hostnames[i], p, -1))
  184. X            saved_hosts = i;
  185. X
  186. X        /* Add the hostname to the path being constructed */
  187. X        hostnames[saved_hosts++] = p;
  188. X
  189. X        /* If the original path or the address is a fully qualified
  190. X         * hostname (domain info is included), then break here
  191. X         */
  192. X        if (p == addr || is_domain && domain_path)
  193. X            break;
  194. X        /* If we know that we call this host, break */
  195. X        for (i = 0; known_hosts && known_hosts[i]; i++)
  196. X            if (!lcase_strncmp(p, known_hosts[i], -1))
  197. X            break;
  198. X        if (known_hosts && known_hosts[i])
  199. X            break;
  200. X        }
  201. X        /* temporary holder for where we are in buffer (save address) */
  202. X        p2 = b;
  203. X        if (is_domain && domain_path && *domain_path)
  204. X        b += Strcpy(b, domain_path), *b++ = '!';
  205. X        while (saved_hosts-- > 0) {
  206. X        b += Strcpy(b, hostnames[saved_hosts]);
  207. X        *b++ = '!';
  208. X        }
  209. X        b += Strcpy(b, recipient);
  210. X        if (!strcmp(p2, tmp)) { /* if the same, address was unmodified */
  211. X        b = p2; /* reset offset in buf (b) to where we were (p2) */
  212. X        goto unmodified;
  213. X        }
  214. X        if (*name)
  215. X        b += strlen(sprintf(b, " (%s)", name));
  216. X    } else {
  217. X        char c;
  218. unmodified:
  219. X        c = *end;
  220. X        *end = 0;
  221. X        b += Strcpy(b, start); /* copy the entire address with comments */
  222. X        *end = c;
  223. X    }
  224. X    if (b - buf > size) {
  225. X        wprint("Warning: address list truncated!\n");
  226. X        /* Use a very poor heuristic to find the last complete address */
  227. X        for (b = buf+size - 1; *b != ','; b--)
  228. X        ;
  229. X        wprint("Lost addresses: %s%s\n", b, end); /* end = not yet parsed */
  230. X        while (isspace(*b) || *b == ',')
  231. X        b--;
  232. X        break;
  233. X    }
  234. X    for (start = end; *start == ',' || isspace(*start); start++)
  235. X        ;
  236. X    if (!*start)
  237. X        break;
  238. X    *b++ = ',', *b++ = ' ', *b = '\0';
  239. X    }
  240. X    (void) strcpy(original, buf);
  241. }
  242. X
  243. /*
  244. X * rm_cmts_in_addr() removes the comment lines in addresses that result from
  245. X * sendmail or other mailers which append the user's "real name" on the
  246. X * from lines.  See get_name_n_addr().
  247. X */
  248. rm_cmts_in_addr(str)
  249. register char *str;
  250. {
  251. X    char addr[BUFSIZ], buf[HDRSIZ], *start = str;
  252. X    register char *b = buf;
  253. X
  254. X    *b = 0;
  255. X    do  {
  256. X    if (!(str = get_name_n_addr(str, NULL, addr)))
  257. X        break;
  258. X    b += Strcpy(b, addr);
  259. X    while (*str == ',' || isspace(*str))
  260. X        str++;
  261. X    if (*str)
  262. X        *b++ = ',', *b++ = ' ', *b = '\0';
  263. X    } while (*str);
  264. X    for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  265. X    *b = 0;
  266. X    (void) strcpy(start, buf);
  267. }
  268. X
  269. /*
  270. X * take_me_off() is intended to search for the user's login name in an
  271. X * address string and remove it.  If "metoo" is set, return without change.
  272. X * determine which addresses are the "user'"'s addresses by comparing them
  273. X * against the host/path names in alternates.  If the "*" is used, then
  274. X * this matches the address against the user's current login and -any- path.
  275. X *
  276. X * Note that the alternates list is an array of addresses stored *reversed*!
  277. X */
  278. take_me_off(str)
  279. char *str;
  280. {
  281. X    int i = 0, rm_me;
  282. X    char tmp[256], addr[256], buf[HDRSIZ], *start = str;
  283. X    register char *p, *p2, *b = buf;
  284. X
  285. X    if (!str || !*str)
  286. X    return;
  287. X
  288. X    Debug("take_me_off()\n");
  289. X    *b = 0;
  290. X    do  {
  291. X    rm_me = FALSE;
  292. X    /* get the first "address" and advance p to next addr (ignore name) */
  293. X    if (!(p = get_name_n_addr(str, NULL, tmp)))
  294. X        break; /* we've reached the end of the address list */
  295. X    /* see if user's login is in the address */
  296. X    if (!strcmp(login, tmp))
  297. X        rm_me = TRUE;
  298. X    else {
  299. X        int len;
  300. X        /* put address in !-format and store in "addr" */
  301. X        (void) bang_form(addr, tmp);
  302. X        (void) reverse(addr);
  303. X        for (i = 0; alternates && alternates[i] && !rm_me; i++) {
  304. X        if (alternates[i][0] == '*') {
  305. X            if (alternates[i][1] == '\0')
  306. X            p2 = reverse(strcpy(tmp, login));
  307. X            else
  308. X            p2 = reverse(strcpy(tmp, &alternates[i][1]));
  309. X        } else
  310. X            p2 = alternates[i];
  311. X        if (!lcase_strncmp(p2, addr, (len = strlen(p2))) &&
  312. X            (!addr[len] || addr[len] == '!')) {
  313. X            Debug("\t%s\n", reverse(addr));
  314. X            rm_me = TRUE;
  315. X        }
  316. X        }
  317. X        for (i = 0; !rm_me && ourname && ourname[i]; i++) {
  318. X        p2 = tmp + Strcpy(tmp, ourname[i]);
  319. X        *p2++ = '!';
  320. X        (void) strcpy(p2, login);
  321. X        (void) reverse(tmp);
  322. X        if (!lcase_strncmp(tmp, addr, (len = strlen(tmp))) &&
  323. X            (!addr[len] || addr[len] == '!')) {
  324. X            Debug("\t%s\n", reverse(addr));
  325. X            rm_me = TRUE;
  326. X        }
  327. X        }
  328. X    }
  329. X    /* The address is not the user's -- put it into the returned list */
  330. X    if (!rm_me) {
  331. X        char c = *p;
  332. X        *p = 0;
  333. X        b += Strcpy(b, str);
  334. X        *p = c;
  335. X    }
  336. X    while (*p == ',' || isspace(*p))
  337. X        p++;
  338. X    if (*p && !rm_me)
  339. X        *b++ = ',', *b++ = ' ', *b = '\0';
  340. X    } while (*(str = p));
  341. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  342. X    *b = 0;
  343. X    (void) strcpy(start, buf);
  344. }
  345. X
  346. /*
  347. X * Place commas in between all addresses that don't already have
  348. X * them.  Addresses which use comments which are in parens or _not_
  349. X * within angle brackets *must* already have commas around them or
  350. X * you can't determine what is a comment and what is an address.
  351. X */
  352. fix_up_addr(str)
  353. char *str;
  354. {
  355. X    char buf[HDRSIZ], *start = str;
  356. X    register char c, *p, *b = buf;
  357. X
  358. X    *b = 0;
  359. X    do  {
  360. X    /* get_name returns a pointer to the next address */
  361. X    if (!(p = get_name_n_addr(str, NULL, NULL)))
  362. X        break;
  363. X    c = *p, *p = 0;
  364. X    if (strlen(str) + (b - buf) >= sizeof(buf) - 2) {
  365. X        /* wprint("Address too long! Lost address: \"%s\"\n", str); */
  366. X        *p = c;
  367. X        break;
  368. X    }
  369. X    for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  370. X        *b = 0;
  371. X    for (*p = c; *p == ',' || isspace(*p); p++)
  372. X        ;
  373. X    if (*p)
  374. X        *b++ = ',', *b++ = ' ', *b = '\0';
  375. X    } while (*(str = p));
  376. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  377. X    *b = 0;
  378. X    (void) strcpy(start, buf);
  379. }
  380. X
  381. /*
  382. X * Remove redundant addresses.
  383. X * Assume improve_uucp_paths, fix_up_addr or whatever have already been called.
  384. X */
  385. rm_redundant_addrs(to, cc)
  386. char *to, *cc;
  387. {
  388. X    char tmp[256], addr[256], buf[HDRSIZ];
  389. X    char **list; /* a list of addresses for comparison */
  390. X    int list_cnt = 0, l;
  391. X    register char c, *p, *b, *start;
  392. X
  393. X    Debug("rm_redundant_addrs()\n");
  394. X    list = (char **) calloc(256, sizeof(char *));
  395. X    if (!list) {
  396. X    error("out of memory in rm_redundant_addrs");
  397. X    return;
  398. X    }
  399. X    start = to;
  400. X    b = buf, *b = 0;
  401. X    /* first do the To header */
  402. X    do  {
  403. X    /* get_name returns a pointer to the next address */
  404. X    if (!(p = get_name_n_addr(to, NULL, tmp)))
  405. X        break;
  406. X    c = *p, *p = 0;
  407. X    (void) bang_form(addr, tmp);
  408. X    for (l = 0; l < list_cnt; l++)
  409. X        if (!lcase_strncmp(addr, list[l], -1))
  410. X        break;
  411. X    /* if l == list_cnt, we got a new address, store it and add to buf */
  412. X    if (l == list_cnt) {
  413. X        /* Don't overwrite buffer. */
  414. X        if (list_cnt < 256)
  415. X        list[list_cnt++] = savestr(addr);
  416. X        if (b > buf)
  417. X        *b++ = ',', *b++ = ' ', *b = '\0';
  418. X        for (b += Strcpy(b, to); b > buf && isspace(*(b-1)); b--)
  419. X        *b = 0;
  420. X    } else
  421. X        Debug("\t%s\n", tmp); /* already specified (removed from list) */
  422. X    for (*p = c; *p == ',' || isspace(*p); p++)
  423. X        ;
  424. X    } while (*(to = p));
  425. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  426. X    *b = 0;
  427. X    (void) strcpy(start, buf);
  428. X    b = buf, *b = 0;
  429. X    /* Now do the Cc header.  If addr is listed in the To field, rm it in cc */
  430. X    start = cc;
  431. X    do  {
  432. X    /* get_name returns a pointer to the next address */
  433. X    if (!(p = get_name_n_addr(cc, NULL, tmp)))
  434. X        break;
  435. X    c = *p, *p = 0;
  436. X    (void) bang_form(addr, tmp);
  437. X    for (l = 0; l < list_cnt; l++)
  438. X        if (!lcase_strncmp(addr, list[l], -1))
  439. X        break;
  440. X    if (l == list_cnt) {
  441. X        /* Don't overwrite buffer. */
  442. X        if (list_cnt < sizeof(list)/sizeof(char *))
  443. X        list[list_cnt++] = savestr(addr);
  444. X        if (b > buf)
  445. X        *b++ = ',', *b++ = ' ', *b = '\0';
  446. X        for (b += Strcpy(b, cc); b > buf && isspace(*(b-1)); b--)
  447. X        *b = 0;
  448. X    } else
  449. X        Debug("\t%s\n", tmp); /* already specified (removed from list) */
  450. X    for (*p = c; *p == ',' || isspace(*p); p++)
  451. X        ;
  452. X    } while (*(cc = p));
  453. X    list[list_cnt] = NULL; /* for free_vec */
  454. X    free_vec(list);
  455. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  456. X    *b = 0;
  457. X    (void) strcpy(start, buf);
  458. }
  459. X
  460. /*
  461. X * Get address and name from a string (str) which came from an address header
  462. X * in a message or typed by the user.  The string may contain one or more
  463. X * well-formed addresses.  Each must be separated by a comma.
  464. X *
  465. X * address, address, address
  466. X * address (comment or name here)
  467. X * comment or name <address>
  468. X * "Comment, even those with comma's!" <address>
  469. X * address (comma, (more parens), etc...)
  470. X *
  471. X * This does *not* handle cases like:
  472. X *    comment <address (comment)>
  473. X *
  474. X * find the *first* address here and return a pointer to the end of the
  475. X * address (usually a comma).  Return NULL on error: non-matching parens,
  476. X * brackets, quotes...
  477. X */
  478. char *
  479. get_name_n_addr(str, name, addr)
  480. register char *str, *name, *addr;
  481. {
  482. X    register char *p, *p2, *beg_addr = addr, *beg_name = name, c;
  483. X
  484. X    if (addr)
  485. X    *addr = 0;
  486. X    if (name)
  487. X    *name = 0;
  488. X    if (!str || !*str)
  489. X    return NULL;
  490. X
  491. X    while (isspace(*str))
  492. X    str++;
  493. X
  494. X    /* first check to see if there's something to look for */
  495. X    if (!(p = any(str, ",(<\""))) {
  496. X    /* no comma or indication of a quote character. Find a space and
  497. X     * return that.  If nothing, the entire string is a complete address
  498. X     */
  499. X    if (p = any(str, " \t"))
  500. X        c = *p, *p = 0;
  501. X    if (addr)
  502. X        (void) strcpy(addr, str);
  503. X    if (p)
  504. X        *p = c;
  505. X    return p? p : str + strlen(str);
  506. X    }
  507. X
  508. X    /* comma terminated before any comment stuff.  If so, check for whitespace
  509. X     * before-hand cuz it's possible that strings aren't comma separated yet
  510. X     * and they need to be.
  511. X     *
  512. X     * address address address, address
  513. X     *                        ^p  <- p points here.
  514. X     *        ^p2 <- should point here.
  515. X     */
  516. X    if (*p == ',') {
  517. X    c = *p, *p = 0;
  518. X    if (p2 = any(str, " \t"))
  519. X        *p = ',', c = *p2, p = p2, *p = 0;
  520. X    if (addr)
  521. X        (void) strcpy(addr, str);
  522. X    *p = c;
  523. X    return p;
  524. X    }
  525. X
  526. X    /* starting to get hairy -- we found an angle bracket. This means that
  527. X     * everything outside of those brackets are comments until we find that
  528. X     * all important comma.  A comment AFTER the <addr> :
  529. X     *  <address> John Doe
  530. X     * can't call this function recursively or it'll think that "John Doe"
  531. X     * is a string with two legal address on it (each name being an address).
  532. X     */
  533. X    if (*p == '<') { /* note that "str" still points to comment stuff! */
  534. X    if (name && *str) {
  535. X        *p = 0;
  536. X        name += Strcpy(name, str);
  537. X        *p = '<';
  538. X    }
  539. X    if (!(p2 = index(p+1, '>'))) {
  540. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  541. X        return NULL;
  542. X    }
  543. X    if (addr) {
  544. X        /* to support <addr (comment)> style addresses, add code here */
  545. X        *p2 = 0;
  546. X        skipspaces(1);
  547. X        addr += Strcpy(addr, p);
  548. X        while (addr > beg_addr && isspace(*(addr-1)))
  549. X        *--addr = 0;
  550. X        *p2 = '>';
  551. X    }
  552. X    /* take care of the case "... <addr> com (ment)" */
  553. X    {
  554. X        int p_cnt = 0; /* parenthesis counter */
  555. X        p = p2;
  556. X        /* don't recurse yet -- scan till null, comma or '<'(add to name) */
  557. X        for (p = p2; p[1] && (p_cnt || p[1] != ',' && p[1] != '<'); p++) {
  558. X        if (p[1] == '(')
  559. X            p_cnt++;
  560. X        else if (p[1] == ')')
  561. X            p_cnt--;
  562. X        if (name)
  563. X            *name++ = p[1];
  564. X        }
  565. X        if (p_cnt) {
  566. X        wprint("Warning! Malformed name: \"%s\"\n", name);
  567. X        return NULL;
  568. X        }
  569. X    }
  570. X    if (name && name > beg_name) {
  571. X        while (isspace(*(name-1)))
  572. X        --name;
  573. X        *name = 0;
  574. X    }
  575. X    }
  576. X
  577. X    /* this is the worst -- now we have parentheses/quotes.  These guys can
  578. X     * recurse pretty badly and contain commas within them.
  579. X     */
  580. X    if (*p == '(' || *p == '"') {
  581. X    char *start = p;
  582. X    int comment = 1;
  583. X    c = *p;
  584. X    /* "str" points to address while p points to comments */
  585. X    if (addr && *str) {
  586. X        *p = 0;
  587. X        while (isspace(*str))
  588. X        str++;
  589. X        addr += Strcpy(addr, str);
  590. X        while (addr > beg_addr && isspace(*(addr-1)))
  591. X        *--addr = 0;
  592. X        *p = c;
  593. X    }
  594. X    while (comment) {
  595. X        if (c == '"' && !(p = index(p+1, '"')) ||
  596. X        c == '(' && !(p = any(p+1, "()"))) {
  597. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  598. X        return NULL;
  599. X        }
  600. X        if (*p == '(') /* loop again on parenthesis. quote ends loop */
  601. X        comment++;
  602. X        else
  603. X        comment--;
  604. X    }
  605. X    /* Something like ``Comment (Comment) <addr>''.  In this case
  606. X     * the name should include both comment parts with the
  607. X     * parenthesis.   We have to redo addr.
  608. X     */
  609. X    if ((p2 = any(p+1, "<,")) && *p2 == '<') {
  610. X        if (!(p = index(p2, '>'))) {
  611. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  612. X        return NULL;
  613. X        }
  614. X        if (addr = beg_addr) { /* reassign addr and compare to null */
  615. X        c = *p; *p = 0;
  616. X        addr += Strcpy(addr, p2+1);
  617. X        while (addr > beg_addr && isspace(*(addr-1)))
  618. X            *--addr = 0;
  619. X        *p = c;
  620. X        }
  621. X        if (name) {
  622. X        c = *p2; *p2 = 0;
  623. X        name += Strcpy(name, str);
  624. X        while (name > beg_name && isspace(*(name-1)))
  625. X            *--name = 0;
  626. X        *p2 = c;
  627. X        }
  628. X    } else if (name && start[1]) {
  629. X        c = *p, *p = 0; /* c may be ')' instead of '(' now */
  630. X        name += Strcpy(name, start+1);
  631. X        while (name > beg_name && isspace(*(name-1)))
  632. X        *--name = 0;
  633. X        *p = c;
  634. X    }
  635. X    }
  636. X    skipspaces(1);
  637. X    /* this is so common, save time by returning now */
  638. X    if (!*p || *p == ',' || *p == '<')
  639. X    return p;
  640. X    return get_name_n_addr(p, name, addr);
  641. }
  642. X
  643. /* takes string 's' which can be a name or list of names separated by
  644. X * commas and checks to see if each is aliased to something else.
  645. X * return address of the static buf.
  646. X */
  647. char *
  648. alias_to_address(s)
  649. register char *s;
  650. {
  651. X    static char buf[HDRSIZ];
  652. X    register char *p, *p2, *tmp;
  653. X    char newbuf[HDRSIZ], c;
  654. X    static int recursive;
  655. X
  656. X    if (!aliases)
  657. X    return strcpy(buf, s);
  658. X    if (!s || !*s)
  659. X    return NULL;
  660. X    if (!recursive) {
  661. X    bzero(buf, sizeof buf);
  662. X    p2 = buf;  /* if we're starting all this, p2 starts at &buf[0] */
  663. X    } else
  664. X    p2 = buf+strlen(buf);   /* else, pick up where we left off */
  665. X
  666. X    if (++recursive == 30) {
  667. X    wprint("alias references too many addresses!\n");
  668. X    recursive = 0;
  669. X    return NULL;
  670. X    }
  671. X    do  {
  672. X    char addr[256];
  673. X    if (!(p = get_name_n_addr(s, NULL, addr)))
  674. X        break;
  675. X    c = *p, *p = 0;
  676. X
  677. X    /* On recursive calls, compare against the entire
  678. X     * previous expansion, not just the address part.
  679. X     */
  680. X    if (recursive > 1)
  681. X        (void) strcpy(addr, s);
  682. X
  683. X    /* if this is an alias, recurse this routine to expand it out */
  684. X    if ((tmp = do_set(aliases, addr)) && *tmp) {
  685. X        if (!alias_to_address(strcpy(newbuf, tmp))) {
  686. X        *p = c;
  687. X        return NULL;
  688. X        } else
  689. X        p2 = buf+strlen(buf);
  690. X    /* Now, make sure the buffer doesn't overflow */
  691. X    } else if (strlen(s) + (p2-buf) + 2 > sizeof buf) {  /* add ", " */
  692. X        wprint("address length too long.\n");
  693. X        recursive = 0;
  694. X        *p = c;
  695. X        return NULL;
  696. X    } else {
  697. X        /* append the new alias (or unchanged address) onto the buffer */
  698. X        p2 += Strcpy(p2, s);
  699. X        *p2++ = ',', *p2++ = ' ', *p2 = '\0';
  700. X    }
  701. X    for (*p = c; *p == ',' || isspace(*p); p++)
  702. X        ;
  703. X    } while (*(s = p));
  704. X    if (recursive)
  705. X    recursive--;
  706. X    if (!recursive)
  707. X    *(p2-2) = 0;  /* get rid of last ", " if end of recursion */
  708. X    return buf;
  709. }
  710. X
  711. /*
  712. X * Wrap addresses so that the headers don't exceed n chars (typically 80).
  713. X */
  714. char *
  715. wrap_addrs(str, n)
  716. char *str;
  717. {
  718. X    char buf[HDRSIZ * 2], *start = str;
  719. X    register char *b = buf, *p, c, *line_start = buf;
  720. X
  721. X    *b = 0;
  722. X    do  {
  723. X    /* get_name returns a pointer to the next address */
  724. X    if (!(p = get_name_n_addr(str, NULL, NULL)))
  725. X        break;
  726. X    c = *p, *p = 0;
  727. X    if (b > buf) {
  728. X        *b++ = ',', *b++ = ' ', *b = '\0';
  729. X        if (b - line_start + strlen(str) + 8 /* \t = 8 */ >= n)
  730. X        *b++ = '\n', *b++ = '\t', line_start = b;
  731. X    }
  732. X    for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  733. X        *b = 0;
  734. X    for (*p = c; *p == ',' || isspace(*p); p++)
  735. X        ;
  736. X    } while (*(str = p));
  737. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  738. X    *b = 0;
  739. X    return strcpy(start, buf);
  740. }
  741. SHAR_EOF
  742. echo 'File addrs.c is complete' &&
  743. chmod 0644 addrs.c ||
  744. echo 'restore of addrs.c failed'
  745. Wc_c="`wc -c < 'addrs.c'`"
  746. test 33326 -eq "$Wc_c" ||
  747.     echo 'addrs.c: original size 33326, current size' "$Wc_c"
  748. rm -f _shar_wnt_.tmp
  749. fi
  750. # ============= advanced.mushrc ==============
  751. if test -f 'advanced.mushrc' -a X"$1" != X"-c"; then
  752.     echo 'x - skipping advanced.mushrc (File already exists)'
  753.     rm -f _shar_wnt_.tmp
  754. else
  755. > _shar_wnt_.tmp
  756. echo 'x - extracting advanced.mushrc (Text)'
  757. sed 's/^X//' << 'SHAR_EOF' > 'advanced.mushrc' &&
  758. # advanced.mushrc
  759. # by Bart Schaefer
  760. # with special thanks to Phil Lapsley <phil@east.Berkeley.EDU>, who
  761. # provided the original files on which this example is based.  Most of
  762. # Phil's stuff is still here -- just reorganized and updated to use
  763. # mush 6.4 features that were unavailable when Phil did the originals.
  764. #
  765. # This file is intended to demonstrate helpful ways to use the
  766. # .mushrc, not advanced mush commands.
  767. X
  768. # The variable $thisfolder is always set EXCEPT when the .mushrc file
  769. # is read the first time.  A test for non-existance of $thisfolder
  770. # allows the same .mushrc file to be sourced repeatedly without
  771. # redundant execution of the initialization commands.
  772. #
  773. if ! $?thisfolder
  774. X    # Ignore the usual stuff
  775. X    ignore    received via message-id status priority
  776. X    # Hide folders in ~/.mail and save read mail in spool
  777. X    set        folder=~/.mail hold
  778. X    # Remember a few commands, set up editors, act like a shell
  779. X    set        history=20 editor=ex visual=vi unix
  780. X    # Prompt has folder name, message number, history number
  781. X    set        prompt="%f %m (!) & "
  782. X    # Header summaries show name, date, and subject
  783. X    set        hdr_format="%25n %-15d  %27s"
  784. X    # Initialize the cmds below (see later comments)
  785. X    set        first_time=1
  786. X
  787. X    # These two commands are used for automated "bursting" of the spool
  788. X    # mailbox.  This means that the messages are reorganized into new
  789. X    # folders to be read in a prearranged order.  See comments below.
  790. X    #
  791. X    # n brings up the next folder, not the next message
  792. X    cmd        n    'source ~/.mushrc'
  793. X    # N gets the next folder without "bursting"
  794. X    cmd        N    'set first_time=0; source ~/.mushrc'
  795. X
  796. X    # Delete messages by pattern-matching.  Examples:
  797. X    #  del f mailer-daemon        Delete mail from mailer-daemon
  798. X    #  del t mush-users            Delete mail to mush-users
  799. X    cmd        del    'pick -i -\!* | delete'
  800. X    # Forwarding
  801. X    cmd        for    'mail -f'
  802. X    # Quick folder change
  803. X    cmd        F    'folder'
  804. X
  805. X    # Some useful aliases
  806. X    alias    dheller    'The Mush God <argv@sun.com>'
  807. X    alias    barts    'Archangel Mushael <schaefer@cse.ogi.edu>'
  808. X
  809. X    # On init, don't source beyond this point
  810. X    exit
  811. endif    # End of init section -- read on startup only
  812. X
  813. # This part of the file handles "bursting".  A burst is done when the
  814. # n cmd is used the first time.  This is most useful if you habitually
  815. # have lots of mail when you first log in each morning; unwanted mail
  816. # can be deleted, and other mail organized for you.
  817. #
  818. # The folders in this example bursting scheme are:
  819. #    mush-users    anything to or cc'ed to mush-users
  820. #    stats        daily stats
  821. #    root        root mail other than daily stats
  822. # Mail not falling into one of these categories is left in the system
  823. # mailbox to be dealt with first.
  824. #
  825. if $first_time
  826. X    # Kill off some uucp garbage
  827. X    pick -i -s "file c.* delete" | delete
  828. X    pick -i -s "file .* can.t access" | delete
  829. X    pick -i -s "remote access to path/file denied" | delete
  830. X    # Nuke the boring usenet stuff
  831. X    pick -i -f usenet | pick -i -s "uucp map for" | delete
  832. X    pick -i -t usenet | pick -i -s "returned mail" | delete
  833. X    pick -i -t usenet | pick -i -s "automatic test echo" | delete
  834. X    pick -i -t "owner-post" | pick -i -s "unknown mailer" | delete
  835. X    pick -i -s "usenet disk space report" | delete
  836. X    pick -i -s "very old news articles" | delete
  837. X    pick -i -s "uucp map for" | delete
  838. X    # Wipe out some uninteresting daily stats
  839. X    pick -i -s "the maid was here." | delete
  840. X    pick -i -s "daily accounting" | delete
  841. X    pick -i -t netsurvey | delete
  842. X    # Get rid of these things for good.  This isn't essential, but
  843. X    # avoids complexity in the later "pick" commands.
  844. X    update
  845. X    # Save anything "to" or "cc" to mush-users in that folder.
  846. X    pick -i -t mush-users | save +mush-users
  847. X    pick -i -h cc mush-users | save +mush-users
  848. X    # Also save interesting daily stat mail and generic root mail
  849. X    pick -i -f root | pick -i -s stats | save +stats
  850. X    pick -i -f root | pick -i -s report | save +stats
  851. X    pick -i -f uucp | pick -i -s report | save +stats
  852. X    pick -i -f root | pick -i -s summary | save +stats
  853. X    pick -i -f root | pick -i -s munge | save +stats
  854. X    pick -i -t root | save +root
  855. X    # Again, make the changes permanent.  Saved mail gets deleted.
  856. X    # This won't work if you have $keepsave set.
  857. X    update
  858. X
  859. X    # Make sure we don't burst again needlessly.
  860. X    set first_time=0
  861. X
  862. X    # Stop sourcing here.  Otherwise, we'd change folders without
  863. X    # handling the mail left in the system mailbox.
  864. X    exit
  865. endif
  866. X
  867. # Finally, handle stepping through the folders one by one.  This has been
  868. # set up for sendmail, where the system mailbox is /usr/spool/mail/$USER,
  869. # but could easily be modified for other mailers.
  870. #
  871. # $thisfolder:t returns the tail only of the folder name.
  872. X
  873. if $thisfolder:t == $USER
  874. X    folder +stats
  875. X    exit
  876. endif
  877. X
  878. if $thisfolder:t == stats
  879. X    folder +mush-users
  880. X    exit
  881. endif
  882. X
  883. if $thisfolder:t == mush-users
  884. X    folder +root
  885. X    exit
  886. endif
  887. X
  888. # Default back to the system mailbox
  889. folder %
  890. X
  891. # End of advanced.mushrc
  892. SHAR_EOF
  893. chmod 0644 advanced.mushrc ||
  894. echo 'restore of advanced.mushrc failed'
  895. Wc_c="`wc -c < 'advanced.mushrc'`"
  896. test 4976 -eq "$Wc_c" ||
  897.     echo 'advanced.mushrc: original size 4976, current size' "$Wc_c"
  898. rm -f _shar_wnt_.tmp
  899. fi
  900. # ============= bind.c ==============
  901. if test -f 'bind.c' -a X"$1" != X"-c"; then
  902.     echo 'x - skipping bind.c (File already exists)'
  903.     rm -f _shar_wnt_.tmp
  904. else
  905. > _shar_wnt_.tmp
  906. echo 'x - extracting bind.c (Text)'
  907. sed 's/^X//' << 'SHAR_EOF' > 'bind.c' &&
  908. /* bind.c */
  909. X
  910. #include "bindings.h"
  911. #include "mush.h"
  912. X
  913. extern char *c_macro();
  914. static un_bind();
  915. X
  916. struct cmd_map *cmd_map, *line_map, *bang_map;
  917. X
  918. /*
  919. X * Bindings are added here in REVERSE of the order that
  920. X * they will be displayed!  Display order is based on a
  921. X * guess about the frequency of use and (to a lesser
  922. X * extent) how hard they are to remember.
  923. X *
  924. X * The user's own new bindings, if any, will be displayed
  925. X * before any of these default bindings.
  926. X */
  927. init_bindings()
  928. {
  929. #ifdef CURSES
  930. X    /* Help gets displayed last */
  931. X    add_bind("?", C_HELP, NULL, &cmd_map);
  932. X    add_bind("V", C_VERSION, NULL, &cmd_map);
  933. X
  934. X    /* Miscellaneous shell commands */
  935. X    add_bind("%", C_CHDIR, NULL, &cmd_map);
  936. X    add_bind("|", C_PRINT_MSG, NULL, &cmd_map);
  937. X    add_bind("!", C_SHELL_ESC, NULL, &cmd_map);
  938. X    add_bind(":", C_CURSES_ESC, NULL, &cmd_map);
  939. X
  940. X    /* Mush customization commands */
  941. X    /* NOTE: No default C_MACRO bindings */
  942. X    add_bind(")", C_SAVEOPTS, NULL, &cmd_map);
  943. X    add_bind("(", C_SOURCE, NULL, &cmd_map);
  944. X    add_bind("&!", C_MAP_BANG, NULL, &cmd_map);
  945. X    add_bind("&:", C_MAP, NULL, &cmd_map);
  946. X    add_bind("&&", C_BIND_MACRO, NULL, &cmd_map);
  947. X    add_bind("v", C_VAR_SET, NULL, &cmd_map);
  948. X    add_bind("i", C_IGNORE, NULL, &cmd_map);
  949. X    add_bind("h", C_OWN_HDR, NULL, &cmd_map);
  950. X    add_bind("B", C_UNBIND, NULL, &cmd_map);
  951. X    add_bind("b", C_BIND, NULL, &cmd_map);
  952. X    add_bind("a", C_ALIAS, NULL, &cmd_map);
  953. X
  954. X    /* Display modification commands */
  955. X    add_bind("\022", C_REVERSE, NULL, &cmd_map);    /* ^R */
  956. X    add_bind("\014", C_REDRAW, NULL, &cmd_map);        /* ^L */
  957. X    add_bind("Z", C_PREV_SCREEN, NULL, &cmd_map);
  958. X    add_bind("z", C_NEXT_SCREEN, NULL, &cmd_map);
  959. X
  960. X    /* Searching and sorting commands */
  961. X    add_bind("\016", C_CONT_SEARCH, NULL, &cmd_map);    /* ^N */
  962. X    add_bind("\037", C_PREV_SEARCH, NULL, &cmd_map);    /* ^/ */
  963. X    add_bind("/", C_NEXT_SEARCH, NULL, &cmd_map);
  964. X    add_bind("O", C_REV_SORT, NULL, &cmd_map);
  965. X    add_bind("o", C_SORT, NULL, &cmd_map);
  966. X
  967. X    /* Ways to get out */
  968. X    add_bind("X", C_EXIT_HARD, NULL, &cmd_map);
  969. X    add_bind("x", C_EXIT, NULL, &cmd_map);
  970. X    add_bind("Q", C_QUIT_HARD, NULL, &cmd_map);
  971. X    add_bind("q", C_QUIT, NULL, &cmd_map);
  972. X
  973. X    /* Folder modification commands */
  974. X    add_bind("\025", C_UPDATE, NULL, &cmd_map);        /* ^U */
  975. X    add_bind("\020", C_PRESERVE, NULL, &cmd_map);    /* ^P */
  976. X    add_bind("*", C_MARK_MSG, NULL, &cmd_map);
  977. X    add_bind("W", C_WRITE_LIST, NULL, &cmd_map);
  978. X    add_bind("w", C_WRITE_MSG, NULL, &cmd_map);
  979. X    add_bind("U", C_UNDEL_LIST, NULL, &cmd_map);
  980. X    add_bind("u", C_UNDEL_MSG, NULL, &cmd_map);
  981. X    add_bind("S", C_SAVE_LIST, NULL, &cmd_map);
  982. X    add_bind("s", C_SAVE_MSG, NULL, &cmd_map);
  983. X    add_bind("f", C_FOLDER, NULL, &cmd_map);
  984. X    add_bind("D", C_DELETE_LIST, NULL, &cmd_map);
  985. X    add_bind("d", C_DELETE_MSG, NULL, &cmd_map);
  986. X    add_bind("C", C_COPY_LIST, NULL, &cmd_map);
  987. X    add_bind("c", C_COPY_MSG, NULL, &cmd_map);
  988. X
  989. X    /* Cursor movement and message selection */
  990. X    add_bind("g", C_GOTO_MSG, NULL, &cmd_map);
  991. X    add_bind("}", C_BOTTOM_PAGE, NULL, &cmd_map);
  992. X    add_bind("{", C_TOP_PAGE, NULL, &cmd_map);
  993. X    add_bind("$", C_LAST_MSG, NULL, &cmd_map);
  994. X    add_bind("^", C_FIRST_MSG, NULL, &cmd_map);
  995. X    add_bind("\013",C_PREV_MSG, NULL, &cmd_map);    /* ^K */
  996. X    add_bind("\012", C_NEXT_MSG, NULL, &cmd_map);    /* ^J */
  997. X    add_bind("-",C_PREV_MSG, NULL, &cmd_map);
  998. X    add_bind("+",C_NEXT_MSG, NULL, &cmd_map);
  999. X    add_bind("K", C_PREV_MSG, NULL, &cmd_map);
  1000. X    add_bind("k", C_PREV_MSG, NULL, &cmd_map);
  1001. X    add_bind("J", C_NEXT_MSG, NULL, &cmd_map);
  1002. X    add_bind("j", C_NEXT_MSG, NULL, &cmd_map);
  1003. X
  1004. X    /* Mail-sending commands */
  1005. X    add_bind("R", C_REPLY_ALL, NULL, &cmd_map);
  1006. X    add_bind("r", C_REPLY_SENDER, NULL, &cmd_map);
  1007. X    add_bind("M", C_MAIL_FLAGS, NULL, &cmd_map);
  1008. X    add_bind("m", C_MAIL, NULL, &cmd_map);
  1009. X
  1010. X    /* Mail-reading commands */
  1011. X    add_bind(".", C_DISPLAY_MSG, NULL, &cmd_map);
  1012. X    add_bind("T", C_TOP_MSG, NULL, &cmd_map);
  1013. X    add_bind("t", C_DISPLAY_MSG, NULL, &cmd_map);
  1014. X    add_bind("p", C_DISPLAY_MSG, NULL, &cmd_map);
  1015. X    add_bind("n", C_DISPLAY_NEXT, NULL, &cmd_map);
  1016. X
  1017. #endif /* CURSES */
  1018. }
  1019. X
  1020. /* Bindable function names.
  1021. X *  Most of these can't be used if CURSES is not defined,
  1022. X *  but help and lookups get confused if they aren't all here.
  1023. X */
  1024. struct cmd_map map_func_names[] = {
  1025. X    /* These MUST be in numerical order; see bindings.h */
  1026. X    { C_NULL,        "no-op",        NULL, NULL_MAP },
  1027. X    { C_GOTO_MSG,    "goto-msg",        NULL, NULL_MAP },
  1028. X    { C_WRITE_LIST,    "write-list",        NULL, NULL_MAP },
  1029. X    { C_WRITE_MSG,    "write",        NULL, NULL_MAP },
  1030. X    { C_SAVE_LIST,    "save-list",        NULL, NULL_MAP },
  1031. X    { C_SAVE_MSG,    "save",            NULL, NULL_MAP },
  1032. X    { C_COPY_LIST,    "copy-list",        NULL, NULL_MAP },
  1033. X    { C_COPY_MSG,    "copy",            NULL, NULL_MAP },
  1034. X    { C_DELETE_LIST,    "delete-list",        NULL, NULL_MAP },
  1035. X    { C_DELETE_MSG,    "delete",        NULL, NULL_MAP },
  1036. X    { C_UNDEL_LIST,    "undelete-list",    NULL, NULL_MAP },
  1037. X    { C_UNDEL_MSG,    "undelete",        NULL, NULL_MAP },
  1038. X    { C_REDRAW,        "redraw",        NULL, NULL_MAP },
  1039. X    { C_REVERSE,    "reverse-video",    NULL, NULL_MAP },
  1040. X    { C_NEXT_MSG,    "next-msg",        NULL, NULL_MAP },
  1041. X    { C_PREV_MSG,    "back-msg",        NULL, NULL_MAP },
  1042. X    { C_FIRST_MSG,    "first-msg",        NULL, NULL_MAP },
  1043. X    { C_LAST_MSG,    "last-msg",        NULL, NULL_MAP },
  1044. X    { C_TOP_PAGE,    "top-page",        NULL, NULL_MAP },
  1045. X    { C_BOTTOM_PAGE,    "bottom-page",        NULL, NULL_MAP },
  1046. X    { C_NEXT_SCREEN,    "screen-next",        NULL, NULL_MAP },
  1047. X    { C_PREV_SCREEN,    "screen-back",        NULL, NULL_MAP },
  1048. X    { C_SOURCE,        "source",        NULL, NULL_MAP },
  1049. X    { C_SAVEOPTS,    "saveopts",        NULL, NULL_MAP },
  1050. X    { C_NEXT_SEARCH,    "search-next",        NULL, NULL_MAP },
  1051. X    { C_PREV_SEARCH,    "search-back",        NULL, NULL_MAP },
  1052. X    { C_CONT_SEARCH,    "search-again",        NULL, NULL_MAP },
  1053. X    { C_PRESERVE,    "preserve",        NULL, NULL_MAP },
  1054. X    { C_REV_SORT,    "sort-reverse",        NULL, NULL_MAP },
  1055. X    { C_SORT,        "sort",            NULL, NULL_MAP },
  1056. X    { C_QUIT_HARD,    "quit!",        NULL, NULL_MAP },
  1057. X    { C_QUIT,        "quit",            NULL, NULL_MAP },
  1058. X    { C_EXIT_HARD,    "exit!",        NULL, NULL_MAP },
  1059. X    { C_EXIT,        "exit",            NULL, NULL_MAP },
  1060. X    { C_UPDATE,        "update",        NULL, NULL_MAP },
  1061. X    { C_FOLDER,        "folder",        NULL, NULL_MAP },
  1062. X    { C_SHELL_ESC,    "shell-escape",        NULL, NULL_MAP },
  1063. X    { C_CURSES_ESC,    "line-mode",        NULL, NULL_MAP },
  1064. X    { C_PRINT_MSG,    "lpr",            NULL, NULL_MAP },
  1065. X    { C_CHDIR,        "chdir",        NULL, NULL_MAP },
  1066. X    { C_VAR_SET,    "variable",        NULL, NULL_MAP },
  1067. X    { C_IGNORE,        "ignore",        NULL, NULL_MAP },
  1068. X    { C_ALIAS,        "alias",        NULL, NULL_MAP },
  1069. X    { C_OWN_HDR,    "my-hdrs",        NULL, NULL_MAP },
  1070. X    { C_VERSION,    "version",        NULL, NULL_MAP },
  1071. X    { C_MAIL_FLAGS,    "mail-flags",        NULL, NULL_MAP },
  1072. X    { C_MAIL,        "mail",            NULL, NULL_MAP },
  1073. X    { C_REPLY_ALL,    "reply-all",        NULL, NULL_MAP },
  1074. X    { C_REPLY_SENDER,    "reply",        NULL, NULL_MAP },
  1075. X    { C_DISPLAY_NEXT,    "display-next",        NULL, NULL_MAP },
  1076. X    { C_DISPLAY_MSG,    "display",        NULL, NULL_MAP },
  1077. X    { C_TOP_MSG,    "top",            NULL, NULL_MAP },
  1078. X    { C_BIND_MACRO,    "bind-macro",        NULL, NULL_MAP },
  1079. X    { C_BIND,        "bind",            NULL, NULL_MAP },
  1080. X    { C_UNBIND,        "unbind",        NULL, NULL_MAP },
  1081. X    { C_MAP_BANG,    "map!",            NULL, NULL_MAP },
  1082. X    { C_MAP,        "map",            NULL, NULL_MAP },
  1083. X    { C_MACRO,        "macro",        NULL, NULL_MAP },
  1084. X    { C_MARK_MSG,    "mark",            NULL, NULL_MAP },
  1085. X    /* C_HELP Must be the last one! */
  1086. X    { C_HELP,        "help",            NULL, NULL_MAP }
  1087. };
  1088. X
  1089. #ifdef CURSES
  1090. X
  1091. /*
  1092. X * getcmd() is called from curses mode only.  It waits for char input from
  1093. X * the user via m_getchar() (which means that a macro could provide input)
  1094. X * and then compares the chars input against the "bind"ings set up by the
  1095. X * user (or the defaults).  For example, 'j' could bind to "next msg" which
  1096. X * is interpreted by the big switch statement in curses_command() (curses.c).
  1097. X * getcmd() returns the int-value of the curses command the input is "bound"
  1098. X * to.  If the input is unrecognized, C_NULL is returned (curses_command()
  1099. X * might require some cleanup, so this is valid, too).
  1100. X *
  1101. X * Since the input could originate from a macro rather than the terminal,
  1102. X * check to see if this is the case and search for a '[' char which indicates
  1103. X * that there is a curses command or other "long" command to be executed.
  1104. X */
  1105. getcmd()
  1106. {
  1107. X    char         buf[MAX_BIND_LEN * 3];
  1108. X    register int     c, m, match;
  1109. X    register char    *p = buf;
  1110. X    register struct cmd_map *list;
  1111. X
  1112. X    bzero(buf, MAX_BIND_LEN);
  1113. X    active_cmd = NULL_MAP;
  1114. X    c = m_getchar();
  1115. X    /* If user did job control (^Z), then the interrupt flag will be
  1116. X     * set.  Be sure it's unset before continuing.
  1117. X     */
  1118. X    turnoff(glob_flags, WAS_INTR);
  1119. X    if (isdigit(c)) {
  1120. X    buf[0] = c;
  1121. X    buf[1] = '\0';
  1122. X    Ungetstr(buf); /* So mac_flush can clear on error */
  1123. X    return C_GOTO_MSG;
  1124. X    }
  1125. X    for (;;) {
  1126. X    if (ison(glob_flags, IN_MACRO) && c == MAC_LONG_CMD)
  1127. X        return long_mac_cmd(c, TRUE);
  1128. X    else
  1129. X        *p++ = c;
  1130. X    m = 0;
  1131. X    for (list = cmd_map; list; list = list->m_next) {
  1132. X        if ((match = prefix(buf, list->m_str)) == MATCH) {
  1133. X        if (debug)
  1134. X            print("\"%s\" ",
  1135. X            ctrl_strcpy(buf,
  1136. X                    map_func_names[list->m_cmd].m_str,
  1137. X                    TRUE));
  1138. X        if (list->m_cmd == C_MACRO) {
  1139. X            curs_macro(list->x_str);
  1140. X            return getcmd();
  1141. X        }
  1142. X        active_cmd = list;
  1143. X        return (int)list->m_cmd;
  1144. X        } else if (match != NO_MATCH)
  1145. X        m++;
  1146. X    }
  1147. X    if (m == 0) {
  1148. X        if (debug) {
  1149. X        char tmp[sizeof buf];
  1150. X        print("No binding for \"%s\" found.",
  1151. X            ctrl_strcpy(tmp, buf, TRUE));
  1152. X        }
  1153. X        return C_NULL;
  1154. X    }
  1155. X    c = m_getchar();
  1156. X    }
  1157. }
  1158. X
  1159. #endif /* CURSES */
  1160. X
  1161. /*
  1162. X * bind_it() is used to set or unset bind, map and map! settings.
  1163. X * bind is used to accelerate curses commands by mapping key sequences
  1164. X * to curses commands.  map is used to accelerate command mode keysequences
  1165. X * by simulating stdin.  map! is the same, but used when in compose mode.
  1166. X *
  1167. X * bind_it() doesn't touch messages; return -1 for curses mode.
  1168. X * return -2 to have curses command set CNTD_CMD to prevent screen refresh
  1169. X * to allow user to read output in case of multiple lines.
  1170. X *
  1171. X * Since this routine deals with a lot of binding and unbinding of things
  1172. X * like line-mode "map"s and is interactive (calls Getstr()), be very careful
  1173. X * not to allow expansions during interaction.
  1174. X */
  1175. bind_it(len, argv)
  1176. char **argv;
  1177. {
  1178. X    char string[MAX_BIND_LEN], buf[256], *name = NULL;
  1179. X    char *rawstr; /* raw format of string (ptr to string if no argv avail) */
  1180. X    char ascii[MAX_BIND_LEN*2]; /* printable ascii version of string */
  1181. X    register int x;
  1182. X    SIGRET (*oldint)(), (*oldquit)();
  1183. X    struct cmd_map **map_list;
  1184. X    int unbind = (argv && **argv == 'u');
  1185. X    int map = 0, is_bind_macro = 0;
  1186. X    int ret = 0 - iscurses; /* return value */
  1187. X
  1188. X    if (argv && !strcmp(name = *argv, "bind-macro"))
  1189. X    is_bind_macro++;
  1190. X
  1191. X    if (map = (argv && (!strcmp(name, "map!") || !strcmp(name, "unmap!"))))
  1192. X    map_list = &bang_map;
  1193. X    else if (map = (argv && (!strcmp(name, "map") || !strcmp(name, "unmap"))))
  1194. X    map_list = &line_map;
  1195. X    else
  1196. X    map_list = &cmd_map;
  1197. X
  1198. X    if (argv && *++argv && !strcmp(*argv, "-?"))
  1199. X    /* Subtract ret and iscurses to signal output */
  1200. X    return help(0, unbind? name+2 : name, cmd_help) - ret - iscurses;
  1201. X
  1202. X    if (iscurses)
  1203. X    on_intr();
  1204. X
  1205. X    if (unbind) {
  1206. X    if (!*argv) {
  1207. X        char savec = complete;
  1208. X        complete = 0;
  1209. X        print("%s what? ", name);
  1210. X        len = Getstr(buf, sizeof buf, 0);
  1211. X        complete = savec;
  1212. X        if (len <= 0) {
  1213. X        if (iscurses)
  1214. X            off_intr();
  1215. X        return -1;
  1216. X        }
  1217. X        rawstr = m_xlate(buf);
  1218. X    } else
  1219. X        rawstr = m_xlate(*argv);
  1220. X    if (!un_bind(rawstr, map_list)) {
  1221. X        (void) ctrl_strcpy(ascii, rawstr, TRUE);
  1222. X        print("\"%s\" isn't bound to a command.\n", ascii);
  1223. X    }
  1224. X    if (iscurses)
  1225. X        off_intr();
  1226. X    return ret;
  1227. X    }
  1228. X    if (argv && *argv) {
  1229. X    rawstr = m_xlate(*argv);
  1230. X    (void) ctrl_strcpy(ascii, rawstr, TRUE);
  1231. X    if (!*++argv) {
  1232. X        /*
  1233. X         * determine whether "argv" references a "map" or a "bind"
  1234. X         */
  1235. X        int binding = c_bind(rawstr, *map_list);
  1236. X        if (binding == C_MACRO) {
  1237. X        char *mapping = c_macro(NULL, rawstr, *map_list);
  1238. X        if (mapping) {
  1239. X            print("\"%s\" is mapped to ", ascii);
  1240. X            print_more("\"%s\".\n",
  1241. X            ctrl_strcpy(buf, mapping, FALSE));
  1242. X        } else
  1243. X            print("\"%s\" isn't mapped.\n", ascii);
  1244. X        } else if (binding)
  1245. X        print("\"%s\" is %s to \"%s\".\n", ascii,
  1246. X            map? "mapped" : "bound", map_func_names[binding].m_str);
  1247. X        else if (map)
  1248. X        print("\"%s\" isn't mapped.\n", ascii);
  1249. X        else
  1250. X        print("\"%s\" isn't bound to a command.\n", ascii);
  1251. X        if (iscurses)
  1252. X        off_intr();
  1253. X        return ret;
  1254. X    }
  1255. X    } else {
  1256. X    char savec = complete;
  1257. X    complete = 0;
  1258. X    print("%s [<CR>=all, -?=help]: ", name);
  1259. X    len = Getstr(string, MAX_BIND_LEN-1, 0);
  1260. X    complete = savec;
  1261. X    if (len == 0) {
  1262. X        int add_to_ret = iscurses;
  1263. #ifdef CURSES
  1264. X        if (iscurses)
  1265. X        move(LINES-1, 0), refresh();
  1266. #endif
  1267. X        if (map || is_bind_macro)
  1268. X        add_to_ret = !c_macro(name, NULL, *map_list);
  1269. X        else
  1270. X        add_to_ret = !c_bind(NULL, *map_list);
  1271. X        if (iscurses)
  1272. X        off_intr();
  1273. X        /* signal CTND_CMD if there was output */
  1274. X        return ret - add_to_ret;
  1275. X    }
  1276. X    if (len < 0) {
  1277. X        if (iscurses)
  1278. X        off_intr();
  1279. X        return ret;
  1280. X    }
  1281. X    rawstr = m_xlate(string);
  1282. X    (void) ctrl_strcpy(ascii, rawstr, TRUE);
  1283. X    }
  1284. X    /* if a binding was given on the command line */
  1285. X    if (argv && *argv && !map)
  1286. X    if (is_bind_macro)
  1287. X        (void) strcpy(buf, "macro");
  1288. X    else
  1289. X        (void) strcpy(buf, *argv++);
  1290. X    else {
  1291. X    /* at this point, "rawstr" and "ascii" should both be set */
  1292. X    int binding;
  1293. X
  1294. X    if (!strcmp(ascii, "-?")) {
  1295. X        if (iscurses)
  1296. X        clr_bot_line();
  1297. X        ret -= help(0, name, cmd_help);
  1298. X        if (iscurses)
  1299. X        off_intr();
  1300. X        /* Subtract iscurses to signal CNTD_CMD */
  1301. X        return ret - iscurses;
  1302. X    }
  1303. X
  1304. X    if (!map && !is_bind_macro) {
  1305. X        binding = c_bind(rawstr, *map_list);
  1306. X
  1307. X        for (len = 0; len == 0; ) {
  1308. X        print("\"%s\" = <%s>: New binding [<CR> for list]: ",
  1309. X            ascii, (binding? map_func_names[binding].m_str : "unset"));
  1310. X        len = Getstr(buf, sizeof buf, 0);
  1311. X        if (iscurses)
  1312. X            clr_bot_line();
  1313. X        /* strip any trailing whitespace */
  1314. X        if (len > 0)
  1315. X            len = no_newln(buf) - buf;
  1316. X        if (len == 0) {
  1317. X            (void) do_pager(NULL, TRUE);
  1318. X            if (iscurses)
  1319. X            putchar('\n');
  1320. X            for (x = 1; x <= C_HELP; x++) {
  1321. X            if (!(x % 4))
  1322. X                if (do_pager("\n", FALSE) == EOF)
  1323. X                break;
  1324. X            (void) do_pager(sprintf(buf, "%-15.15s  ",
  1325. X                        map_func_names[x].m_str), FALSE);
  1326. X            }
  1327. X            (void) do_pager("\n", FALSE);
  1328. X            (void) do_pager(NULL, FALSE);
  1329. X            ret -= iscurses;
  1330. X        }
  1331. X        }
  1332. X    } else /* map */
  1333. X        (void) strcpy(buf, "macro"), len = 5;
  1334. X    /* if list was printed, ret < -1 -- tells CNTD_CMD to be set and
  1335. X     * prevents screen from being refreshed (lets user read output
  1336. X     */
  1337. X    if (len == -1) {
  1338. X        if (iscurses)
  1339. X        off_intr();
  1340. X        return ret;
  1341. X    }
  1342. X    }
  1343. X    for (x = 1; x <= C_HELP; x++) {
  1344. X    if (prefix(buf, map_func_names[x].m_str) == MATCH) {
  1345. X        int add_to_ret;
  1346. X        if (debug)
  1347. X        print("\"%s\" will execute \"%s\".\n", ascii, buf);
  1348. X        if (map_func_names[x].m_cmd == C_MACRO) {
  1349. X        if (argv && *argv) {
  1350. X            (void) argv_to_string(buf, argv);
  1351. X            (void) m_xlate(buf); /* Convert buf to raw chars */
  1352. X            add_to_ret =
  1353. X            do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
  1354. X        } else {
  1355. X            char exp[MAX_MACRO_LEN*2]; /* printable expansion */
  1356. X            char *mapping = c_macro(NULL, rawstr, *map_list);
  1357. X
  1358. X            if (mapping)
  1359. X            (void) ctrl_strcpy(exp, mapping, TRUE);
  1360. X            print("\"%s\" = <%s>", ascii, mapping ? exp : "unset");
  1361. X            putchar('\n'), print("New macro: ");
  1362. X            ret -= iscurses; /* To signal screen messed up */
  1363. X            /* we are done with buf, so we can trash over it */
  1364. X            len = Getstr(buf, MAX_MACRO_LEN, 0);
  1365. X            if (len > 0) {
  1366. X            if (iscurses)
  1367. X                clr_bot_line();
  1368. X            (void) m_xlate(buf); /* Convert buf to raw chars */
  1369. X            add_to_ret =
  1370. X                do_bind(rawstr, C_MACRO, buf, map_list);
  1371. X            if (debug) {
  1372. X                (void) ctrl_strcpy(exp, buf, TRUE);
  1373. X                print("\"%s\" will execute \"%s\".\n", ascii, exp);
  1374. X            }
  1375. X            } else if (len < 0) {
  1376. X            if (iscurses)
  1377. X                off_intr();
  1378. X            return ret;
  1379. X            } else
  1380. X            print("Can't bind to null macro"), putchar('\n');
  1381. X        }
  1382. X        } else /* not a macro */ {
  1383. X        (void) argv_to_string(buf, argv);
  1384. X        add_to_ret =
  1385. X            do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
  1386. X        }
  1387. X        /* if do_bind had no errors, it returned -1.  If we already
  1388. X         * messed up the screen, then ret is less than -1.  return the
  1389. X         * lesser of the two to make sure that CNTD_CMD gets set right
  1390. X         */
  1391. X        if (iscurses)
  1392. X        off_intr();
  1393. X        return min(add_to_ret, ret);
  1394. X    }
  1395. X    }
  1396. X    print("\"%s\": Unknown function.\n", buf);
  1397. X    if (iscurses)
  1398. X    off_intr();
  1399. X    return ret;
  1400. }
  1401. X
  1402. /*
  1403. X * print current key to command bindings if "str" is NULL.
  1404. X * else return the integer "m_cmd" which the str is bound to.
  1405. X */
  1406. c_bind(str, opts)
  1407. register char *str;
  1408. register struct cmd_map *opts;
  1409. {
  1410. X    register int    incurses = iscurses;
  1411. X
  1412. X    if (!str) {
  1413. X    if (!opts) {
  1414. X        print("No command bindings.\n");
  1415. X        return C_ERROR;
  1416. X    }
  1417. X    if (incurses)
  1418. X        clr_bot_line(), iscurses = FALSE;
  1419. X    (void) do_pager(NULL, TRUE);
  1420. X    (void) do_pager("Current key to command bindings:\n", FALSE);
  1421. X    (void) do_pager("\n", FALSE);
  1422. X    }
  1423. X
  1424. X    for (; opts; opts = opts->m_next) {
  1425. X    char buf[BUFSIZ], buf2[MAX_BIND_LEN], exp[MAX_MACRO_LEN*2], *xp;
  1426. X    if (!str) {
  1427. X        (void) ctrl_strcpy(buf2, opts->m_str, FALSE);
  1428. X        if ((xp = opts->x_str) && opts->m_cmd == C_MACRO)
  1429. X        xp = ctrl_strcpy(exp, opts->x_str, TRUE);
  1430. X        if (do_pager(sprintf(buf, "%s\t%-15.15s %s\n",
  1431. X             buf2, map_func_names[opts->m_cmd].m_str,
  1432. X             xp? xp : ""),
  1433. X             FALSE) == EOF)
  1434. X        break;
  1435. X    } else
  1436. X        if (strcmp(str, opts->m_str))
  1437. X        continue;
  1438. X        else
  1439. X        return opts->m_cmd;
  1440. X    }
  1441. X
  1442. X    iscurses = incurses;
  1443. X    if (!str)
  1444. X    (void) do_pager(NULL, FALSE);
  1445. X    return C_NULL;
  1446. }
  1447. X
  1448. /*
  1449. X * Doesn't touch messages, but changes macros: return -1.
  1450. X * Error output causes return < -1.
  1451. X *  args is currently the execute string of a macro mapping, but may be
  1452. X *  used in the future as an argument string for any curses command.
  1453. X */
  1454. do_bind(str, func, args, map_list)
  1455. register char *str, *args;
  1456. struct cmd_map **map_list;
  1457. long func;
  1458. {
  1459. X    register int ret = -1;
  1460. X    register struct cmd_map *list;
  1461. X    int match;
  1462. X
  1463. X    if (func == C_MACRO && !check_mac_bindings(args))
  1464. X    --ret;
  1465. X    (void) un_bind(str, map_list);
  1466. X    for (list = *map_list; list; list = list->m_next)
  1467. X    if ((match = prefix(str, list->m_str)) != NO_MATCH) {
  1468. X        ret--;
  1469. X        switch (match) {
  1470. X        case MATCH:
  1471. X            puts("Something impossible just happened.");
  1472. X        when A_PREFIX_B:
  1473. X            wprint("Warning: \"%s\" prefixes \"%s\" (%s)\n", str,
  1474. X            list->m_str, map_func_names[list->m_cmd].m_str);
  1475. X        when B_PREFIX_A:
  1476. X            wprint("Warning: \"%s\" (%s) prefixes: \"%s\"\n",
  1477. X            list->m_str, map_func_names[list->m_cmd].m_str, str);
  1478. X        }
  1479. X    }
  1480. X    add_bind(str, func, args, map_list);
  1481. X    /* errors decrement ret.  If ret returns less than -1, CNTD_CMD is set
  1482. X     * and no redrawing is done so user can see the warning signs
  1483. X     */
  1484. X    return ret;
  1485. }
  1486. X
  1487. /*
  1488. X * add a binding to a list.  This may include "map"s or other mappings since
  1489. X * the map_list argument can control that.  The "func" is an int defined in
  1490. X * bindings.h ... the "str" passed is the string the user would have to type
  1491. X * to get the macro/map/binding expanded.  This must in in raw format: no
  1492. X * \n's to mean \015.  Convert first using m_xlate().
  1493. X */
  1494. add_bind(str, func, args, map_list)
  1495. register char *str, *args;
  1496. struct cmd_map **map_list;
  1497. long func;
  1498. {
  1499. X    register struct cmd_map *tmp;
  1500. X
  1501. X    if (!str || !*str)
  1502. X    return;
  1503. X
  1504. X    /* now make a new option struct and set fields */
  1505. X    if (!(tmp = (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
  1506. X    error("calloc");
  1507. X    return;
  1508. X    }
  1509. X    tmp->m_next = *map_list;
  1510. X    *map_list = tmp;
  1511. X
  1512. X    tmp->m_str = savestr(str);
  1513. X    tmp->m_cmd = func; /* strdup handles the NULL case */
  1514. X    if (args && *args)
  1515. X    tmp->x_str = savestr(args);
  1516. X    else
  1517. X    tmp->x_str = NULL;
  1518. }
  1519. X
  1520. static
  1521. un_bind(p, map_list)
  1522. register char *p;
  1523. struct cmd_map **map_list;
  1524. {
  1525. X    register struct cmd_map *list = *map_list, *tmp;
  1526. X
  1527. X    if (!list || !*list->m_str || !p || !*p)
  1528. X    return 0;
  1529. X
  1530. X    if (!strcmp(p, (*map_list)->m_str)) {
  1531. X    *map_list = (*map_list)->m_next;
  1532. X    xfree (list->m_str);
  1533. X    if (list->x_str)
  1534. X        xfree (list->x_str);
  1535. X    xfree((char *)list);
  1536. X    return 1;
  1537. X    }
  1538. X    for ( ; list->m_next; list = list->m_next)
  1539. X    if (!strcmp(p, list->m_next->m_str)) {
  1540. X        tmp = list->m_next;
  1541. X        list->m_next = list->m_next->m_next;
  1542. X        xfree (tmp->m_str);
  1543. X        if (tmp->x_str)
  1544. X        xfree (tmp->x_str);
  1545. X        xfree ((char *)tmp);
  1546. X        return 1;
  1547. X    }
  1548. X    return 0;
  1549. }
  1550. X
  1551. prefix(a, b)
  1552. register char *a, *b;
  1553. {
  1554. X    if (!a || !b)
  1555. X    return NO_MATCH;
  1556. X
  1557. X    while (*a && *b && *a == *b)
  1558. X    a++, b++;
  1559. X    if (!*a && !*b)
  1560. X    return MATCH;
  1561. X    if (!*a && *b)
  1562. X    return A_PREFIX_B;
  1563. X    if (*a && !*b)
  1564. X    return B_PREFIX_A;
  1565. X    return NO_MATCH;
  1566. }
  1567. SHAR_EOF
  1568. chmod 0644 bind.c ||
  1569. echo 'restore of bind.c failed'
  1570. Wc_c="`wc -c < 'bind.c'`"
  1571. test 20663 -eq "$Wc_c" ||
  1572.     echo 'bind.c: original size 20663, current size' "$Wc_c"
  1573. rm -f _shar_wnt_.tmp
  1574. fi
  1575. # ============= bindings.h ==============
  1576. if test -f 'bindings.h' -a X"$1" != X"-c"; then
  1577.     echo 'x - skipping bindings.h (File already exists)'
  1578.     rm -f _shar_wnt_.tmp
  1579. else
  1580. > _shar_wnt_.tmp
  1581. echo 'x - extracting bindings.h (Text)'
  1582. sed 's/^X//' << 'SHAR_EOF' > 'bindings.h' &&
  1583. /* bindings.h  -- command bindings */
  1584. X
  1585. #define MAX_BIND_LEN 20   /* max length a string can be to bind to a command */
  1586. #define MAX_MACRO_LEN 256 /* max length of a macro bound to a command */
  1587. X
  1588. /* to see if a key sequence matches, prefixes or misses a set binding */
  1589. #define NO_MATCH    0
  1590. #define MATCH        1
  1591. #define A_PREFIX_B    2
  1592. #define B_PREFIX_A    3
  1593. X
  1594. /*
  1595. X * Constants to define curses mode functions.
  1596. X */
  1597. #ifdef NULL_MAP
  1598. #undef NULL_MAP
  1599. #endif /* NULL_MAP */
  1600. #define NULL_MAP    (struct cmd_map *)0
  1601. X
  1602. #define C_ERROR        (-1L)
  1603. #define C_NULL        0L
  1604. #define C_GOTO_MSG    1L
  1605. #define C_WRITE_LIST    2L
  1606. #define C_WRITE_MSG    3L
  1607. #define C_SAVE_LIST    4L
  1608. #define C_SAVE_MSG    5L
  1609. #define C_COPY_LIST    6L
  1610. #define C_COPY_MSG    7L
  1611. #define C_DELETE_LIST    8L
  1612. #define C_DELETE_MSG    9L
  1613. #define C_UNDEL_LIST    10L
  1614. #define C_UNDEL_MSG    11L
  1615. #define C_REDRAW    12L
  1616. #define C_REVERSE    13L
  1617. #define C_NEXT_MSG    14L
  1618. SHAR_EOF
  1619. true || echo 'restore of bindings.h failed'
  1620. fi
  1621. echo 'End of  part 3'
  1622. echo 'File bindings.h is continued in part 4'
  1623. echo 4 > _shar_seq_.tmp
  1624. exit 0
  1625. exit 0 # Just in case...
  1626. -- 
  1627. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1628. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1629. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1630. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1631.