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.