v18i060: mush - Mail User's Shell, Part03/22
Dan Heller
argv at zipcode.com
Sun Apr 21 12:49:11 AEST 1991
Submitted-by: Dan Heller <argv at zipcode.com>
Posting-number: Volume 18, Issue 60
Archive-name: mush/part03
Supersedes: mush: Volume 12, Issue 28-47
#!/bin/sh
# do not concatenate these parts, unpack them in order with /bin/sh
# file addrs.c continued
#
if test ! -r _shar_seq_.tmp; then
echo 'Please unpack part 1 first!'
exit 1
fi
(read Scheck
if test "$Scheck" != 3; then
echo Please unpack part "$Scheck" next!
exit 1
else
exit 0
fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
echo 'x - still skipping addrs.c'
else
echo 'x - continuing file addrs.c'
sed 's/^X//' << 'SHAR_EOF' >> 'addrs.c' &&
X p = sender + c;
X *p++ = ',', *p++ = ' ';
X (void) strcpy(p, next);
X (void) strcpy(to, sender);
}
X
/*
X * pass a string describing header like, "Subject: ", current value, and
X * whether or not to prompt for it or to just post the information.
X * If do_prompt is true, "type in" the current value so user can either
X * modify it, erase it, or add to it.
X */
char *
set_header(str, curstr, do_prompt)
register char *str, *curstr;
{
X static char buf[HDRSIZ];
X int offset = 0;
X register char *p = curstr;
X
X if (!str)
X str = "";
X
X buf[0] = 0;
X print(str);
X (void) fflush(stdout); /* force str curstr */
X if (do_prompt) {
X if (curstr)
X if (isoff(glob_flags, ECHO_FLAG)) {
X Ungetstr(curstr);
X } else
#ifdef TIOCSTI
X for (p = curstr; *p; p++)
X if (ioctl(0, TIOCSTI, p) == -1) {
X error("ioctl: TIOCSTI");
X print("You must retype the entire line.\n%s", str);
X break;
X }
#else /* !TIOCSTI */
X print("WARNING: -e flag! Type the line over.\n%s", str);
#endif /* TIOCSTI */
X
X if (istool)
X return NULL;
X /* simulate the fact that we're getting input for the letter even tho
X * we may not be. set_header is called before IS_GETTING is true,
X * but if we set it to true temporarily, then signals will return to
X * the right place (stop/continue).
X */
X {
X u_long getting = ison(glob_flags, IS_GETTING);
X int wrapping = wrapcolumn;
X /* Funky trick here. If the prompt string is empty,
X * assume that we are allowed to do line wrap;
X * otherwise, temporarily disable line wrap
X */
X if (*str)
X wrapcolumn = 0;
X if (!getting)
X turnon(glob_flags, IS_GETTING);
X if (Getstr(buf, sizeof(buf), offset) == -1) {
X putchar('\n');
X buf[0] = 0;
X }
X if (!getting)
X turnoff(glob_flags, IS_GETTING);
X wrapcolumn = wrapping;
X }
X } else
X puts(strcpy(buf, curstr));
X if (debug > 1)
X print("returning (%s) from set_header\n", buf);
X return buf;
}
X
/*
X * improve uucp paths by looking at the name of each host listed in the
X * path given.
X * sun!island!pixar!island!argv
X * It's a legal address, but redundant. Also, if we know we talk to particular
X * hosts via uucp, then we can just start with that host and disregard the path
X * preceding it. So, first get the known hosts and save them. Then start
X * at the end of the original path (at the last ! found), and move backwards
X * saving each hostname. If we get to a host that we know about, stop there
X * and use that address. If the system knows about domains, skip all paths
X * that precede a domain hostname. If we get to a host we've already seen,
X * then delete it and all the hosts since then until the first occurrence of
X * that hostname. When we get to the beginning, the address will be complete.
X * The route_path is prepended to each address to check make sure this path
X * is used if no known_hosts precede it in that address.
X *
X * Return all results into the original buffer passed to us. If route_path
X * adds to the length of all the paths, then the original buffer could be
X * overwritten. someone should check for this!
X */
improve_uucp_paths(original, size, route_path)
char *original, *route_path;
{
X char name[256], addr[256], buf[2 * HDRSIZ], *end;
X char *hostnames[32], tmp[sizeof addr], *domain_path;
X register char *p, *p2, *recipient, *start = original, *b = buf;
X int saved_hosts, i, is_domain;
X
X if (!original || !*original)
X return;
X
X /* use domain_path to point to the path for pathnames that have
X * a fully qualified domain host in them.
X */
X domain_path = do_set(set_options, "domain_route");
X while (end = get_name_n_addr(start, name, tmp)) {
X /* first copy the route path, then the rest of the address. */
X p = addr;
X if (route_path && *route_path) {
X p += Strcpy(addr, route_path);
X *p++ = '!';
X }
X (void) bang_form(p, tmp);
X saved_hosts = 0;
X if (p2 = rindex(p, '!')) {
X recipient = p2+1;
X /* save the uucp-style address *without* route_path in tmp */
X (void) strcpy(tmp, p);
X for (p = p2; p > addr; p--) {
X is_domain = 0;
X /* null the '!' separating the rest of the path from the part
X * of the path preceding it and move p back to the previous
X * '!' (or beginning to addr) for hostname to point to.
X */
X for (*p-- = 0; p > addr && *p != '!'; p--)
X if (!is_domain && domain_path && *p == '.' &&
X lcase_strncmp(p, ".uucp", 5))
X is_domain++;
X /* if p is not at the addr, move it forward past the '!' */
X if (p != addr)
X ++p; /* now points to a null terminated hostname */
X /* if host is ourselves, ignore this and preceding hosts */
X for (i = 0; ourname && ourname[i]; i++)
X if (!lcase_strncmp(p, ourname[i], -1))
X break;
X if (ourname && ourname[i]) {
X is_domain = 0; /* we've eliminated all domains */
X break;
X }
X /* check already saved hostnames. If host is one of them,
X * delete remaining hostnames since there is a redundant path.
X */
X for (i = 0; i < saved_hosts; i++)
X if (!lcase_strncmp(hostnames[i], p, -1))
X saved_hosts = i;
X
X /* Add the hostname to the path being constructed */
X hostnames[saved_hosts++] = p;
X
X /* If the original path or the address is a fully qualified
X * hostname (domain info is included), then break here
X */
X if (p == addr || is_domain && domain_path)
X break;
X /* If we know that we call this host, break */
X for (i = 0; known_hosts && known_hosts[i]; i++)
X if (!lcase_strncmp(p, known_hosts[i], -1))
X break;
X if (known_hosts && known_hosts[i])
X break;
X }
X /* temporary holder for where we are in buffer (save address) */
X p2 = b;
X if (is_domain && domain_path && *domain_path)
X b += Strcpy(b, domain_path), *b++ = '!';
X while (saved_hosts-- > 0) {
X b += Strcpy(b, hostnames[saved_hosts]);
X *b++ = '!';
X }
X b += Strcpy(b, recipient);
X if (!strcmp(p2, tmp)) { /* if the same, address was unmodified */
X b = p2; /* reset offset in buf (b) to where we were (p2) */
X goto unmodified;
X }
X if (*name)
X b += strlen(sprintf(b, " (%s)", name));
X } else {
X char c;
unmodified:
X c = *end;
X *end = 0;
X b += Strcpy(b, start); /* copy the entire address with comments */
X *end = c;
X }
X if (b - buf > size) {
X wprint("Warning: address list truncated!\n");
X /* Use a very poor heuristic to find the last complete address */
X for (b = buf+size - 1; *b != ','; b--)
X ;
X wprint("Lost addresses: %s%s\n", b, end); /* end = not yet parsed */
X while (isspace(*b) || *b == ',')
X b--;
X break;
X }
X for (start = end; *start == ',' || isspace(*start); start++)
X ;
X if (!*start)
X break;
X *b++ = ',', *b++ = ' ', *b = '\0';
X }
X (void) strcpy(original, buf);
}
X
/*
X * rm_cmts_in_addr() removes the comment lines in addresses that result from
X * sendmail or other mailers which append the user's "real name" on the
X * from lines. See get_name_n_addr().
X */
rm_cmts_in_addr(str)
register char *str;
{
X char addr[BUFSIZ], buf[HDRSIZ], *start = str;
X register char *b = buf;
X
X *b = 0;
X do {
X if (!(str = get_name_n_addr(str, NULL, addr)))
X break;
X b += Strcpy(b, addr);
X while (*str == ',' || isspace(*str))
X str++;
X if (*str)
X *b++ = ',', *b++ = ' ', *b = '\0';
X } while (*str);
X for (b--; b > start && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
}
X
/*
X * take_me_off() is intended to search for the user's login name in an
X * address string and remove it. If "metoo" is set, return without change.
X * determine which addresses are the "user'"'s addresses by comparing them
X * against the host/path names in alternates. If the "*" is used, then
X * this matches the address against the user's current login and -any- path.
X *
X * Note that the alternates list is an array of addresses stored *reversed*!
X */
take_me_off(str)
char *str;
{
X int i = 0, rm_me;
X char tmp[256], addr[256], buf[HDRSIZ], *start = str;
X register char *p, *p2, *b = buf;
X
X if (!str || !*str)
X return;
X
X Debug("take_me_off()\n");
X *b = 0;
X do {
X rm_me = FALSE;
X /* get the first "address" and advance p to next addr (ignore name) */
X if (!(p = get_name_n_addr(str, NULL, tmp)))
X break; /* we've reached the end of the address list */
X /* see if user's login is in the address */
X if (!strcmp(login, tmp))
X rm_me = TRUE;
X else {
X int len;
X /* put address in !-format and store in "addr" */
X (void) bang_form(addr, tmp);
X (void) reverse(addr);
X for (i = 0; alternates && alternates[i] && !rm_me; i++) {
X if (alternates[i][0] == '*') {
X if (alternates[i][1] == '\0')
X p2 = reverse(strcpy(tmp, login));
X else
X p2 = reverse(strcpy(tmp, &alternates[i][1]));
X } else
X p2 = alternates[i];
X if (!lcase_strncmp(p2, addr, (len = strlen(p2))) &&
X (!addr[len] || addr[len] == '!')) {
X Debug("\t%s\n", reverse(addr));
X rm_me = TRUE;
X }
X }
X for (i = 0; !rm_me && ourname && ourname[i]; i++) {
X p2 = tmp + Strcpy(tmp, ourname[i]);
X *p2++ = '!';
X (void) strcpy(p2, login);
X (void) reverse(tmp);
X if (!lcase_strncmp(tmp, addr, (len = strlen(tmp))) &&
X (!addr[len] || addr[len] == '!')) {
X Debug("\t%s\n", reverse(addr));
X rm_me = TRUE;
X }
X }
X }
X /* The address is not the user's -- put it into the returned list */
X if (!rm_me) {
X char c = *p;
X *p = 0;
X b += Strcpy(b, str);
X *p = c;
X }
X while (*p == ',' || isspace(*p))
X p++;
X if (*p && !rm_me)
X *b++ = ',', *b++ = ' ', *b = '\0';
X } while (*(str = p));
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
}
X
/*
X * Place commas in between all addresses that don't already have
X * them. Addresses which use comments which are in parens or _not_
X * within angle brackets *must* already have commas around them or
X * you can't determine what is a comment and what is an address.
X */
fix_up_addr(str)
char *str;
{
X char buf[HDRSIZ], *start = str;
X register char c, *p, *b = buf;
X
X *b = 0;
X do {
X /* get_name returns a pointer to the next address */
X if (!(p = get_name_n_addr(str, NULL, NULL)))
X break;
X c = *p, *p = 0;
X if (strlen(str) + (b - buf) >= sizeof(buf) - 2) {
X /* wprint("Address too long! Lost address: \"%s\"\n", str); */
X *p = c;
X break;
X }
X for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
X *b = 0;
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X if (*p)
X *b++ = ',', *b++ = ' ', *b = '\0';
X } while (*(str = p));
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
}
X
/*
X * Remove redundant addresses.
X * Assume improve_uucp_paths, fix_up_addr or whatever have already been called.
X */
rm_redundant_addrs(to, cc)
char *to, *cc;
{
X char tmp[256], addr[256], buf[HDRSIZ];
X char **list; /* a list of addresses for comparison */
X int list_cnt = 0, l;
X register char c, *p, *b, *start;
X
X Debug("rm_redundant_addrs()\n");
X list = (char **) calloc(256, sizeof(char *));
X if (!list) {
X error("out of memory in rm_redundant_addrs");
X return;
X }
X start = to;
X b = buf, *b = 0;
X /* first do the To header */
X do {
X /* get_name returns a pointer to the next address */
X if (!(p = get_name_n_addr(to, NULL, tmp)))
X break;
X c = *p, *p = 0;
X (void) bang_form(addr, tmp);
X for (l = 0; l < list_cnt; l++)
X if (!lcase_strncmp(addr, list[l], -1))
X break;
X /* if l == list_cnt, we got a new address, store it and add to buf */
X if (l == list_cnt) {
X /* Don't overwrite buffer. */
X if (list_cnt < 256)
X list[list_cnt++] = savestr(addr);
X if (b > buf)
X *b++ = ',', *b++ = ' ', *b = '\0';
X for (b += Strcpy(b, to); b > buf && isspace(*(b-1)); b--)
X *b = 0;
X } else
X Debug("\t%s\n", tmp); /* already specified (removed from list) */
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X } while (*(to = p));
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
X b = buf, *b = 0;
X /* Now do the Cc header. If addr is listed in the To field, rm it in cc */
X start = cc;
X do {
X /* get_name returns a pointer to the next address */
X if (!(p = get_name_n_addr(cc, NULL, tmp)))
X break;
X c = *p, *p = 0;
X (void) bang_form(addr, tmp);
X for (l = 0; l < list_cnt; l++)
X if (!lcase_strncmp(addr, list[l], -1))
X break;
X if (l == list_cnt) {
X /* Don't overwrite buffer. */
X if (list_cnt < sizeof(list)/sizeof(char *))
X list[list_cnt++] = savestr(addr);
X if (b > buf)
X *b++ = ',', *b++ = ' ', *b = '\0';
X for (b += Strcpy(b, cc); b > buf && isspace(*(b-1)); b--)
X *b = 0;
X } else
X Debug("\t%s\n", tmp); /* already specified (removed from list) */
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X } while (*(cc = p));
X list[list_cnt] = NULL; /* for free_vec */
X free_vec(list);
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
}
X
/*
X * Get address and name from a string (str) which came from an address header
X * in a message or typed by the user. The string may contain one or more
X * well-formed addresses. Each must be separated by a comma.
X *
X * address, address, address
X * address (comment or name here)
X * comment or name <address>
X * "Comment, even those with comma's!" <address>
X * address (comma, (more parens), etc...)
X *
X * This does *not* handle cases like:
X * comment <address (comment)>
X *
X * find the *first* address here and return a pointer to the end of the
X * address (usually a comma). Return NULL on error: non-matching parens,
X * brackets, quotes...
X */
char *
get_name_n_addr(str, name, addr)
register char *str, *name, *addr;
{
X register char *p, *p2, *beg_addr = addr, *beg_name = name, c;
X
X if (addr)
X *addr = 0;
X if (name)
X *name = 0;
X if (!str || !*str)
X return NULL;
X
X while (isspace(*str))
X str++;
X
X /* first check to see if there's something to look for */
X if (!(p = any(str, ",(<\""))) {
X /* no comma or indication of a quote character. Find a space and
X * return that. If nothing, the entire string is a complete address
X */
X if (p = any(str, " \t"))
X c = *p, *p = 0;
X if (addr)
X (void) strcpy(addr, str);
X if (p)
X *p = c;
X return p? p : str + strlen(str);
X }
X
X /* comma terminated before any comment stuff. If so, check for whitespace
X * before-hand cuz it's possible that strings aren't comma separated yet
X * and they need to be.
X *
X * address address address, address
X * ^p <- p points here.
X * ^p2 <- should point here.
X */
X if (*p == ',') {
X c = *p, *p = 0;
X if (p2 = any(str, " \t"))
X *p = ',', c = *p2, p = p2, *p = 0;
X if (addr)
X (void) strcpy(addr, str);
X *p = c;
X return p;
X }
X
X /* starting to get hairy -- we found an angle bracket. This means that
X * everything outside of those brackets are comments until we find that
X * all important comma. A comment AFTER the <addr> :
X * <address> John Doe
X * can't call this function recursively or it'll think that "John Doe"
X * is a string with two legal address on it (each name being an address).
X */
X if (*p == '<') { /* note that "str" still points to comment stuff! */
X if (name && *str) {
X *p = 0;
X name += Strcpy(name, str);
X *p = '<';
X }
X if (!(p2 = index(p+1, '>'))) {
X wprint("Warning! Malformed address: \"%s\"\n", str);
X return NULL;
X }
X if (addr) {
X /* to support <addr (comment)> style addresses, add code here */
X *p2 = 0;
X skipspaces(1);
X addr += Strcpy(addr, p);
X while (addr > beg_addr && isspace(*(addr-1)))
X *--addr = 0;
X *p2 = '>';
X }
X /* take care of the case "... <addr> com (ment)" */
X {
X int p_cnt = 0; /* parenthesis counter */
X p = p2;
X /* don't recurse yet -- scan till null, comma or '<'(add to name) */
X for (p = p2; p[1] && (p_cnt || p[1] != ',' && p[1] != '<'); p++) {
X if (p[1] == '(')
X p_cnt++;
X else if (p[1] == ')')
X p_cnt--;
X if (name)
X *name++ = p[1];
X }
X if (p_cnt) {
X wprint("Warning! Malformed name: \"%s\"\n", name);
X return NULL;
X }
X }
X if (name && name > beg_name) {
X while (isspace(*(name-1)))
X --name;
X *name = 0;
X }
X }
X
X /* this is the worst -- now we have parentheses/quotes. These guys can
X * recurse pretty badly and contain commas within them.
X */
X if (*p == '(' || *p == '"') {
X char *start = p;
X int comment = 1;
X c = *p;
X /* "str" points to address while p points to comments */
X if (addr && *str) {
X *p = 0;
X while (isspace(*str))
X str++;
X addr += Strcpy(addr, str);
X while (addr > beg_addr && isspace(*(addr-1)))
X *--addr = 0;
X *p = c;
X }
X while (comment) {
X if (c == '"' && !(p = index(p+1, '"')) ||
X c == '(' && !(p = any(p+1, "()"))) {
X wprint("Warning! Malformed address: \"%s\"\n", str);
X return NULL;
X }
X if (*p == '(') /* loop again on parenthesis. quote ends loop */
X comment++;
X else
X comment--;
X }
X /* Something like ``Comment (Comment) <addr>''. In this case
X * the name should include both comment parts with the
X * parenthesis. We have to redo addr.
X */
X if ((p2 = any(p+1, "<,")) && *p2 == '<') {
X if (!(p = index(p2, '>'))) {
X wprint("Warning! Malformed address: \"%s\"\n", str);
X return NULL;
X }
X if (addr = beg_addr) { /* reassign addr and compare to null */
X c = *p; *p = 0;
X addr += Strcpy(addr, p2+1);
X while (addr > beg_addr && isspace(*(addr-1)))
X *--addr = 0;
X *p = c;
X }
X if (name) {
X c = *p2; *p2 = 0;
X name += Strcpy(name, str);
X while (name > beg_name && isspace(*(name-1)))
X *--name = 0;
X *p2 = c;
X }
X } else if (name && start[1]) {
X c = *p, *p = 0; /* c may be ')' instead of '(' now */
X name += Strcpy(name, start+1);
X while (name > beg_name && isspace(*(name-1)))
X *--name = 0;
X *p = c;
X }
X }
X skipspaces(1);
X /* this is so common, save time by returning now */
X if (!*p || *p == ',' || *p == '<')
X return p;
X return get_name_n_addr(p, name, addr);
}
X
/* takes string 's' which can be a name or list of names separated by
X * commas and checks to see if each is aliased to something else.
X * return address of the static buf.
X */
char *
alias_to_address(s)
register char *s;
{
X static char buf[HDRSIZ];
X register char *p, *p2, *tmp;
X char newbuf[HDRSIZ], c;
X static int recursive;
X
X if (!aliases)
X return strcpy(buf, s);
X if (!s || !*s)
X return NULL;
X if (!recursive) {
X bzero(buf, sizeof buf);
X p2 = buf; /* if we're starting all this, p2 starts at &buf[0] */
X } else
X p2 = buf+strlen(buf); /* else, pick up where we left off */
X
X if (++recursive == 30) {
X wprint("alias references too many addresses!\n");
X recursive = 0;
X return NULL;
X }
X do {
X char addr[256];
X if (!(p = get_name_n_addr(s, NULL, addr)))
X break;
X c = *p, *p = 0;
X
X /* On recursive calls, compare against the entire
X * previous expansion, not just the address part.
X */
X if (recursive > 1)
X (void) strcpy(addr, s);
X
X /* if this is an alias, recurse this routine to expand it out */
X if ((tmp = do_set(aliases, addr)) && *tmp) {
X if (!alias_to_address(strcpy(newbuf, tmp))) {
X *p = c;
X return NULL;
X } else
X p2 = buf+strlen(buf);
X /* Now, make sure the buffer doesn't overflow */
X } else if (strlen(s) + (p2-buf) + 2 > sizeof buf) { /* add ", " */
X wprint("address length too long.\n");
X recursive = 0;
X *p = c;
X return NULL;
X } else {
X /* append the new alias (or unchanged address) onto the buffer */
X p2 += Strcpy(p2, s);
X *p2++ = ',', *p2++ = ' ', *p2 = '\0';
X }
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X } while (*(s = p));
X if (recursive)
X recursive--;
X if (!recursive)
X *(p2-2) = 0; /* get rid of last ", " if end of recursion */
X return buf;
}
X
/*
X * Wrap addresses so that the headers don't exceed n chars (typically 80).
X */
char *
wrap_addrs(str, n)
char *str;
{
X char buf[HDRSIZ * 2], *start = str;
X register char *b = buf, *p, c, *line_start = buf;
X
X *b = 0;
X do {
X /* get_name returns a pointer to the next address */
X if (!(p = get_name_n_addr(str, NULL, NULL)))
X break;
X c = *p, *p = 0;
X if (b > buf) {
X *b++ = ',', *b++ = ' ', *b = '\0';
X if (b - line_start + strlen(str) + 8 /* \t = 8 */ >= n)
X *b++ = '\n', *b++ = '\t', line_start = b;
X }
X for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
X *b = 0;
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X } while (*(str = p));
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X return strcpy(start, buf);
}
SHAR_EOF
echo 'File addrs.c is complete' &&
chmod 0644 addrs.c ||
echo 'restore of addrs.c failed'
Wc_c="`wc -c < 'addrs.c'`"
test 33326 -eq "$Wc_c" ||
echo 'addrs.c: original size 33326, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= advanced.mushrc ==============
if test -f 'advanced.mushrc' -a X"$1" != X"-c"; then
echo 'x - skipping advanced.mushrc (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting advanced.mushrc (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'advanced.mushrc' &&
# advanced.mushrc
# by Bart Schaefer
# with special thanks to Phil Lapsley <phil at east.Berkeley.EDU>, who
# provided the original files on which this example is based. Most of
# Phil's stuff is still here -- just reorganized and updated to use
# mush 6.4 features that were unavailable when Phil did the originals.
#
# This file is intended to demonstrate helpful ways to use the
# .mushrc, not advanced mush commands.
X
# The variable $thisfolder is always set EXCEPT when the .mushrc file
# is read the first time. A test for non-existance of $thisfolder
# allows the same .mushrc file to be sourced repeatedly without
# redundant execution of the initialization commands.
#
if ! $?thisfolder
X # Ignore the usual stuff
X ignore received via message-id status priority
X # Hide folders in ~/.mail and save read mail in spool
X set folder=~/.mail hold
X # Remember a few commands, set up editors, act like a shell
X set history=20 editor=ex visual=vi unix
X # Prompt has folder name, message number, history number
X set prompt="%f %m (!) & "
X # Header summaries show name, date, and subject
X set hdr_format="%25n %-15d %27s"
X # Initialize the cmds below (see later comments)
X set first_time=1
X
X # These two commands are used for automated "bursting" of the spool
X # mailbox. This means that the messages are reorganized into new
X # folders to be read in a prearranged order. See comments below.
X #
X # n brings up the next folder, not the next message
X cmd n 'source ~/.mushrc'
X # N gets the next folder without "bursting"
X cmd N 'set first_time=0; source ~/.mushrc'
X
X # Delete messages by pattern-matching. Examples:
X # del f mailer-daemon Delete mail from mailer-daemon
X # del t mush-users Delete mail to mush-users
X cmd del 'pick -i -\!* | delete'
X # Forwarding
X cmd for 'mail -f'
X # Quick folder change
X cmd F 'folder'
X
X # Some useful aliases
X alias dheller 'The Mush God <argv at sun.com>'
X alias barts 'Archangel Mushael <schaefer at cse.ogi.edu>'
X
X # On init, don't source beyond this point
X exit
endif # End of init section -- read on startup only
X
# This part of the file handles "bursting". A burst is done when the
# n cmd is used the first time. This is most useful if you habitually
# have lots of mail when you first log in each morning; unwanted mail
# can be deleted, and other mail organized for you.
#
# The folders in this example bursting scheme are:
# mush-users anything to or cc'ed to mush-users
# stats daily stats
# root root mail other than daily stats
# Mail not falling into one of these categories is left in the system
# mailbox to be dealt with first.
#
if $first_time
X # Kill off some uucp garbage
X pick -i -s "file c.* delete" | delete
X pick -i -s "file .* can.t access" | delete
X pick -i -s "remote access to path/file denied" | delete
X # Nuke the boring usenet stuff
X pick -i -f usenet | pick -i -s "uucp map for" | delete
X pick -i -t usenet | pick -i -s "returned mail" | delete
X pick -i -t usenet | pick -i -s "automatic test echo" | delete
X pick -i -t "owner-post" | pick -i -s "unknown mailer" | delete
X pick -i -s "usenet disk space report" | delete
X pick -i -s "very old news articles" | delete
X pick -i -s "uucp map for" | delete
X # Wipe out some uninteresting daily stats
X pick -i -s "the maid was here." | delete
X pick -i -s "daily accounting" | delete
X pick -i -t netsurvey | delete
X # Get rid of these things for good. This isn't essential, but
X # avoids complexity in the later "pick" commands.
X update
X # Save anything "to" or "cc" to mush-users in that folder.
X pick -i -t mush-users | save +mush-users
X pick -i -h cc mush-users | save +mush-users
X # Also save interesting daily stat mail and generic root mail
X pick -i -f root | pick -i -s stats | save +stats
X pick -i -f root | pick -i -s report | save +stats
X pick -i -f uucp | pick -i -s report | save +stats
X pick -i -f root | pick -i -s summary | save +stats
X pick -i -f root | pick -i -s munge | save +stats
X pick -i -t root | save +root
X # Again, make the changes permanent. Saved mail gets deleted.
X # This won't work if you have $keepsave set.
X update
X
X # Make sure we don't burst again needlessly.
X set first_time=0
X
X # Stop sourcing here. Otherwise, we'd change folders without
X # handling the mail left in the system mailbox.
X exit
endif
X
# Finally, handle stepping through the folders one by one. This has been
# set up for sendmail, where the system mailbox is /usr/spool/mail/$USER,
# but could easily be modified for other mailers.
#
# $thisfolder:t returns the tail only of the folder name.
X
if $thisfolder:t == $USER
X folder +stats
X exit
endif
X
if $thisfolder:t == stats
X folder +mush-users
X exit
endif
X
if $thisfolder:t == mush-users
X folder +root
X exit
endif
X
# Default back to the system mailbox
folder %
X
# End of advanced.mushrc
SHAR_EOF
chmod 0644 advanced.mushrc ||
echo 'restore of advanced.mushrc failed'
Wc_c="`wc -c < 'advanced.mushrc'`"
test 4976 -eq "$Wc_c" ||
echo 'advanced.mushrc: original size 4976, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= bind.c ==============
if test -f 'bind.c' -a X"$1" != X"-c"; then
echo 'x - skipping bind.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting bind.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'bind.c' &&
/* bind.c */
X
#include "bindings.h"
#include "mush.h"
X
extern char *c_macro();
static un_bind();
X
struct cmd_map *cmd_map, *line_map, *bang_map;
X
/*
X * Bindings are added here in REVERSE of the order that
X * they will be displayed! Display order is based on a
X * guess about the frequency of use and (to a lesser
X * extent) how hard they are to remember.
X *
X * The user's own new bindings, if any, will be displayed
X * before any of these default bindings.
X */
init_bindings()
{
#ifdef CURSES
X /* Help gets displayed last */
X add_bind("?", C_HELP, NULL, &cmd_map);
X add_bind("V", C_VERSION, NULL, &cmd_map);
X
X /* Miscellaneous shell commands */
X add_bind("%", C_CHDIR, NULL, &cmd_map);
X add_bind("|", C_PRINT_MSG, NULL, &cmd_map);
X add_bind("!", C_SHELL_ESC, NULL, &cmd_map);
X add_bind(":", C_CURSES_ESC, NULL, &cmd_map);
X
X /* Mush customization commands */
X /* NOTE: No default C_MACRO bindings */
X add_bind(")", C_SAVEOPTS, NULL, &cmd_map);
X add_bind("(", C_SOURCE, NULL, &cmd_map);
X add_bind("&!", C_MAP_BANG, NULL, &cmd_map);
X add_bind("&:", C_MAP, NULL, &cmd_map);
X add_bind("&&", C_BIND_MACRO, NULL, &cmd_map);
X add_bind("v", C_VAR_SET, NULL, &cmd_map);
X add_bind("i", C_IGNORE, NULL, &cmd_map);
X add_bind("h", C_OWN_HDR, NULL, &cmd_map);
X add_bind("B", C_UNBIND, NULL, &cmd_map);
X add_bind("b", C_BIND, NULL, &cmd_map);
X add_bind("a", C_ALIAS, NULL, &cmd_map);
X
X /* Display modification commands */
X add_bind("\022", C_REVERSE, NULL, &cmd_map); /* ^R */
X add_bind("\014", C_REDRAW, NULL, &cmd_map); /* ^L */
X add_bind("Z", C_PREV_SCREEN, NULL, &cmd_map);
X add_bind("z", C_NEXT_SCREEN, NULL, &cmd_map);
X
X /* Searching and sorting commands */
X add_bind("\016", C_CONT_SEARCH, NULL, &cmd_map); /* ^N */
X add_bind("\037", C_PREV_SEARCH, NULL, &cmd_map); /* ^/ */
X add_bind("/", C_NEXT_SEARCH, NULL, &cmd_map);
X add_bind("O", C_REV_SORT, NULL, &cmd_map);
X add_bind("o", C_SORT, NULL, &cmd_map);
X
X /* Ways to get out */
X add_bind("X", C_EXIT_HARD, NULL, &cmd_map);
X add_bind("x", C_EXIT, NULL, &cmd_map);
X add_bind("Q", C_QUIT_HARD, NULL, &cmd_map);
X add_bind("q", C_QUIT, NULL, &cmd_map);
X
X /* Folder modification commands */
X add_bind("\025", C_UPDATE, NULL, &cmd_map); /* ^U */
X add_bind("\020", C_PRESERVE, NULL, &cmd_map); /* ^P */
X add_bind("*", C_MARK_MSG, NULL, &cmd_map);
X add_bind("W", C_WRITE_LIST, NULL, &cmd_map);
X add_bind("w", C_WRITE_MSG, NULL, &cmd_map);
X add_bind("U", C_UNDEL_LIST, NULL, &cmd_map);
X add_bind("u", C_UNDEL_MSG, NULL, &cmd_map);
X add_bind("S", C_SAVE_LIST, NULL, &cmd_map);
X add_bind("s", C_SAVE_MSG, NULL, &cmd_map);
X add_bind("f", C_FOLDER, NULL, &cmd_map);
X add_bind("D", C_DELETE_LIST, NULL, &cmd_map);
X add_bind("d", C_DELETE_MSG, NULL, &cmd_map);
X add_bind("C", C_COPY_LIST, NULL, &cmd_map);
X add_bind("c", C_COPY_MSG, NULL, &cmd_map);
X
X /* Cursor movement and message selection */
X add_bind("g", C_GOTO_MSG, NULL, &cmd_map);
X add_bind("}", C_BOTTOM_PAGE, NULL, &cmd_map);
X add_bind("{", C_TOP_PAGE, NULL, &cmd_map);
X add_bind("$", C_LAST_MSG, NULL, &cmd_map);
X add_bind("^", C_FIRST_MSG, NULL, &cmd_map);
X add_bind("\013",C_PREV_MSG, NULL, &cmd_map); /* ^K */
X add_bind("\012", C_NEXT_MSG, NULL, &cmd_map); /* ^J */
X add_bind("-",C_PREV_MSG, NULL, &cmd_map);
X add_bind("+",C_NEXT_MSG, NULL, &cmd_map);
X add_bind("K", C_PREV_MSG, NULL, &cmd_map);
X add_bind("k", C_PREV_MSG, NULL, &cmd_map);
X add_bind("J", C_NEXT_MSG, NULL, &cmd_map);
X add_bind("j", C_NEXT_MSG, NULL, &cmd_map);
X
X /* Mail-sending commands */
X add_bind("R", C_REPLY_ALL, NULL, &cmd_map);
X add_bind("r", C_REPLY_SENDER, NULL, &cmd_map);
X add_bind("M", C_MAIL_FLAGS, NULL, &cmd_map);
X add_bind("m", C_MAIL, NULL, &cmd_map);
X
X /* Mail-reading commands */
X add_bind(".", C_DISPLAY_MSG, NULL, &cmd_map);
X add_bind("T", C_TOP_MSG, NULL, &cmd_map);
X add_bind("t", C_DISPLAY_MSG, NULL, &cmd_map);
X add_bind("p", C_DISPLAY_MSG, NULL, &cmd_map);
X add_bind("n", C_DISPLAY_NEXT, NULL, &cmd_map);
X
#endif /* CURSES */
}
X
/* Bindable function names.
X * Most of these can't be used if CURSES is not defined,
X * but help and lookups get confused if they aren't all here.
X */
struct cmd_map map_func_names[] = {
X /* These MUST be in numerical order; see bindings.h */
X { C_NULL, "no-op", NULL, NULL_MAP },
X { C_GOTO_MSG, "goto-msg", NULL, NULL_MAP },
X { C_WRITE_LIST, "write-list", NULL, NULL_MAP },
X { C_WRITE_MSG, "write", NULL, NULL_MAP },
X { C_SAVE_LIST, "save-list", NULL, NULL_MAP },
X { C_SAVE_MSG, "save", NULL, NULL_MAP },
X { C_COPY_LIST, "copy-list", NULL, NULL_MAP },
X { C_COPY_MSG, "copy", NULL, NULL_MAP },
X { C_DELETE_LIST, "delete-list", NULL, NULL_MAP },
X { C_DELETE_MSG, "delete", NULL, NULL_MAP },
X { C_UNDEL_LIST, "undelete-list", NULL, NULL_MAP },
X { C_UNDEL_MSG, "undelete", NULL, NULL_MAP },
X { C_REDRAW, "redraw", NULL, NULL_MAP },
X { C_REVERSE, "reverse-video", NULL, NULL_MAP },
X { C_NEXT_MSG, "next-msg", NULL, NULL_MAP },
X { C_PREV_MSG, "back-msg", NULL, NULL_MAP },
X { C_FIRST_MSG, "first-msg", NULL, NULL_MAP },
X { C_LAST_MSG, "last-msg", NULL, NULL_MAP },
X { C_TOP_PAGE, "top-page", NULL, NULL_MAP },
X { C_BOTTOM_PAGE, "bottom-page", NULL, NULL_MAP },
X { C_NEXT_SCREEN, "screen-next", NULL, NULL_MAP },
X { C_PREV_SCREEN, "screen-back", NULL, NULL_MAP },
X { C_SOURCE, "source", NULL, NULL_MAP },
X { C_SAVEOPTS, "saveopts", NULL, NULL_MAP },
X { C_NEXT_SEARCH, "search-next", NULL, NULL_MAP },
X { C_PREV_SEARCH, "search-back", NULL, NULL_MAP },
X { C_CONT_SEARCH, "search-again", NULL, NULL_MAP },
X { C_PRESERVE, "preserve", NULL, NULL_MAP },
X { C_REV_SORT, "sort-reverse", NULL, NULL_MAP },
X { C_SORT, "sort", NULL, NULL_MAP },
X { C_QUIT_HARD, "quit!", NULL, NULL_MAP },
X { C_QUIT, "quit", NULL, NULL_MAP },
X { C_EXIT_HARD, "exit!", NULL, NULL_MAP },
X { C_EXIT, "exit", NULL, NULL_MAP },
X { C_UPDATE, "update", NULL, NULL_MAP },
X { C_FOLDER, "folder", NULL, NULL_MAP },
X { C_SHELL_ESC, "shell-escape", NULL, NULL_MAP },
X { C_CURSES_ESC, "line-mode", NULL, NULL_MAP },
X { C_PRINT_MSG, "lpr", NULL, NULL_MAP },
X { C_CHDIR, "chdir", NULL, NULL_MAP },
X { C_VAR_SET, "variable", NULL, NULL_MAP },
X { C_IGNORE, "ignore", NULL, NULL_MAP },
X { C_ALIAS, "alias", NULL, NULL_MAP },
X { C_OWN_HDR, "my-hdrs", NULL, NULL_MAP },
X { C_VERSION, "version", NULL, NULL_MAP },
X { C_MAIL_FLAGS, "mail-flags", NULL, NULL_MAP },
X { C_MAIL, "mail", NULL, NULL_MAP },
X { C_REPLY_ALL, "reply-all", NULL, NULL_MAP },
X { C_REPLY_SENDER, "reply", NULL, NULL_MAP },
X { C_DISPLAY_NEXT, "display-next", NULL, NULL_MAP },
X { C_DISPLAY_MSG, "display", NULL, NULL_MAP },
X { C_TOP_MSG, "top", NULL, NULL_MAP },
X { C_BIND_MACRO, "bind-macro", NULL, NULL_MAP },
X { C_BIND, "bind", NULL, NULL_MAP },
X { C_UNBIND, "unbind", NULL, NULL_MAP },
X { C_MAP_BANG, "map!", NULL, NULL_MAP },
X { C_MAP, "map", NULL, NULL_MAP },
X { C_MACRO, "macro", NULL, NULL_MAP },
X { C_MARK_MSG, "mark", NULL, NULL_MAP },
X /* C_HELP Must be the last one! */
X { C_HELP, "help", NULL, NULL_MAP }
};
X
#ifdef CURSES
X
/*
X * getcmd() is called from curses mode only. It waits for char input from
X * the user via m_getchar() (which means that a macro could provide input)
X * and then compares the chars input against the "bind"ings set up by the
X * user (or the defaults). For example, 'j' could bind to "next msg" which
X * is interpreted by the big switch statement in curses_command() (curses.c).
X * getcmd() returns the int-value of the curses command the input is "bound"
X * to. If the input is unrecognized, C_NULL is returned (curses_command()
X * might require some cleanup, so this is valid, too).
X *
X * Since the input could originate from a macro rather than the terminal,
X * check to see if this is the case and search for a '[' char which indicates
X * that there is a curses command or other "long" command to be executed.
X */
getcmd()
{
X char buf[MAX_BIND_LEN * 3];
X register int c, m, match;
X register char *p = buf;
X register struct cmd_map *list;
X
X bzero(buf, MAX_BIND_LEN);
X active_cmd = NULL_MAP;
X c = m_getchar();
X /* If user did job control (^Z), then the interrupt flag will be
X * set. Be sure it's unset before continuing.
X */
X turnoff(glob_flags, WAS_INTR);
X if (isdigit(c)) {
X buf[0] = c;
X buf[1] = '\0';
X Ungetstr(buf); /* So mac_flush can clear on error */
X return C_GOTO_MSG;
X }
X for (;;) {
X if (ison(glob_flags, IN_MACRO) && c == MAC_LONG_CMD)
X return long_mac_cmd(c, TRUE);
X else
X *p++ = c;
X m = 0;
X for (list = cmd_map; list; list = list->m_next) {
X if ((match = prefix(buf, list->m_str)) == MATCH) {
X if (debug)
X print("\"%s\" ",
X ctrl_strcpy(buf,
X map_func_names[list->m_cmd].m_str,
X TRUE));
X if (list->m_cmd == C_MACRO) {
X curs_macro(list->x_str);
X return getcmd();
X }
X active_cmd = list;
X return (int)list->m_cmd;
X } else if (match != NO_MATCH)
X m++;
X }
X if (m == 0) {
X if (debug) {
X char tmp[sizeof buf];
X print("No binding for \"%s\" found.",
X ctrl_strcpy(tmp, buf, TRUE));
X }
X return C_NULL;
X }
X c = m_getchar();
X }
}
X
#endif /* CURSES */
X
/*
X * bind_it() is used to set or unset bind, map and map! settings.
X * bind is used to accelerate curses commands by mapping key sequences
X * to curses commands. map is used to accelerate command mode keysequences
X * by simulating stdin. map! is the same, but used when in compose mode.
X *
X * bind_it() doesn't touch messages; return -1 for curses mode.
X * return -2 to have curses command set CNTD_CMD to prevent screen refresh
X * to allow user to read output in case of multiple lines.
X *
X * Since this routine deals with a lot of binding and unbinding of things
X * like line-mode "map"s and is interactive (calls Getstr()), be very careful
X * not to allow expansions during interaction.
X */
bind_it(len, argv)
char **argv;
{
X char string[MAX_BIND_LEN], buf[256], *name = NULL;
X char *rawstr; /* raw format of string (ptr to string if no argv avail) */
X char ascii[MAX_BIND_LEN*2]; /* printable ascii version of string */
X register int x;
X SIGRET (*oldint)(), (*oldquit)();
X struct cmd_map **map_list;
X int unbind = (argv && **argv == 'u');
X int map = 0, is_bind_macro = 0;
X int ret = 0 - iscurses; /* return value */
X
X if (argv && !strcmp(name = *argv, "bind-macro"))
X is_bind_macro++;
X
X if (map = (argv && (!strcmp(name, "map!") || !strcmp(name, "unmap!"))))
X map_list = &bang_map;
X else if (map = (argv && (!strcmp(name, "map") || !strcmp(name, "unmap"))))
X map_list = &line_map;
X else
X map_list = &cmd_map;
X
X if (argv && *++argv && !strcmp(*argv, "-?"))
X /* Subtract ret and iscurses to signal output */
X return help(0, unbind? name+2 : name, cmd_help) - ret - iscurses;
X
X if (iscurses)
X on_intr();
X
X if (unbind) {
X if (!*argv) {
X char savec = complete;
X complete = 0;
X print("%s what? ", name);
X len = Getstr(buf, sizeof buf, 0);
X complete = savec;
X if (len <= 0) {
X if (iscurses)
X off_intr();
X return -1;
X }
X rawstr = m_xlate(buf);
X } else
X rawstr = m_xlate(*argv);
X if (!un_bind(rawstr, map_list)) {
X (void) ctrl_strcpy(ascii, rawstr, TRUE);
X print("\"%s\" isn't bound to a command.\n", ascii);
X }
X if (iscurses)
X off_intr();
X return ret;
X }
X if (argv && *argv) {
X rawstr = m_xlate(*argv);
X (void) ctrl_strcpy(ascii, rawstr, TRUE);
X if (!*++argv) {
X /*
X * determine whether "argv" references a "map" or a "bind"
X */
X int binding = c_bind(rawstr, *map_list);
X if (binding == C_MACRO) {
X char *mapping = c_macro(NULL, rawstr, *map_list);
X if (mapping) {
X print("\"%s\" is mapped to ", ascii);
X print_more("\"%s\".\n",
X ctrl_strcpy(buf, mapping, FALSE));
X } else
X print("\"%s\" isn't mapped.\n", ascii);
X } else if (binding)
X print("\"%s\" is %s to \"%s\".\n", ascii,
X map? "mapped" : "bound", map_func_names[binding].m_str);
X else if (map)
X print("\"%s\" isn't mapped.\n", ascii);
X else
X print("\"%s\" isn't bound to a command.\n", ascii);
X if (iscurses)
X off_intr();
X return ret;
X }
X } else {
X char savec = complete;
X complete = 0;
X print("%s [<CR>=all, -?=help]: ", name);
X len = Getstr(string, MAX_BIND_LEN-1, 0);
X complete = savec;
X if (len == 0) {
X int add_to_ret = iscurses;
#ifdef CURSES
X if (iscurses)
X move(LINES-1, 0), refresh();
#endif
X if (map || is_bind_macro)
X add_to_ret = !c_macro(name, NULL, *map_list);
X else
X add_to_ret = !c_bind(NULL, *map_list);
X if (iscurses)
X off_intr();
X /* signal CTND_CMD if there was output */
X return ret - add_to_ret;
X }
X if (len < 0) {
X if (iscurses)
X off_intr();
X return ret;
X }
X rawstr = m_xlate(string);
X (void) ctrl_strcpy(ascii, rawstr, TRUE);
X }
X /* if a binding was given on the command line */
X if (argv && *argv && !map)
X if (is_bind_macro)
X (void) strcpy(buf, "macro");
X else
X (void) strcpy(buf, *argv++);
X else {
X /* at this point, "rawstr" and "ascii" should both be set */
X int binding;
X
X if (!strcmp(ascii, "-?")) {
X if (iscurses)
X clr_bot_line();
X ret -= help(0, name, cmd_help);
X if (iscurses)
X off_intr();
X /* Subtract iscurses to signal CNTD_CMD */
X return ret - iscurses;
X }
X
X if (!map && !is_bind_macro) {
X binding = c_bind(rawstr, *map_list);
X
X for (len = 0; len == 0; ) {
X print("\"%s\" = <%s>: New binding [<CR> for list]: ",
X ascii, (binding? map_func_names[binding].m_str : "unset"));
X len = Getstr(buf, sizeof buf, 0);
X if (iscurses)
X clr_bot_line();
X /* strip any trailing whitespace */
X if (len > 0)
X len = no_newln(buf) - buf;
X if (len == 0) {
X (void) do_pager(NULL, TRUE);
X if (iscurses)
X putchar('\n');
X for (x = 1; x <= C_HELP; x++) {
X if (!(x % 4))
X if (do_pager("\n", FALSE) == EOF)
X break;
X (void) do_pager(sprintf(buf, "%-15.15s ",
X map_func_names[x].m_str), FALSE);
X }
X (void) do_pager("\n", FALSE);
X (void) do_pager(NULL, FALSE);
X ret -= iscurses;
X }
X }
X } else /* map */
X (void) strcpy(buf, "macro"), len = 5;
X /* if list was printed, ret < -1 -- tells CNTD_CMD to be set and
X * prevents screen from being refreshed (lets user read output
X */
X if (len == -1) {
X if (iscurses)
X off_intr();
X return ret;
X }
X }
X for (x = 1; x <= C_HELP; x++) {
X if (prefix(buf, map_func_names[x].m_str) == MATCH) {
X int add_to_ret;
X if (debug)
X print("\"%s\" will execute \"%s\".\n", ascii, buf);
X if (map_func_names[x].m_cmd == C_MACRO) {
X if (argv && *argv) {
X (void) argv_to_string(buf, argv);
X (void) m_xlate(buf); /* Convert buf to raw chars */
X add_to_ret =
X do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
X } else {
X char exp[MAX_MACRO_LEN*2]; /* printable expansion */
X char *mapping = c_macro(NULL, rawstr, *map_list);
X
X if (mapping)
X (void) ctrl_strcpy(exp, mapping, TRUE);
X print("\"%s\" = <%s>", ascii, mapping ? exp : "unset");
X putchar('\n'), print("New macro: ");
X ret -= iscurses; /* To signal screen messed up */
X /* we are done with buf, so we can trash over it */
X len = Getstr(buf, MAX_MACRO_LEN, 0);
X if (len > 0) {
X if (iscurses)
X clr_bot_line();
X (void) m_xlate(buf); /* Convert buf to raw chars */
X add_to_ret =
X do_bind(rawstr, C_MACRO, buf, map_list);
X if (debug) {
X (void) ctrl_strcpy(exp, buf, TRUE);
X print("\"%s\" will execute \"%s\".\n", ascii, exp);
X }
X } else if (len < 0) {
X if (iscurses)
X off_intr();
X return ret;
X } else
X print("Can't bind to null macro"), putchar('\n');
X }
X } else /* not a macro */ {
X (void) argv_to_string(buf, argv);
X add_to_ret =
X do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
X }
X /* if do_bind had no errors, it returned -1. If we already
X * messed up the screen, then ret is less than -1. return the
X * lesser of the two to make sure that CNTD_CMD gets set right
X */
X if (iscurses)
X off_intr();
X return min(add_to_ret, ret);
X }
X }
X print("\"%s\": Unknown function.\n", buf);
X if (iscurses)
X off_intr();
X return ret;
}
X
/*
X * print current key to command bindings if "str" is NULL.
X * else return the integer "m_cmd" which the str is bound to.
X */
c_bind(str, opts)
register char *str;
register struct cmd_map *opts;
{
X register int incurses = iscurses;
X
X if (!str) {
X if (!opts) {
X print("No command bindings.\n");
X return C_ERROR;
X }
X if (incurses)
X clr_bot_line(), iscurses = FALSE;
X (void) do_pager(NULL, TRUE);
X (void) do_pager("Current key to command bindings:\n", FALSE);
X (void) do_pager("\n", FALSE);
X }
X
X for (; opts; opts = opts->m_next) {
X char buf[BUFSIZ], buf2[MAX_BIND_LEN], exp[MAX_MACRO_LEN*2], *xp;
X if (!str) {
X (void) ctrl_strcpy(buf2, opts->m_str, FALSE);
X if ((xp = opts->x_str) && opts->m_cmd == C_MACRO)
X xp = ctrl_strcpy(exp, opts->x_str, TRUE);
X if (do_pager(sprintf(buf, "%s\t%-15.15s %s\n",
X buf2, map_func_names[opts->m_cmd].m_str,
X xp? xp : ""),
X FALSE) == EOF)
X break;
X } else
X if (strcmp(str, opts->m_str))
X continue;
X else
X return opts->m_cmd;
X }
X
X iscurses = incurses;
X if (!str)
X (void) do_pager(NULL, FALSE);
X return C_NULL;
}
X
/*
X * Doesn't touch messages, but changes macros: return -1.
X * Error output causes return < -1.
X * args is currently the execute string of a macro mapping, but may be
X * used in the future as an argument string for any curses command.
X */
do_bind(str, func, args, map_list)
register char *str, *args;
struct cmd_map **map_list;
long func;
{
X register int ret = -1;
X register struct cmd_map *list;
X int match;
X
X if (func == C_MACRO && !check_mac_bindings(args))
X --ret;
X (void) un_bind(str, map_list);
X for (list = *map_list; list; list = list->m_next)
X if ((match = prefix(str, list->m_str)) != NO_MATCH) {
X ret--;
X switch (match) {
X case MATCH:
X puts("Something impossible just happened.");
X when A_PREFIX_B:
X wprint("Warning: \"%s\" prefixes \"%s\" (%s)\n", str,
X list->m_str, map_func_names[list->m_cmd].m_str);
X when B_PREFIX_A:
X wprint("Warning: \"%s\" (%s) prefixes: \"%s\"\n",
X list->m_str, map_func_names[list->m_cmd].m_str, str);
X }
X }
X add_bind(str, func, args, map_list);
X /* errors decrement ret. If ret returns less than -1, CNTD_CMD is set
X * and no redrawing is done so user can see the warning signs
X */
X return ret;
}
X
/*
X * add a binding to a list. This may include "map"s or other mappings since
X * the map_list argument can control that. The "func" is an int defined in
X * bindings.h ... the "str" passed is the string the user would have to type
X * to get the macro/map/binding expanded. This must in in raw format: no
X * \n's to mean \015. Convert first using m_xlate().
X */
add_bind(str, func, args, map_list)
register char *str, *args;
struct cmd_map **map_list;
long func;
{
X register struct cmd_map *tmp;
X
X if (!str || !*str)
X return;
X
X /* now make a new option struct and set fields */
X if (!(tmp = (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
X error("calloc");
X return;
X }
X tmp->m_next = *map_list;
X *map_list = tmp;
X
X tmp->m_str = savestr(str);
X tmp->m_cmd = func; /* strdup handles the NULL case */
X if (args && *args)
X tmp->x_str = savestr(args);
X else
X tmp->x_str = NULL;
}
X
static
un_bind(p, map_list)
register char *p;
struct cmd_map **map_list;
{
X register struct cmd_map *list = *map_list, *tmp;
X
X if (!list || !*list->m_str || !p || !*p)
X return 0;
X
X if (!strcmp(p, (*map_list)->m_str)) {
X *map_list = (*map_list)->m_next;
X xfree (list->m_str);
X if (list->x_str)
X xfree (list->x_str);
X xfree((char *)list);
X return 1;
X }
X for ( ; list->m_next; list = list->m_next)
X if (!strcmp(p, list->m_next->m_str)) {
X tmp = list->m_next;
X list->m_next = list->m_next->m_next;
X xfree (tmp->m_str);
X if (tmp->x_str)
X xfree (tmp->x_str);
X xfree ((char *)tmp);
X return 1;
X }
X return 0;
}
X
prefix(a, b)
register char *a, *b;
{
X if (!a || !b)
X return NO_MATCH;
X
X while (*a && *b && *a == *b)
X a++, b++;
X if (!*a && !*b)
X return MATCH;
X if (!*a && *b)
X return A_PREFIX_B;
X if (*a && !*b)
X return B_PREFIX_A;
X return NO_MATCH;
}
SHAR_EOF
chmod 0644 bind.c ||
echo 'restore of bind.c failed'
Wc_c="`wc -c < 'bind.c'`"
test 20663 -eq "$Wc_c" ||
echo 'bind.c: original size 20663, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= bindings.h ==============
if test -f 'bindings.h' -a X"$1" != X"-c"; then
echo 'x - skipping bindings.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting bindings.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'bindings.h' &&
/* bindings.h -- command bindings */
X
#define MAX_BIND_LEN 20 /* max length a string can be to bind to a command */
#define MAX_MACRO_LEN 256 /* max length of a macro bound to a command */
X
/* to see if a key sequence matches, prefixes or misses a set binding */
#define NO_MATCH 0
#define MATCH 1
#define A_PREFIX_B 2
#define B_PREFIX_A 3
X
/*
X * Constants to define curses mode functions.
X */
#ifdef NULL_MAP
#undef NULL_MAP
#endif /* NULL_MAP */
#define NULL_MAP (struct cmd_map *)0
X
#define C_ERROR (-1L)
#define C_NULL 0L
#define C_GOTO_MSG 1L
#define C_WRITE_LIST 2L
#define C_WRITE_MSG 3L
#define C_SAVE_LIST 4L
#define C_SAVE_MSG 5L
#define C_COPY_LIST 6L
#define C_COPY_MSG 7L
#define C_DELETE_LIST 8L
#define C_DELETE_MSG 9L
#define C_UNDEL_LIST 10L
#define C_UNDEL_MSG 11L
#define C_REDRAW 12L
#define C_REVERSE 13L
#define C_NEXT_MSG 14L
SHAR_EOF
true || echo 'restore of bindings.h failed'
fi
echo 'End of part 3'
echo 'File bindings.h is continued in part 4'
echo 4 > _shar_seq_.tmp
exit 0
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent at sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent at uunet.uu.net.
More information about the Comp.sources.misc
mailing list