[comp.sources.misc] v18i066: mush - Mail User's Shell, Part09/22

argv@zipcode.com (Dan Heller) (04/22/91)

Submitted-by: Dan Heller <argv@zipcode.com>
Posting-number: Volume 18, Issue 66
Archive-name: mush/part09
Supersedes: mush: Volume 12, Issue 28-47

#!/bin/sh
# this is Part.09 (part 9 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file glob.c continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 9; 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 glob.c'
else
echo 'x - continuing file glob.c'
sed 's/^X//' << 'SHAR_EOF' >> 'glob.c' &&
X		if (!*pat || *pat == ']' || *pat == '\\' && !*++pat) {
X		    done = TRUE;
X		    break;
X		}
X		/* Check for a range. */
X		if (*(pat + 1) == '-') {
X		    char c = *pat++;
X		    /* We don't handle open-ended ranges. */
X		    if (*++pat == ']' || *pat == '\\' && !*++pat) {
X			done = TRUE;
X			break;
X		    }
X		    if (*str < c || *str > *pat) {
X			pat++;
X			goto repeat;
X		    }
X		} else if (*pat != *str) {
X		    pat++;
X		    goto repeat;
X		}
X		/*
X		 * We matched either the range or a literal member of
X		 * the set.  Skip to the end of the set.
X		 */
X		pat++;
X		while (*pat && *pat != ']')
X		    if (*pat++ == '\\' && *pat)
X			pat++;
X		/*
X		 * If no pattern remains, the set was never closed,
X		 * so don't increment.  This will cause a FALSE return.
X		 */
X		if (*pat) {
X		    pat++;
X		    str++;
X		}
X		break;
X	    case '?':	/* Match any one character */
X		str++;
X		break;
X	    case '{':	/* } Match any of a set of patterns */
X		return sglob(str, pat - 1, TRPL_NULL);
X		break;
X	    default:
X		done = TRUE;
X	}
X    }
X    while (*pat == '*')
X	pat++;
X    return ((*str == '\0') && (*pat == '\0'));
}
X
/*
X * Match a pattern set {s1,s2,...} followed by any other pattern.
X * Pattern sets and other patterns may nest arbitrarily.
X *
X * If "mat" is not a null pointer, a vector of possible expansions
X * is generated and placed in *mat; otherwise, the expansions are
X * matched against str and a truth value is returned ("/" is NOT
X * treated as a directory separator in this case).  NOTE: The vector
X * of expansions may still contain nested pattern sets, which must
X * be expanded separately.  See sxp().
X *
X * Currently allows at most 256 alternatives per set.  Enough? :-)
X */
static
sglob(str, pat, mat)
char *str, *pat, ***mat;
{
X    char *p, *newpat[256], *oldpat[256], buf[MAXPATHLEN], *b = buf;
X    int copy = 1, nest = 0, i = 0, ret = 0;
X
X    if (!pat)
X	return FALSE;
X
X    while (*pat) {
X	if (copy)
X	    if (*pat != '{') /*}*/ {
X		if (*pat == '\\' && pat[1])
X		    *b++ = *pat++;
X		*b++ = *pat++;
X		continue;
X	    } else {
X		copy = 0;
X		pat++;
X	    }
X	p = pat;
X	while (*pat && (nest || *pat != ',' && /*{*/ *pat != '}')) {
X	    if (*pat == '\\')
X		pat++;
X	    else if (*pat == '{')
X		nest++;
X	    else if (*pat == '}')
X		nest--;
X	    if (*pat)
X		pat++;
X	}
X	if (*pat) {
X	    oldpat[i] = pat;
X	    newpat[i++] = p;
X	    if (*pat != ',') {
X		*pat++ = 0;
X		break;
X	    } else
X		*pat++ = 0;
X	}
X    }
X    oldpat[i] = NULL;
X    if (i > 0 && mat) {
X	*mat = (char **)malloc((unsigned)((i + 1) * sizeof(char *)));
X	if (*mat)
X	    (*mat)[i] = NULL;
X	else
X	    return -1;
X	ret = i;
X    }
X    while (!mat && i-- > 0)
X	if (ret = glob2(str, newpat[i], pat))
X	    break;
X    for (i = 0; oldpat[i]; i++) {
X	if (mat && *mat) {
X	    (void) sprintf(b, "%s%s", newpat[i], pat);
X	    (*mat)[i] = savestr(buf);
X	}
X	if (oldpat[i + 1])
X	    oldpat[i][0] = ',';
X	else
X	    oldpat[i][0] = /*{*/ '}';
X    }
X    if (ret == 0 && b > buf && mat) {
X	*b = 0;
X	ret = ((*mat = unitv(buf)) ? 1 : -1);
X    }
X    return ret;
}
X
/*
X * Pre-expand pattern set notations so sets containing "/" separators
X * can be globbed successfully.  Returns the number of expansions.
X */
sxp(pat, exp)
char *pat, ***exp;
{
X    char **t1 = DUBL_NULL, **t2;
X    int n, new, cnt = 0;
X
X    if ((n = sglob(NULL, pat, &t1)) < 2) {
X	*exp = t1;
X	return n;
X    }
X    *exp = DUBL_NULL;
X    while (n-- && cnt >= 0) {
X	new = sxp(t1[n], &t2);
X	cnt = catv(cnt, exp, new, t2);
X    }
X    xfree(t1);
X    return cnt;
}
X
/*
X * Generate the "glob difference" of two vectors (*argvp and patv).
X * The "glob difference" means to remove all strings from argv that
X * match any of the glob patterns in patv.
X *
X * Returns the number of strings remaining in *argvp.  The strings "removed"
X * from argv are actually left at the end of *argvp, so they can still be
X * accessed; their number will of course be argc - (returned value).
X */
gdiffv(argc, argvp, patc, patv)
int argc, patc;
char ***argvp, **patv;
{
X    char **argv, *t;
X    int ac, pc, oldac = argc;
X
X    if (argc < 1 || patc < 1 || !patv || !*patv)
X	return argc;
X    if (!argvp || !(argv = *argvp) || !*argv)
X	return -1;
X    for (ac = 0; ac < argc && argv[ac]; ac++) {
X	for (pc = 0; ac < argc && pc < patc && patv[pc]; pc++) {
X	    /*
X	     * We shouldn't cross '/' characters, so test
X	     * only the "tail" of each element of argv.
X	     */
X	    if (!(t = rindex(argv[ac], '/')))
X	        t = argv[ac];
X	    if (glob(t, patv[pc])) {
X		/* Move matches to the end and reduce argc */
X		t = argv[ac];
X		argv[ac] = argv[--argc];
X		argv[argc] = t;
X		/* Start patterns over on the new string */
X		pc = -1; /* It'll be incremented to 0 */
X	    }
X	}
X    }
X    /*
X     * Sort the two parts of the argv.  uniqcmp() works here only if
X     * there already are no duplicates, but we assume that for now.
X     */
X    if (argc)
X	qsort((char *)argv, argc, sizeof(char *), uniqcmp);
X    if (oldac > argc)
X	qsort((char *)&argv[argc], oldac - argc, sizeof(char *), uniqcmp);
X    return argc;
}
X
/*
X * Generate the longest common prefix from all strings in a vector
X * If "skip" is nonzero, that many chars are assumed to be in common
X * and are not tested.  WARNING: skip must be <= than the length of
X * the shortest string in the vector!  Safest to call with skip = 0.
X *
X * Returns the length of the longest common prefix.
X */
lcprefix(vec, skip)
char **vec;
int skip;
{
X    char c, **v;
X    int done = FALSE;
X
X    if (!vec || !*vec || skip < 0)
X	return 0;
X    do {
X	for (v = vec + 1, c = vec[0][skip]; c && *v; v++)
X	    if (v[0][skip] != c) {
X		done = TRUE;
X		break;
X	    }
X    } while (!done && c && ++skip);
X    return skip;
}
X
#define MAXCOLS 8	/* Max number of columns of words to make */
#define MINWIDTH 10	/* Minimum width of each column of words */
#ifdef CURSES
#define MAXWIDTH (iscurses? COLS : 80)
#else /* CURSES */
#define MAXWIDTH 80	/* Maximum width of all columns */
#endif /* CURSES */
X
/*
X * Print a vector in columns
X *
X * If "skip" is nonzero, that many chars are assumed to be in common
X * and are not printed.  WARNING: skip must be <= than the length of
X * the shortest string in the vector!  Safest to call with skip = 0.
X */
columnate(argc, argv, skip)
int argc;
char **argv;
int skip;
{
X    int colstep, colwidth[MAXCOLS + 1];
X    int maxcols = min(argc, MAXCOLS);
X    int minwidth, maxwidth, *widths;
X    int maxword = 0, n, c;
X
X    if (argc <= 0 || !argv || !*argv)
X	return -1;
X    if (!(widths = (int *)malloc((unsigned)((argc + 1) * sizeof(int)))))
X	return -1;
X
X    /*
X     * Compute the widths of all words in the vector, and
X     * remember the maximum width and which word had it.
X     * Also remember the minimum width.
X     */
X    for (minwidth = MAXWIDTH, maxwidth = n = 0; n < argc; n++) {
X	widths[n] = max(strlen(argv[n] + skip) + 2, MINWIDTH);
X	if (widths[n] > MAXWIDTH - MINWIDTH)
X	    break;
X	if (widths[n] > maxwidth) {
X	    maxwidth = widths[n];
X	    maxword = n;
X	} 
X	if (widths[n] < minwidth)
X	    minwidth = widths[n];
X    }
X
X    for (; maxcols > 0; maxcols--) {
X	if (argc % maxcols)
X	    colstep = argc / maxcols + 1;
X	else
X	    colstep = argc / maxcols;
X	colwidth[MAXCOLS] = 0;
X	for (c = 0; c < maxcols; c++) {
X	    colwidth[c] = 0;
X	    for (n = c * colstep; n < (c + 1) * colstep && n < argc; n++)
X		colwidth[c] = max(colwidth[c], widths[n]);
X	    colwidth[MAXCOLS] += colwidth[c];
X	}
X	if (colwidth[MAXCOLS] <= MAXWIDTH)
X	    break;
X    }
X    xfree(widths);
X
X    if (maxcols < 2 && minwidth <= MAXWIDTH / 2) {
X	/*
X	 * The maxword fills too much screen, so redo everything
X	 * above it, print maxword, then do everything below it.
X	 */
X	if (maxword > 0 && columnate(maxword, argv, skip) < 0)
X	    return -1;
X	wprint("%s\n", argv[maxword] + skip);
X	if (argc - maxword < 2)
X	    return 0;
X	return columnate(argc - maxword - 1, &argv[maxword + 1], skip);
X    }
X
X    for (n = 0; n < colstep; n++) {
X	for (c = 0; c < maxcols && n + c * colstep < argc - colstep; c++)
X	    wprint("%-*.*s", colwidth[c], colwidth[c],
X					    argv[n + c * colstep] + skip);
X	wprint("%s\n", argv[n + c * colstep] + skip);
X    }
X
X    return 0;
}
X
#ifndef DIRECTORY
X
#undef NULL
#define NULL 0
X
/*
X *  4.2BSD directory access emulation for non-4.2 systems.
X *  Based upon routines in appendix D of Portable C and Unix System
X *  Programming by J. E. Lapin (Rabbit Software).
X *
X *  No responsibility is taken for any error in accuracies inherent
X *  either to the comments or the code of this program, but if
X *  reported to me then an attempt will be made to fix them.
X */
X
/*  Support for Berkeley directory reading routines on a V7/SysV file
X *  system.
X */
X
/*  Open a directory. */
X
DIR *
opendir(name)
char *name ;
{
X  register DIR *dirp ;
X  register int fd ;
X
X  if ((fd = open(name, 0)) == -1) return NULL ;
X  if ((dirp = (DIR *) malloc(sizeof(DIR))) == NULL)
X    {
X      close(fd) ;
X      return NULL ;
X    }
X  dirp->dd_fd = fd ;
X  dirp->dd_loc = 0 ;
X  return dirp ;
}
X
X
/*  Read an old style directory entry and present it as a new one. */
X
#define  ODIRSIZ  14
X
struct olddirent
{
X  short  od_ino ;
X  char   od_name[ODIRSIZ] ;
} ;
X
X
/*  Get next entry in a directory. */
X
struct dirent *
readdir(dirp)
register DIR *dirp ;
{
X  register struct olddirent *dp ;
X  static struct dirent dir ;
X
X  for (;;)
X    {
X      if (dirp->dd_loc == 0)
X        {
X          dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ) ;
X          if (dirp->dd_size <= 0) return NULL ;
X        }
X      if (dirp->dd_loc >= dirp->dd_size)
X        {
X          dirp->dd_loc = 0 ;
X          continue ;
X        }
X
X      dp = (struct olddirent *)(dirp->dd_buf + dirp->dd_loc) ;
X      dirp->dd_loc += sizeof(struct olddirent) ;
X
X      if (dp->od_ino == 0) continue ;
X
X      dir.d_fileno = dp->od_ino ;
X      strncpy(dir.d_name, dp->od_name, ODIRSIZ) ;
X      dir.d_name[ODIRSIZ] = '\0' ;       /* Ensure termination. */
X      dir.d_namlen = strlen(dir.d_name) ;
X      dir.d_reclen = DIRSIZ(&dir) ;
X      return(&dir) ;
X    }
}
X
X
/*  Close a directory. */
X
void
closedir(dirp)
register DIR *dirp ;
{
X  close(dirp->dd_fd) ;
X  dirp->dd_fd = -1 ;
X  dirp->dd_loc = 0 ;
X  xfree(dirp) ;
} 
X
#endif /* DIRECTORY */
SHAR_EOF
echo 'File glob.c is complete' &&
chmod 0644 glob.c ||
echo 'restore of glob.c failed'
Wc_c="`wc -c < 'glob.c'`"
test 19617 -eq "$Wc_c" ||
	echo 'glob.c: original size 19617, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= glob.h ==============
if test -f 'glob.h' -a X"$1" != X"-c"; then
	echo 'x - skipping glob.h (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting glob.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'glob.h' &&
#ifdef BSD
#define DIRECTORY
#endif /* BSD */
X
#ifdef DIRECTORY
#ifdef SYSV /* Some SysV 3.0 or higher */
#include <dirent.h>
#else /* SYSV */
#include <sys/dir.h>
#define dirent direct
#endif
#else /* !DIRECTORY */
X
/*
X *  4.2BSD directory access emulation for non-4.2 systems.
X *  Based upon routines in appendix D of Portable C and Unix System
X *  Programming by J. E. Lapin (Rabbit Software).
X *
X *  No responsibility is taken for any error in accuracies inherent
X *  either to the comments or the code of this program, but if
X *  reported to me then an attempt will be made to fix them.
X */
X
#ifndef  DEV_BSIZE
#define  DEV_BSIZE  512           /* Device block size. */
#endif
X
#define  DIRBLKSIZ  DEV_BSIZE
#define  MAXNAMLEN  255           /* Name must be no longer than this. */
X
struct dirent
{
X  long  d_fileno ;                /* Inode number of entry. */
X  short d_reclen ;                /* Length of this record. */
X  short d_namlen ;                /* Length of d_name string. */
X  char  d_name[MAXNAMLEN + 1] ;   /* Directory name. */
} ;
X
/*  The DIRSIZ macro gives the minimum record length that will hold the
X *  directory entry. This requires the amount of space in struct direct
X *  without the d_name field, plus enough space for the name with a
X *  terminating null byte (dp->d_namlen+1), rounded up to a 4 byte
X *  boundary.
X */
X
#undef   DIRSIZ
#define  DIRSIZ(dp)                                \
X         ((sizeof (struct dirent) - (MAXNAMLEN+1)) \
X         + (((dp)->d_namlen+1 + 3) &~ 3))
X
/*  Definitions for library routines operating on directories. */
X
typedef struct _dirdesc
{
X  int    dd_fd ;
X  long   dd_loc ;
X  long   dd_size ;
X  char   dd_buf[DIRBLKSIZ] ;
} DIR ;
X
#ifndef  NULL
#define  NULL  0
#endif
X
extern  DIR              *opendir() ;
extern  struct dirent    *readdir() ;
extern  long             telldir() ;
extern  void             seekdir() ;
#define rewinddir(dirp)  seekdir((dirp), (long) 0)
extern  void             closedir() ;
X
#endif /* DIRECTORY */
X
#define DELIM " \t;|"
#define META "/?*[{"
#define FMETA "?*[{"
SHAR_EOF
chmod 0644 glob.h ||
echo 'restore of glob.h failed'
Wc_c="`wc -c < 'glob.h'`"
test 2071 -eq "$Wc_c" ||
	echo 'glob.h: original size 2071, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= hdr_sw.c ==============
if test -f 'hdr_sw.c' -a X"$1" != X"-c"; then
	echo 'x - skipping hdr_sw.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting hdr_sw.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'hdr_sw.c' &&
/* @(#)hdr_sw.c	(c) copyright	2/17/90 (Dan Heller) */
X
/* This file handles all the header subwindow code.  It would be much
X * better if this subwindow, which displays the headers for the current
X * folder, were a textsw.  That way, this file would go away completely.
X * Until then, we have to create the window (canvas), define an event
X * handler for when events happen in this window, create our own scrollbar,
X * figure out when the user scrolls with it, attach our own popup menu to
X * the canvas, handle events for that, let's see... kitchen sink?  Oh,
X * that's over there in the corner.
X */
#include "mush.h"
#ifdef SUN_4_0 /* SunOS 4.0+ */
#include <sunwindow/win_keymap.h>
#endif /* SUN_4_0 */
X
extern Panel hdr_panel;
extern void hdr_io(), fkey_interposer();
X
static Notify_value scroll_hdr();
static void msg_menu_func(), do_menu(), msg_menu_notify();
Menu msg_menu;
X
void
make_hdr_sw(parent)
Frame parent;
{
X    Textsw tmpsw;
X
X    if (!(hdr_sw = window_create(parent, CANVAS,
X	WIN_HEIGHT,		10 + screen*l_height(),
X	WIN_WIDTH,		WIN_EXTEND_TO_EDGE,
X	WIN_BELOW,		hdr_panel,
X	WIN_EVENT_PROC,		hdr_io,
X	CANVAS_AUTO_CLEAR,	TRUE,
X	CANVAS_RETAINED,	TRUE,
X	WIN_CONSUME_KBD_EVENTS,
X	    WIN_ASCII_EVENTS, WIN_LEFT_KEYS, WIN_TOP_KEYS, WIN_RIGHT_KEYS, NULL,
X	WIN_IGNORE_KBD_EVENTS,
X	    WIN_UP_ASCII_EVENTS, NULL,
X	WIN_CONSUME_PICK_EVENTS,
X	    LOC_WINENTER, WIN_MOUSE_BUTTONS, LOC_MOVE, NULL,
X	WIN_VERTICAL_SCROLLBAR, scrollbar_create(0),
X	NULL)))
X	perror("hdr_sw"), cleanup(0);
X    hdr_win = canvas_pixwin(hdr_sw);
X    (void) notify_interpose_event_func(hdr_sw, fkey_interposer, NOTIFY_SAFE);
X    (void) notify_interpose_event_func(hdr_sw, scroll_hdr, NOTIFY_SAFE);
X    scrollbar_set((Scrollbar)window_get(hdr_sw, WIN_VERTICAL_SCROLLBAR),
X	SCROLL_NORMALIZE,	FALSE,
X	SCROLL_ADVANCED_MODE,	TRUE,
X	SCROLL_LINE_HEIGHT,	l_height(),
X	SCROLL_VIEW_LENGTH,	screen,
X	NULL);
#ifdef SUN_4_0 /* SunOS 4.0+ */
X    /* This is a particularly ugly hack.  If Sun only documented the correct
X     * way to set up the key mapping for a window the way that textsw's do
X     * then we wouldn't have to do anything this awful.  Maybe in 4.2.....
X     *
X     * The object here is to get the same translation table for our header
X     * canvas as for a textsw (more or less anyway).  This way the arrow
X     * keys and such work right.
X     */
X    tmpsw = window_create(parent, TEXTSW, NULL);
#ifdef SUN_4_1
X    keymap_from_fd[(int)window_get(hdr_sw, WIN_FD)].keymap =
X	keymap_from_fd[(int)window_get(tmpsw, WIN_FD)].keymap;
X    keymap_from_fd[(int)window_get(tmpsw, WIN_FD)].keymap = (Keymap *) 0;
#else /* !SUN_4_1 */
X    keymap_from_fd[(int)window_get(hdr_sw, WIN_FD)].kf_keymap =
X	keymap_from_fd[(int)window_get(tmpsw, WIN_FD)].kf_keymap;
X    keymap_from_fd[(int)window_get(tmpsw, WIN_FD)].kf_keymap = (Keymap *) 0;
#endif /* SUN_4_1 */
X    (void) window_destroy(tmpsw);
#endif /* SUN_4_0 */
}
X
static Notify_value
scroll_hdr(canvas, event, arg, type)
Canvas	canvas;
Event	*event;
Notify_arg	arg;
Notify_event_type	type;
{
X    int amount, count, i;
X    int show_deleted = !!do_set(set_options, "show_deleted");
X    char *argv[3], msgnum[8];
X    Scrollbar sb;
X    argv[0] = "headers";
X    argv[1] = msgnum;
X    argv[2] = NULL;
X
X    switch (decode_scroll((Notify_client) canvas, event, screen, &amount)) {
X	case MUSH_SCROLL_PASS_EVENT:
X	    switch(ID) {
X		case SCROLL_ENTER:
X		case SCROLL_EXIT:
X		    return NOTIFY_IGNORED;
X		case SCROLL_REQUEST:
X		    sb = (Scrollbar)arg;
X		    switch( (Scroll_motion)
X			 scrollbar_get(sb, SCROLL_REQUEST_MOTION)) {
X			case SCROLL_LINE_FORWARD:
X			    amount = 1;
X			    break;
X			case SCROLL_LINE_BACKWARD:
X			    amount = -1;
X			    break;
X			case SCROLL_ABSOLUTE:
X			    i = (int)scrollbar_get(sb, SCROLL_VIEW_START);
X			    if (!show_deleted) {
X				count = i;
X				for (i = 0; i < msg_cnt-1; i++)
X	    			    if (!ison(msg[i].m_flags, DELETE) &&
X					    count-- == 0)
X					break;
X			    }
X			    (void) sprintf(msgnum, "%d", i+1);
X			    argv[1] = msgnum;
X			    (void) do_hdrs(2, argv, NULL);
X			    return(NOTIFY_DONE);
X			default:
X			    amount =
X				(int)scrollbar_get(sb, SCROLL_VIEW_START) -
X				(int)scrollbar_get(sb, SCROLL_LAST_VIEW_START);
X			    break;
X		    }
X		    break;
X		default:
X		    return notify_next_event_func(canvas, event, arg, type);
X	    }
X	    break;
X	case MUSH_SCROLL_IGNORE:
X	    return NOTIFY_IGNORED;
X	case MUSH_SCROLL_TO:
X	    if (amount == 1) {
X		argv[1] = "1";
X		(void) do_hdrs(2, argv, NULL);
X		return NOTIFY_DONE;
X	    } else {
X		(void) sprintf(msgnum, "%d", msg_cnt - screen + 1);
X		argv[1] = msgnum;
X		(void) do_hdrs(2, argv, NULL);
X		return NOTIFY_DONE;
X	    }
X    }
X    if (amount == screen)
X	argv[1] = "+";
X    else if (amount == -screen)
X	argv[1] = "-";
X    else if (amount >= 0) {
X	if (amount < screen)
X	    (void) sprintf(msgnum, "%d", min(n_array[amount]+1, msg_cnt-1));
X	else {
X	    /* so much for layering */
X	    for (i = n_array[0]+1; i < msg_cnt-1 && amount > 0; i++)
X		if (show_deleted || !ison(msg[i].m_flags, DELETE))
X			amount--;
X	    (void) sprintf(msgnum, "%d", i);
X	}
X    } else {
X	/* so much for layering */
X	for (i = n_array[0]; i > 0 && amount < 0; i--)
X	    if (show_deleted || !ison(msg[i-1].m_flags, DELETE))
X		amount++;
X	(void) sprintf(msgnum, "%d", i + 1);
X    }
X    (void) do_hdrs(2, argv, NULL);
X    return NOTIFY_DONE;
}
X
/*
X * Routines to handle io on the hdr_sw (canvas).
X */
X
/* if MENU button goes down on a hdr, drawbox around hdr and popup menu */
#define draw(x1,y1,x2,y2) (void) pw_vector(hdr_win, x1,y1,x2,y2,PIX_XOR,1)
#define box(x1,y1,x2,y2)  \
X	draw(x1,y1, x1,y2), draw(x1,y2, x2,y2), \
X	draw(x2,y2, x2,y1), draw(x2,y1, x1,y1)
X
#define READ_MSG	(char *)'r'
#define DEL_MSG		(char *)'d'
#define UNDEL_MSG	(char *)'u'
#define REPL_MSG	(char *)'R'
#define SAVE_MSG	(char *)'s'
#define PRNT_MSG	(char *)'p'
#define PRE_MSG		(char *)'P'
#define MARK_MSG	(char *)'m'
#define HELP_MSG	(char *)'H'
X
#define MARK_TOGGLE	(char *)'T'
#define MARK_A		(char *)'A'
#define MARK_B		(char *)'B'
#define MARK_C		(char *)'C'
#define MARK_D		(char *)'D'
#define MARK_E		(char *)'E'
#define MARK_CLEAR	(char *)'c'
#define MARK_HELP	(char *)'h'
X
/*ARGSUSED*/
void
hdr_io(canvas, event, arg)
Canvas canvas;
Event *event;
caddr_t arg;
{
X    static int	which_cursor;
X    int 	line;
X
X    if (ID == WIN_REPAINT) {
X	if (is_iconic != (int) window_get(tool, FRAME_CLOSED)) {
X	    check_new_mail();
X
X	    /*  Reload time with value of timeout upon timer expiration. */
X	    mail_timer.it_interval.tv_sec = time_out;
X
X	    mail_timer.it_value.tv_sec = time_out;
X	    (void) notify_set_itimer_func(tool, do_check,
X		ITIMER_REAL, &mail_timer, (struct itimerval *) 0);
X	    is_iconic = 0;
X	}
X    }
X
X    /* make cursor change which button is lit */
X    switch (which_cursor) {
X	case 0 : (void) window_set(canvas, WIN_CURSOR, l_cursor, NULL);
X	when 1 : (void) window_set(canvas, WIN_CURSOR, m_cursor, NULL);
X	when 2 : (void) window_set(canvas, WIN_CURSOR, r_cursor, NULL);
X    }
X
X    which_cursor = (which_cursor+1) % 3;
X
X    /* just return -- we just wanted to make the cursor flicker */
X    if (ID == LOC_STILL || ID == LOC_MOVE || ID == LOC_WINENTER ||
X	ID == LOC_RGNENTER || ID == KBD_USE || ID == KBD_DONE)
X	return;
X
X    if (event_is_button(event) && event_is_down(event)) {
X	line = (event_y(event) - 5) / l_height();
X	if (line < 0)
X	    line = 0;
X	else if (line >= screen)
X	    line = screen - 1;
X	if (!msg_cnt || n_array[line] > msg_cnt)
X	    return;
X	if (ID == MS_RIGHT)
X	    do_menu(hdr_sw, event, window_get(hdr_sw, WIN_FD), n_array[line]);
X	else if (ID == MS_MIDDLE) {
X	    set_isread(n_array[line]);
X	    msg_menu_func((int)DEL_MSG, n_array[line]);
X	} else {
X	    int do_do_hdrs = 0;
X	    if (current_msg != n_array[line]) {
X		current_msg = n_array[line];
X		do_do_hdrs++;
X	    }
X	    if (ison(msg[current_msg].m_flags, UNREAD))
X		do_do_hdrs++;
X	    (void) display_msg(n_array[line], (u_long)0);
X	    if (do_do_hdrs)
X		(void) do_hdrs(0, DUBL_NULL, NULL);
X	}
X    } else
X	window_default_event_proc(canvas, event, NULL);
}
X
static struct menu_rec {
X    char *str;	/* Menu item label. */
X    char *data;	/* Menu item client data. */
};
X
void
get_msg_menu()
{
X    int i;
X    Menu_item mi = NULL, sub_mi;
X
X    static struct menu_rec msg_items[] = {
X	{ "Read",            READ_MSG  },
X	{ "Delete",          DEL_MSG   },
X	{ "Undelete",        UNDEL_MSG },
X	{ "Reply",           REPL_MSG  },
X	{ "Save",            SAVE_MSG  },
X	{ "Preserve",        PRE_MSG   },
X	{ "Mark",	     MARK_MSG  },
X	{ "Print",           PRNT_MSG  },
X	{ "Help",            HELP_MSG  },
X    };
X    static struct menu_rec mark_msg_items[] = {
X	{ "Toggle Mark",    MARK_TOGGLE},
X	{ "Priority A",     MARK_A     },
X	{ "Priority B",     MARK_B     },
X	{ "Priority C",     MARK_C     },
X	{ "Priority D",     MARK_D     },
X	{ "Priority E",     MARK_E     },
X	{ "Clear Priority", MARK_CLEAR },
X	{ "Help",           MARK_HELP  },
X    };
X
X    msg_menu = menu_create(MENU_NOTIFY_PROC, menu_return_item, NULL);
X    for (i = 0; i < ArraySize(msg_items); i++) {
X	mi = menu_create_item(MENU_STRING,	msg_items[i].str,
X			      MENU_CLIENT_DATA,	msg_items[i].data,
X			      NULL);
X	if (msg_items[i].data == MARK_MSG) {
X	    int j;
X	    /* get the menu from <Mark> and set as this item's pullright */
X	    Menu the_menu = menu_create(
X		MENU_NOTIFY_PROC, menu_return_item, NULL);
X	    for (j = 0; j < ArraySize(mark_msg_items); j++) {
X		sub_mi = menu_create_item(
X		    MENU_STRING,	mark_msg_items[j].str,
X		    MENU_CLIENT_DATA,	mark_msg_items[j].data,
X		    NULL);
X		(void) menu_set(the_menu, MENU_APPEND_ITEM, sub_mi, NULL);
X	    }
X	    menu_set(mi, MENU_PULLRIGHT, the_menu, NULL);
X	}
X	(void) menu_set(msg_menu, MENU_APPEND_ITEM, mi, NULL);
X    }
}
X
static void
do_menu(can_sw, event, fd, message)
Canvas can_sw;
Event *event;
int fd, message;
{
X    char *action;
X    char *save_place;
X    Menu_item cur_msg_item;
X    static char buf[16];
X
X    if (!msg_cnt) {
X	wprint("No Messages.\n");
X	return;
X    }
X    if (fd) {
X	int line;
X	Rect *hdr_rect;
X	extern Menu hdr_save_menu;
X
X	if (!msg_menu)
X	    get_msg_menu();
X	(void) sprintf(buf, "Message #%d", message+1);
X	/* provide feedback about what message the menu references */
X	for (line = 0; line <= n_array[screen-1]; line++)
X	    if (n_array[line] == message)
X		break;
X	hdr_rect = (Rect *)window_get(hdr_sw, WIN_RECT);
X	box(0, 5 + line * l_height(),
X	    hdr_rect->r_width, 5 + (line+1) * l_height());
X	/* show menu */
X	cur_msg_item = menu_show(msg_menu, can_sw, event, NULL);
X	/* remove feedback */
X	box(0, 5 + line * l_height(),
X	    hdr_rect->r_width, 5 + (line+1) * l_height());
X	/* if user selected something, figure out what was selected. */
X	if (!cur_msg_item)
X	    return;
#ifndef NO_WALK_MENUS
X	if ((Menu)menu_get(cur_msg_item, MENU_PARENT) == hdr_save_menu) {
X	    save_place = (char *)menu_get(cur_msg_item, MENU_CLIENT_DATA);
X	    action = SAVE_MSG;
X	} else
#endif /* NO_WALK_MENUS */
X	    action = (char *) menu_get(cur_msg_item, MENU_CLIENT_DATA);
X    } else
X	action = (char *) event;
X
X    set_isread(message);
X    switch ((int) action) {
X	case SAVE_MSG : {
X	    extern Panel_item msg_num_item, save_item;
X	    (void) panel_set(msg_num_item, PANEL_VALUE,
X					sprintf(buf, "%d", message+1), NULL);
#ifndef NO_WALK_MENUS
X		if (*save_place == '\0') /* magic to mean "use Filename:" */
X		    do_file_dir(save_item, event);
X		else
X		    xx_file_dir(save_item, save_place);
#else /* NO_WALK_MENUS */
X		event_id(event) = MS_LEFT;
X		do_file_dir(save_item, 0, event);
#endif /* NO_WALK_MENUS */
X	    (void) panel_set(msg_num_item, PANEL_VALUE, NO_STRING, NULL);
X	}
X	when HELP_MSG :
X	    help(0, "headers", tool_help);
X	when REPL_MSG : {
X	    extern Panel_item reply_item;
X	    open_compose();
X	    if (!compose_frame)
X		break;	/* open failed */
X	    /* reply_item shouldn't be here */
X	    respond_mail(reply_item, message, NO_EVENT);
X	}
X	when READ_MSG :
X	    if (current_msg != message) {
X		current_msg = message;
X		(void) do_hdrs(0, DUBL_NULL, NULL);
X	    }
#ifdef SUN_3_5
X	    /* Test for a shortage of file descriptors */
X	    if (nopenfiles(0) > 3)
#endif /* SUN_3_5 */
X	    turnon(glob_flags, NEW_FRAME);
X	    more_prompt = compose_hdr(message);
X	    display_msg(message, (u_long)0);
X
X	otherwise :
X	    msg_menu_func((int)action, message);
X    }
}
X
/* msg_menu_func() is a function called to perform message menu actions
X * that are either selected from the popup menu in the header window or
X * from mouse actions that function as accelerators.
X */
static void
msg_menu_func(action, message)
int action;
{
X    int argc;
X    register char **argv;
X    char buf[32];
X
X    switch (action) {
X        case PRNT_MSG :
X	    wprint("Message #%d sent to printer.\n", message+1);
X	    (void) strcpy(buf, "lpr");
X	when UNDEL_MSG : case DEL_MSG :
X	    (void) sprintf(buf, "%selete", (action == (int)DEL_MSG)?"d":"und");
X        when PRE_MSG :
X	    (void) strcpy(buf, "preserve");
X        when MARK_MSG : case MARK_TOGGLE :
X	    (void) sprintf(buf, "%smark",
X		ison(msg[message].m_flags, M_PRIORITY(0))? "un" : "");
X	when MARK_A : case MARK_B : case MARK_C : case MARK_D : case MARK_E :
X	    (void) sprintf(buf, "mark -%c", action);
X	when MARK_CLEAR	:
X	    (void) strcpy(buf, "mark -");
X	when MARK_HELP :
X	    (void) help(0, "mark", tool_help);
X	    return;
X	otherwise :
X	    print("unknown switch: %c\n", action);
X    }
X    (void) sprintf(&buf[strlen(buf)], " %d", message+1);
X
X    if (argv = make_command(buf, (char ***) DUBL_NULL, &argc))
X	(void) do_command(argc, argv, msg_list);
}
SHAR_EOF
chmod 0644 hdr_sw.c ||
echo 'restore of hdr_sw.c failed'
Wc_c="`wc -c < 'hdr_sw.c'`"
test 13462 -eq "$Wc_c" ||
	echo 'hdr_sw.c: original size 13462, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= hdrs.c ==============
if test -f 'hdrs.c' -a X"$1" != X"-c"; then
	echo 'x - skipping hdrs.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting hdrs.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'hdrs.c' &&
/* hdrs.c 	(c) copyright 1986 (Dan Heller) */
X
/*
X * Routines that deal with message headers inside messages
X * msg_get(n, from, count) -- get the From_ line in msg n into "from".
X * header_field(n, str) -- get the header named "str" from msg n.
X * do_hdrs(argc, argv, list) -- diplay message headers.
X * specl_hdrs(argv, list) -- display msgs that share common attributes.
X * compose_hdr(cnt) -- compose a message header from msg n.
X * reply_to(n, all, buf) -- construct a header based on the To: header of n.
X * subject_to(n, buf) -- get the subject for replying to msg n.
X * cc_to(n, buf) -- construct a Cc header based on the Cc of message n.
X */
#include "mush.h"
X
#ifdef SUNTOOL
#define highlight(win,x,y,s) \
X    (void) (pw_text(win,x,y, PIX_SRC, mush_font, s), \
X    pw_text(win,x+1,y, \
X	(ison(glob_flags, REV_VIDEO))? PIX_NOT(PIX_SRC): PIX_SRC|PIX_DST, \
X	mush_font, s))
#endif /* SUNTOOL */
X
/*
X * Get a message from the current folder by its offset.
X * Copy the From_ line to the second argument if the third arg > 0,
X * and return the second argument, or NULL on an error.
X */
char *
msg_get(n, from, count)
int n, count;
char *from;
{
X    if (fseek(tmpf, msg[n].m_offset, L_SET) == -1) {
X	error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
X	turnon(glob_flags, READ_ONLY);
X	return NULL;
X    }
X    if (count)
#ifndef MSG_SEPARATOR
X	return fgets(from, count, tmpf);
#else
X	*from = '\0';
#endif
X    return from;
}
X
/*
X * get which message via the offset and search for the headers which
X * match the string "str". there may be more than one of a field (like Cc:)
X * so get them all and "cat" them together into the static buffer
X * "buf" and return its address.
X */
char *
header_field(n, str)
char *str;
{
X    static char    buf[HDRSIZ];
X    char 	   tmp[HDRSIZ];
X    register char  *p, *p2, *b = buf;
X    int contd_hdr;  /* true if next line is a continuation of the hdr we want */
X
X    /* use msg_get as a test for fseek() -- don't let it fgets() (pass 0) */
X    if (!msg_get(n, tmp, 0))
X	return NULL;
X    *b = 0;
X    while((p = fgets(tmp, sizeof(tmp), tmpf)) && *p != '\n') {
X	if (*p != ' ' && *p != '\t') {
X	    contd_hdr = 0;
X	    /* strcmp ignoring case */
X	    for(p2 = str; *p && *p2 && lower(*p2) == lower(*p); ++p, ++p2);
X	    /* MATCH is true if p2 is at the end of str and *p is ':' */
X	    if (*p2 || *p++ != ':')
X		continue;
X	    else
X		contd_hdr = 1;
X	    if (b > buf && (b - buf) < sizeof buf - 2)
X		*b++ = ',';
X	} else if (!contd_hdr)
X	    continue;
X	skipspaces(0);
X	(void) no_newln(p);
X	if (strlen(p) + (b - buf) < sizeof buf - 1) {
X	    if (b > buf)
X		*b++ = ' ';
X	    b += Strcpy(b, p);
X	}
X    }
X    if (b > buf && *--b == ',')
X	*b = 0;
X    return (*buf)? buf: NULL;
}
X
do_hdrs(argc, argv, list)
register char **argv, list[];
{
X    register int   pageful = 0;
X    SIGRET 	   (*oldint)(), (*oldquit)();
X    int		   show_deleted, srch = 1; /* search forward by default */
X    static int     cnt, oldscrn = 1;
X    register char  *p;
X    char 	   first_char = (argc) ? **argv: 'h';
X
X    if (argc > 1 && !strcmp(argv[1], "-?"))
X	return help(0, "headers", cmd_help);
X
X    if (!msg_cnt) {
X	if (ison(glob_flags, DO_PIPE) && !istool)
X	    return 0;
#ifdef CURSES
X	if (iscurses)
X	    clear();
#endif /* CURSES */
#ifdef SUNTOOL
X	if (istool)
X	    mail_status(0);
#endif /* SUNTOOL */
X	return 0;
X    }
X    if (first_char == ':' || (argc > 1 && argv[1][0] == ':')) {
X	if (first_char != ':')
X	    argv++;
X	return specl_hdrs(argv, list);
X    } else if (argc > 1 && !strncmp(argv[1], "-H:", 3)) {
X	argv[1][0] = ':';
X	argv[1][1] = argv[1][3];
X	argv[1][2] = 0;
X	return specl_hdrs(&argv[1], list);
X    }
X
X    on_intr();
X
X    if (argc && (argv[0][1] == '-' || argc > 1 && !strcmp(argv[1], "-"))) {
X	cnt = max(n_array[0], 0);
X	srch = -1;	/* search backwards */
X    } else if (argc && (argv[0][1] == '+' ||
X	    argc > 1 && !strcmp(argv[1], "+")) ||
X	    first_char == 'z' && !argv[1]) {
X	if (msg_cnt > screen)
X	    cnt = min(msg_cnt - screen, n_array[0] + screen);
X	else
X	    cnt = 0;
X    } else if (argc && *++argv &&
X	    (isdigit(**argv) || **argv == '^' ||
X		**argv == '$' || **argv == '.') ||
X	    ison(glob_flags, IS_PIPE) && list) {
X	/* if we're coming from a pipe, start display at the first msg bit
X	 * set in the msg_list
X	 */
X	int fnd;
X	if (ison(glob_flags, IS_PIPE)) {
X	    if (isoff(glob_flags, DO_PIPE))
X		for (fnd = 0; fnd < msg_cnt; fnd++)
X		    if (msg_bit(list, fnd))
X			wprint("%s\n", compose_hdr(fnd));
X	    off_intr();
X	    return 0;
X	}
X	/* if a number was given, use it */
X	if (!(fnd = chk_msg(*argv))) {
X	    off_intr();
X	    return -1;
X	}
X	for (cnt = fnd - 1; cnt > 0 && cnt + screen > msg_cnt; cnt--)
X	    ;
X    } else if (current_msg < n_array[0] || current_msg > n_array[oldscrn-1] ||
X	    (iscurses || oldscrn != screen) &&
X		(cnt > current_msg + screen || cnt < current_msg - screen))
X	cnt = current_msg; /* adjust if reads have passed screen bounds */
X    else if (cnt >= msg_cnt || !argc || !*argv)
X	/* adjust window to maintain position */
X	cnt = (n_array[0] > msg_cnt) ? current_msg : n_array[0];
X
X    oldscrn = screen;
X    show_deleted = !!do_set(set_options, "show_deleted");
X
X    /* Make sure we have at least $screen headers to print */
X    if (cnt > 0 && !iscurses && first_char == 'h') {
X	int top, bot = cnt;
X	/* first count how many messages we can print without adjusting */
X	for (pageful = 0; pageful<screen && bot<msg_cnt && bot; bot += srch)
X	    if (show_deleted || isoff(msg[bot].m_flags, DELETE))
X		pageful++;
X	/* if we can't print a pagefull of hdrs, back up till we can */
X	for (top = cnt-srch; pageful<screen && top && top<msg_cnt; top -= srch)
X	    if (show_deleted || isoff(msg[top].m_flags, DELETE))
X		pageful++;
X	if (srch < 0)
X	    cnt = bot;	/* the search was upside down */
X	else
X	    cnt = top + (pageful == screen);
X	pageful = 0;	/* Used later as an index, so reset */
X    } else if (cnt > 0 && srch < 0)
X	cnt = max(cnt - screen, 0);
X    else
X	cnt = max(cnt, 0);
X
X    for (;pageful<screen && cnt<msg_cnt && isoff(glob_flags, WAS_INTR); cnt++) {
X	if (!iscurses && !show_deleted && first_char == 'h'
X	    && ison(msg[cnt].m_flags, DELETE))
X	    continue;
X	n_array[pageful++] = cnt;
X	/* this message was displayed -- set the bit */
X	if (list)
X	    set_msg_bit(list, cnt);
X	/* if do_pipe, don't output anything */
X	if (ison(glob_flags, DO_PIPE) && !istool)
X	    continue;
X	p = compose_hdr(cnt);
X	if (!istool && (!iscurses || ison(glob_flags, IS_GETTING)))
X	    puts(p);
#ifdef SUNTOOL
X	else if (istool) {
X	    if (cnt == current_msg) /* embolden or reverse-video */
X		highlight(hdr_win, 0,pageful*l_height(), p);
X	    else
X		(void) pw_text(hdr_win, 0, pageful * l_height(), PIX_SRC,
X							mush_font, p);
X	    Clrtoeol(hdr_win, strlen(p)*l_width(), pageful*l_height());
X	}
#endif /* SUNTOOL */
X
#ifdef CURSES
X	else if (iscurses) {
X	    move(pageful, 0);
X	    printw("%-.*s", COLS-2, p), clrtoeol();
X	}
#endif /* CURSES */
X    }
X    /* just in case a signal stopped us */
X    off_intr();
X    pageful++;
#ifdef CURSES
X    if (iscurses && pageful < screen)
X	move(pageful, 0), clrtobot();
#endif /* CURSES */
X    if (cnt == msg_cnt) {
X	while (pageful <= screen) {
X	    n_array[pageful-1] = msg_cnt+1; /* assign out-of-range values */
#ifdef SUNTOOL
X	    if (istool)
X		Clrtoeol(hdr_win, 0, pageful * l_height());
#endif /* SUNTOOL */
X	    ++pageful;
X	}
X    }
#ifdef SUNTOOL
X    if (istool) {
X	Scrollbar sb = (Scrollbar) window_get(hdr_sw, WIN_VERTICAL_SCROLLBAR);
X
X	if (show_deleted) {
X	    scrollbar_set(sb,
X		SCROLL_OBJECT_LENGTH,	msg_cnt,
X		SCROLL_VIEW_START,	n_array[0],
X		0);
X	} else {
X	    int i, not_deleted, start;
X
X	    for (i = start = 0; i < n_array[0]; i++)
X		if (!ison(msg[i].m_flags, DELETE))
X		    start++;
X	    for (not_deleted = start; i < msg_cnt; i++)
X		if (!ison(msg[i].m_flags, DELETE))
X		    not_deleted++;
X	    scrollbar_set(sb,
X		SCROLL_OBJECT_LENGTH,	not_deleted,
X		SCROLL_VIEW_START,	start,
X		0);
X        }
X
X	scrollbar_paint(sb);
X	mail_status(0);
X    }
#endif /* SUNTOOL */
X
X    return 0;
}
X
#define NEW 1
#define ALL 2
X
specl_hdrs(argv, list)
char **argv, list[];
{
X    u_long	special = 0;
X    int 	n = 0;
X
X    while (argv[0][++n])
X	switch(argv[0][n]) {
X	    case 'a': special = ALL;
X	    when 'd': special = DELETE;
X	    when 'm': special = M_PRIORITY(0);
X	    when 'n': special = NEW;
X	    when 'o': special = OLD;
X	    when 'p': special = PRESERVE;
X	    when 'r': special = REPLIED;
X	    when 's': special = SAVED;
X	    when 'u': special = UNREAD;
X	    otherwise: print("choose from d,m,n,o,p,r,s,u or a"); return -1;
X	}
X    if (debug)
X	(void) check_flags(special);
X
X    for (n = 0; n < msg_cnt; n++) {
X	/*
X	 * First, see if we're looking for NEW messages.
X	 * If so, then check to see if the msg is unread and not old.
X	 * If special > ALL, then special has a mask of bits describing
X	 * the state of the message.
X	 */
X	if (ison(glob_flags, IS_PIPE)&& !msg_bit(list, n))
X	    continue;
X	if (special == ALL || special == NEW &&
X	       (ison(msg[n].m_flags, UNREAD) && isoff(msg[n].m_flags, OLD))) {
X	    if (isoff(glob_flags, DO_PIPE))
X		print("%s\n", compose_hdr(n));
X	    if (list)
X		set_msg_bit(list, n);
X	} else if (special > ALL && ison(msg[n].m_flags, special)) {
X	    if (isoff(glob_flags, DO_PIPE))
X		print("%s\n", compose_hdr(n));
X	    if (list)
X		set_msg_bit(list, n);
X	} else {
X	    if (list)
X		unset_msg_bit(list, n);
X	    if (debug) {
X		(void) printf("msg[%d].m_flags: %d", n, msg[n].m_flags);
X		(void) check_flags(msg[n].m_flags);
X	    }
X	}
X    }
X    return 0;
}
X
#define Strncpy(buf,p) (void)(strncpy(buf,p,sizeof(buf)),buf[sizeof(buf)-1]=0)
X
/*
X * format a header from the information about a message (from, to, date,
X * subject, etc..).  The header for message number "cnt" is built and is
X * returned in the static buffer "buf".  There will be *at least* 9 chars
X * in the buffer which will be something like: " 123 >N " The breakdown
X * is as follows: 4 chars for the message number, 1 space, 1 char for '>'
X * (if current message) and two spaces for message status (new, unread, etc)
X * followed by 1 terminating space.
X * Read other comments in the routine for more info.
X */
char *
format_hdr(cnt, hdr_fmt, show_to)
int cnt, show_to;
char *hdr_fmt;
{
X    static char		buf[256];
X    register char	*p, *p2, *b;
X    int			len, do_pad = FALSE, val, pad, got_dot, isauthor = 0, n;
X    char from[HDRSIZ], subject[256], date[64], lines[16];
X    char to[256], addr[256], name[256], user[256], status[4];
X    char Day[3], Mon[4], Tm[8], Yr[5], Wkday[4], Zone[8], *date_p;
X
X    /* status of the message */
X    if (ison(msg[cnt].m_flags, DELETE))
X	status[0] = '*';
X    else if (ison(msg[cnt].m_flags, PRESERVE))
X	status[0] = 'P';
X    else if (ison(msg[cnt].m_flags, SAVED))
X	status[0] = 'S';
X    else if (ison(msg[cnt].m_flags, OLD) && ison(msg[cnt].m_flags, UNREAD))
X	status[0] = 'U';
X    else if (ison(msg[cnt].m_flags, PRINTED))
X	status[0] = 'p';
X    else if (ison(msg[cnt].m_flags, FORWARD))
X	status[0] = 'f';
X    else if (isoff(msg[cnt].m_flags, UNREAD))
X	status[0] = ' ';
X    else
X	status[0] = 'N';
X
X    if (ison(msg[cnt].m_flags, REPLIED))
X	status[1] = 'r';
X    else
X	status[1] = ' ';
X    status[2] = 0;
X
X    to[0] = from[0] = subject[0] = date[0] = lines[0] = addr[0] =
X    user[0] = name[0] = Day[0] = Mon[0] = Tm[0] = Yr[0] = Wkday[0] = 0;
X
X    /* who's the message to */
X    if ((p = header_field(cnt, "resent-to")) ||
X	(p = header_field(cnt, "to")) ||
X	(p = header_field(cnt, "apparently-to")))
X	Strncpy(to, p);
X
X    /* who's the message from */
X    if ((p = header_field(cnt, "from")) && strcpy(from, p)
X	    || (p = reply_to(cnt, 0, from))) {
X	/* NOTE:  this fails if the sender has '<' or '!' in
X	 * the RFC822 comment fields -- leading "comment"
X	 * or trailing (comment) -- but that isn't critical
X	 */
X	if ((p2 = rindex(p, '!')) || (p2 = index(p, '<')))
X	    p = p2 + 1;
X    } else
X	p = strcpy(from, "unknown"); /* just in case */
X    /* If the From field contains the user's login name, then the message
X     * could be from the user -- attempt to give more useful information
X     * by telling to whom the message was sent.  This is not possible if
X     * the "to" header failed to get info (which is probably impossible).
X     * Use take_me_off() to be sure the message really is from the current
X     * user and not just someone with the same login at another site.
X     */
X    if (show_to && !strncmp(p, login, strlen(login)))
X	(void) take_me_off(from);
X    if (show_to && (isauthor = !*from)) {  /* assign and test */
X	(void) get_name_n_addr(to, name+4, addr+4);
X	if (addr[4])
X	    (void) strncpy(addr, "TO: ", 4);
X	if (name[4]) {  /* check to see if a name got added */
X	    (void) strncpy(name, "TO: ", 4);
X	    Strncpy(from, name);
X	} else
X	    Strncpy(from, addr);
X    } else
X	(void) get_name_n_addr(from, name, addr);
X
X    if (ison(glob_flags, DATE_RECV))
X	date_p = msg[cnt].m_date_recv;
X    else
X	date_p = msg[cnt].m_date_sent;
X    (void) date_to_string(date_p, Yr, Mon, Day, Wkday, Tm, Zone, date);
X
X    /* and the subject */
X    if (p = header_field(cnt, "subject"))
X	Strncpy(subject, p);
X
X    /* now, construct a header out of a format string */
X    if (!hdr_fmt)
X	hdr_fmt = hdr_format;
X    {
X	int i;
X	for (i = MAX_PRIORITY; i > 0; i--)
X	    if (ison(msg[cnt].m_flags, M_PRIORITY(i))) {
X		p2 = sprintf(lines, "%d", i);
X		break;
X	    }
X	(void) sprintf(buf, "%c%3.d%s%c%s ",
X		((cnt == current_msg && !iscurses)? '>': ' '),
X		cnt+1, cnt < 999 ? " " : "",
X		(ison(msg[cnt].m_flags, M_PRIORITY(0)) ? '+' :
X					i > 0 ? 'A' + i - 1 : ' '),
X		status);
X    }
X    /* Count chars since beginning of buf. Initialize to 9 (strlen(buf) so far)
X     * This magic number is used in other places in msgs.c and mail.c
X     */
X    n = 9;
X    b = buf+9;
X    for (p = hdr_fmt; *p; p++)
X	if (*p == '\\')
X	    switch (*++p) {
X		case 't':
X		    while (n % 8)
X			n++, *b++ = ' ';
X		when 'n':
X		    n = 1, *b++ = '\n';
X		otherwise: n++, *b++ = *p;
X	    }
X	else if (*p == '%') {
X	    char fmt[64];
X
X	    p2 = fmt;
X	    /* first check for string padding: %5n, %.4a, %10.5f, %-.3l etc. */
X	    do_pad = pad = val = got_dot = 0;
X	    *p2++ = '%';
X	    if (p[1] != '-')
X		*p2++ = '-';
X	    else
X		++p;
X	    while (isdigit(*++p) || !got_dot && *p == '.') {
X		if (*p == '.')
X		    got_dot = TRUE, val = pad, pad = 0;
X		else
X		    pad = pad * 10 + *p - '0';
X		*p2++ = *p;
X	    }
X	    if (!got_dot && isdigit(p[-1])) {
X		*p2 = 0; /* assure null termination */
X		val = atoi(fmt+1);
X		if (val < 0)
X		    val = -val;
X		p2 += strlen(sprintf(p2, ".%d", val));
X	    }
X	    pad = min(pad, val);
X	    *p2++ = 's', *p2 = 0;
X	    if (!*p)
X		break;
X	    switch (*p) {
X		case 'f': p2 = from, do_pad = TRUE;
X		when 'a':
X		    if (!*(p2 = addr))
X			p2 = from;
X		    do_pad = TRUE;
X		when 'u' :
X		    if (!user[0])
X			(void) bang_form(user, addr);
X		    if (p2 = rindex(user, '!'))
X			p2++;
X		    else
X			p2 = user;
X		when 'n':
X		    if (!*(p2 = name))
X			p2 = from, do_pad = TRUE;
X		when '%': p2 = "%";
X		when 't': p2 = to;
X		when 's': p2 = subject;
X		when 'l': p2 = sprintf(lines, "%d", msg[cnt].m_lines);
X		when 'c': p2 = sprintf(lines, "%ld", msg[cnt].m_size);
X		when 'i': (p2 = header_field(cnt, "message-id")) || (p2 = "");
X		/* date formatting chars */
X		when 'd': p2 = date; /* the full date */
X		when 'T': p2 = Tm;
X		when 'M': p2 = Mon;
X		when 'Y': p2 = Yr;
X		when 'y': p2 = Yr+2;
X		when 'N': p2 = Day;
X		when 'D': case 'W': p2 = Wkday;
X		when 'Z': p2 = Zone;
X		/* Any selected header */
X		when '?': {
X		    p2 = p + 1;
X		    p = index(p2, '?');
X		    if (p) {
X			*p = 0;
X			if (!(p2 = header_field(cnt, p2)))
X			    p2 = "";
X			*p = '?';
X		    } else {
X			p = p2 + (strlen(p2) - 1);
X			if (!(p2 = header_field(cnt, p2)))
X			    p2 = "";
X		    }
X		}
X		otherwise: continue; /* unknown formatting char */
X	    }
X	    if (do_pad && pad && strlen(p2) > pad) {
X		char *old_p2 = p2, *p3;
X		int is_bangform = 0;
X		/* if addr is too long, move pointer forward till the
X		 * "important" part is readable only for ! paths/addresses.
X		 */
X		while (p3 = index(p2, '!')) {
X		    is_bangform = 1;
X		    len = strlen(p3+1); /* xenix has compiler problems */
X		    p2 = p3+1;
X		    if (len + isauthor*4 < pad) {
X			if (isauthor && (p2 -= 4) < old_p2)
X			    p2 = old_p2;
X			break;
X		    }
X		}
X		if (isauthor && p2 > old_p2+4 && !p3 && strlen(p2) + 4 > pad)
X		    p2 -= 4;
X		if (is_bangform && (p3 = rindex(p2, '@'))) {
X		    len = strlen(p3);
X		    while (len-- && --p2 > old_p2) {
X			if (*(p2 + isauthor*4 - 1) == '!')
X			    break;
X		    }
X		}
X		if (old_p2 != p2 && isauthor)
X		    (void) strncpy(p2, "TO: ", 4); /* doesn't null terminate */
X	    }
X	    len = strlen(sprintf(b, fmt, p2));
X	    n += len, b += len;
X	    /* Get around a bug in 5.5 IBM RT which pads with NULs not ' ' */
X	    while (n && !*(b-1))
X		b--, n--;
X	} else
X	    n++, *b++ = *p;
X    /* Since show_to is true only when called from compose_hdr() below,
X     * use it to decide whether trailing whitespace should be trimmed.
X     */
X    if (show_to)
X	for (*b-- = 0; isspace(*b) && *b != '\n'; --b)
X	    *b = 0;
X    else
X	*b = 0;
X    return buf;
}
X
char *
compose_hdr(cnt)
int cnt;
{
X    if (!hdr_format)
X	hdr_format = DEF_HDR_FMT;
X    return format_hdr(cnt, hdr_format, TRUE);
}
X
/*
X * Using message "n", build a list of recipients that you would mail to if
X * you were to reply to this message.  If "all" is true, then it will take
X * everyone from the To line in addition to the original sender.
X * route_addresses() is called from mail.c, not from here.  There are too many
X * other uses for reply_to to always require reconstruction of return paths.
X * Note that we do NOT deal with Cc paths here either.
X * Check to make sure that we in fact return a legit address (i.e. not blanks
X * or null). If such a case occurs, return login name.  Always pad end w/blank.
X */
char *
reply_to(n, all, buf)
char buf[];
{
X    register char *p = NULL, *p2, *b = buf, *field;
X    char line[256], name[256], addr[256], *unscramble_addr();
X
X    if (field = do_set(set_options, "reply_to_hdr")) {
#ifndef MSG_SEPARATOR
X	if (!*field)
X	    goto DoFrom; /* special case -- get the colon-less From line */
#endif /* MSG_SEPARATOR */
X	field = lcase_strcpy(line, field);
X	while (*field) {
X	    if (p2 = any(field, " \t,:"))
X		*p2 = 0;
#ifndef MSG_SEPARATOR
X	    if (!lcase_strncmp(field, "from_", -1))
X		goto DoFrom;
#endif /* MSG_SEPARATOR */
X	    if ((p = header_field(n, field)) || !p2)
X		break;
X	    else {
X		field = p2+1;
X		while (isspace(*field) || *field == ':' || *field == ',')
X		    field++;
X	    }
X	}
X	if (!p)
X	    print("Warning: message contains no `reply_to_hdr' headers.\n");
X    }
X    if (p || (!p && ((p = header_field(n, field = "reply-to")) ||
X		    (p = header_field(n, field = "from")) ||
X		    (p = header_field(n, field = "return-path")))))
X	skipspaces(0);
X    else if (!p) {
#ifndef MSG_SEPARATOR
DoFrom:
X	field = "from_";
X	/* if all else fails, then get the first token in "From" line */
X	if (p2 = msg_get(n, line, sizeof line))
X	    p = index(p2, ' ');
X	else
X	    return "";
X	skipspaces(1);
X	if (p2 = index(p, ' '))
X	    *p2 = 0;
X	(void) unscramble_addr(p, line); /* p is safely recopied to line */
X	p = line;
#else /* MSG_SEPARATOR */
X	wprint("Warning: unable to find who msg %d is from!\n", n+1);
#endif /* MSG_SEPARATOR */
X    }
X    (void) get_name_n_addr(p, name, addr);
X    if (!name[0] && (!lcase_strncmp(field, "return-path", -1) ||
X		     !lcase_strncmp(field, "from_", -1))) {
X	/*
X	 * Get the name of the author of the message we're replying to from the
X	 * From: header since that header contains the author's name.  Only do
X	 * this if the address was gotten from the return-path or from_ lines
X	 * because this is the only way to guarantee that the return address
X	 * matches the author's name.  Reply-To: may not be the same person!
X	 * Check Resent-From: if the address came from the from_ line, else
X	 * check From:, and finally Sender: or Name:.
X	 */
X	if (!lcase_strncmp(field, "from_", -1) &&
X		(p = header_field(n, "resent-from")) ||
X		    (p = header_field(n, "from")) ||
X		    (p = header_field(n, "sender")))
X	    (void) get_name_n_addr(p, name, NULL);
X	if (!name[0] && (p = header_field(n, "name")))
X	    (void) strcpy(name, p);
X	if (name[0]) {
X	    if ((p = any(name, "(<,\"")) && (*p == ',' || *p == '<'))
X		*b++ = '"';
X	    b += Strcpy(b, name);
X	    if (p && (*p == ',' || *p == '<'))
X		*b++ = '"';
X	    *b++ = ' ', *b++ = '<';
X	}
X	b += Strcpy(b, addr);
X	if (name[0])
X	    *b++ = '>', *b = 0;
X    } else
X	b += Strcpy(buf, p);
X
X    /*
X     * if `all' is true, append everyone on the "To:" line(s).
X     * cc_to(), called separately, will catch the cc's
X     */
X    if (all) {
X	int lim = HDRSIZ - (b - buf) - 2;
X	/* Check for overflow on each copy.
X	 * The assumption that HDRSIZ is correct is unwise, but I know it
X	 * to be true for Mush.  Be forewarned if you call this routine.
X	 */
X	if (lim > 0 && (p = header_field(n, "resent-to")) && *p) {
X	    *b++ = ',', *b++ = ' ';
X	    p[lim] = '\0'; /* prevent overflow */
X	    b += Strcpy(b, p);
X	    lim = HDRSIZ - (b - buf) - 2;
X	}
X	if (lim > 0 && (p = header_field(n, "to")) && *p) {
X	    *b++ = ',', *b++ = ' ';
X	    p[lim] = '\0'; /* prevent overflow */
X	    b += Strcpy(b, p);
X	    lim = HDRSIZ - (b - buf) - 2;
X	}
X	if (lim > 0 && (p = header_field(n, "apparently-to")) && *p) {
X	    *b++ = ',', *b++ = ' ';
X	    p[lim] = '\0'; /* prevent overflow */
X	    b += Strcpy(b, p);
X	    lim = HDRSIZ - (b - buf) - 2;
X	}
X	/* Also append the Resent-From address if there is one. */
X	if (lim > 0 && (p = header_field(n, "resent-from")) && *p) {
SHAR_EOF
true || echo 'restore of hdrs.c failed'
fi
echo 'End of  part 9'
echo 'File hdrs.c is continued in part 10'
echo 10 > _shar_seq_.tmp
exit 0
exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@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@uunet.uu.net.