rsalz@uunet.uu.net (Rich Salz) (04/14/89)
Submitted-by: dsinc!syd@uunet.UU.NET (Syd Weinstein) Posting-number: Volume 18, Issue 99 Archive-name: elm2.2/part20 # this is part 20 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file src/save_opts.c continued # CurArch=20 if test ! -r s2_seq_.tmp then echo "Please unpack part 1 first!" exit 1; fi ( read Scheck if test "$Scheck" != $CurArch then echo "Please unpack part $Scheck next!" exit 1; else exit 0; fi ) < s2_seq_.tmp || exit 1 echo "x - Continuing file src/save_opts.c" sed 's/^X//' << 'SHAR_EOF' >> src/save_opts.c X * Bug reports, patches, comments, suggestions should be sent to: X * X * Syd Weinstein, Elm Coordinator X * elm@dsinc.UUCP dsinc!elm X * X ******************************************************************************* X * $Log: save_opts.c,v $ X * Revision 2.17 89/03/25 21:47:09 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** This file contains the routine needed to allow the users to change the X Elm parameters and then save the configuration in a ".elm/elmrc" file in X their home directory. With any luck this will allow them never to have X to actually EDIT the file!! X X**/ X X#include "headers.h" X#include <errno.h> X X#undef onoff X#define onoff(n) (n == 1? "ON":"OFF") X X#define absolute(x) ((x) < 0? -(x) : (x)) X Xextern int errno; Xextern char version_buff[]; X Xchar *error_name(), *sort_name(); Xlong ftell(); X X#include "save_opts.h" X XFILE *elminfo; /* informational file as needed... */ X Xsave_options() X{ X /** Save the options currently specified to a file. This is a X fairly complex routine since it tries to put in meaningful X comments and such as it goes along. The comments are X extracted from the file ELMRC_INFO as defined in the sysdefs.h X file. THAT file has the format; X X varname X <comment> X <comment> X <blank line> X X and each comment is written ABOVE the variable to be added. This X program also tries to make 'pretty' stuff like the alternatives X and such. X **/ X X FILE *newelmrc; X char oldfname[SLEN], newfname[SLEN]; X X sprintf(newfname, "%s/%s", home, elmrcfile); X sprintf(oldfname, "%s/%s", home, old_elmrcfile); X X /** first off, let's see if they already HAVE a .elm/elmrc file **/ X X save_file_stats(newfname); X if (access(newfname, ACCESS_EXISTS) != -1) { X /** YES! Copy it to the file ".old.elmrc".. **/ X if (rename(newfname, oldfname) < 0) X dprint(2, (debugfile, "Unable to rename %s to %s\n", X newfname, oldfname)); X (void) chown(oldfname, userid, groupid); X X } X X /** now let's open the datafile if we can... **/ X X if ((elminfo = fopen(ELMRC_INFO, "r")) == NULL) X error1("Warning: saving without comments! Can't get to %s.", X ELMRC_INFO); X X /** next, open the new .elm/elmrc file... **/ X X if ((newelmrc = fopen(newfname, "w")) == NULL) { X error2("Can't save configuration! Can't write to %s [%s].", X newfname, error_name(errno)); X return; X } X X save_user_options(elminfo, newelmrc); X restore_file_stats(newfname); X X error1("Options saved in file %s.", newfname); X} X Xsave_user_options(elminfo_fd, newelmrc) XFILE *elminfo_fd, *newelmrc; X{ X /** save the information in the file. If elminfo_fd == NULL don't look X for comments! X **/ X X if (elminfo_fd != NULL) X build_offset_table(elminfo_fd); X X fprintf(newelmrc, X "#\n# .elm/elmrc - options file for the ELM mail system\n#\n"); X X if (strlen(full_username) > 0) X fprintf(newelmrc, "# Saved automatically by ELM %s for %s\n#\n\n", X version_buff, full_username); X else X fprintf(newelmrc, "# Saved automatically by ELM %s\n#\n\n", version_buff); X fprintf(newelmrc,"# For yes/no settings with ?, ON means yes, OFF means no\n\n"); X X save_option_string(CALENDAR, raw_calendar_file, newelmrc, FALSE); X save_option_string(EDITOR, raw_editor, newelmrc, FALSE); X X save_option_char(ESCAPECHAR, escape_char, newelmrc); X X save_option_string(FULLNAME, full_username, newelmrc, FALSE); X save_option_string(RECEIVEDMAIL, raw_recvdmail, newelmrc, FALSE); X save_option_string(MAILDIR, raw_folders, newelmrc, FALSE); X save_option_string(PAGER, raw_pager, newelmrc, FALSE); X save_option_string(PREFIX, prefixchars, newelmrc, TRUE); X save_option_string(PRINT, raw_printout, newelmrc, FALSE); X save_option_string(SENTMAIL, raw_sentmail, newelmrc, FALSE); X save_option_string(SHELL, raw_shell, newelmrc, FALSE); X X save_option_string(LOCALSIGNATURE, raw_local_signature, X newelmrc, FALSE); X save_option_string(REMOTESIGNATURE, raw_remote_signature, X newelmrc, FALSE); X X save_option_sort(SORTBY, newelmrc); X X save_option_on_off(ALWAYSDELETE, always_del, newelmrc); X save_option_on_off(ALWAYSSTORE, always_store, newelmrc); X save_option_on_off(ALWAYSKEEP, always_keep, newelmrc); X save_option_on_off(ARROW, arrow_cursor, newelmrc); X save_option_on_off(ASK, question_me, newelmrc); X save_option_on_off(ASKCC, prompt_for_cc, newelmrc); X save_option_on_off(AUTOCOPY, auto_copy, newelmrc); X X save_option_number(BOUNCEBACK, bounceback, newelmrc); X X save_option_on_off(COPY, auto_cc, newelmrc); X save_option_on_off(FORMS, (allow_forms != NO), newelmrc); X save_option_on_off(KEEPEMPTY, keep_empty_files, newelmrc); X save_option_on_off(KEYPAD, hp_terminal, newelmrc); X save_option_on_off(MENU, mini_menu, newelmrc); X save_option_on_off(MOVEPAGE, move_when_paged, newelmrc); X save_option_on_off(NAMES, names_only, newelmrc); X save_option_on_off(NOHEADER, noheader, newelmrc); X save_option_on_off(POINTNEW, point_to_new, newelmrc); X save_option_on_off(PROMPTAFTER, prompt_after_pager, newelmrc); X save_option_on_off(RESOLVE, resolve_mode, newelmrc); X save_option_on_off(SAVENAME, save_by_name, newelmrc); X save_option_on_off(SOFTKEYS, hp_softkeys, newelmrc); X X save_option_number(TIMEOUT, (int) timeout, newelmrc); X X save_option_on_off(TITLES, title_messages, newelmrc); X X save_option_number(USERLEVEL, user_level, newelmrc); X X save_option_on_off(WARNINGS, warnings, newelmrc); X save_option_on_off(WEED, filter, newelmrc); X X save_option_weedlist(WEEDOUT, newelmrc); X save_option_alternatives(ALTERNATIVES, alternative_addresses, newelmrc); X X fclose(newelmrc); X if ( elminfo_fd != NULL ) { X fclose(elminfo_fd); X } X} X Xsave_option_string(iindex, value, fd, underscores) Xint iindex, underscores; Xchar *value; XFILE *fd; X{ X /** Save a string option to the file... only subtlety is when we X save strings with spaces in 'em - translate to underscores! X **/ X X register int i; X char buffer[SLEN]; X X add_comment(iindex, fd); X X strcpy(buffer, value); X X if (underscores) X for (i=0; i < strlen(buffer); i++) X if (buffer[i] == SPACE) buffer[i] = '_'; X X fprintf(fd, "%s = %s\n\n", save_info[iindex].name, buffer); X} X Xsave_option_sort(iindex, fd) Xint iindex; XFILE *fd; X{ X /** save the current sorting option to a file **/ X X add_comment(iindex, fd); X X fprintf(fd, "%s = %s\n\n", save_info[iindex].name, X sort_name(SHORT)); X} X Xsave_option_char(iindex, value, fd) Xint iindex; Xchar value; XFILE *fd; X{ X /** Save a character option to the file **/ X X add_comment(iindex, fd); X X fprintf(fd, "%s = %c\n\n", save_info[iindex].name, value); X} X Xsave_option_number(iindex, value, fd) Xint iindex, value; XFILE *fd; X{ X /** Save a binary option to the file - boy is THIS easy!! **/ X X add_comment(iindex, fd); X X fprintf(fd, "%s = %d\n\n", save_info[iindex].name, value); X} X Xsave_option_on_off(iindex, value, fd) Xint iindex, value; XFILE *fd; X{ X /** Save a binary option to the file - boy is THIS easy!! **/ X X add_comment(iindex, fd); X X fprintf(fd, "%s = %s\n\n", save_info[iindex].name, onoff(value)); X} X Xsave_option_weedlist(iindex, fd) Xint iindex; XFILE *fd; X{ X /** save a list of weedout headers to the file **/ X X int length_so_far = 0, i; X X add_comment(iindex, fd); X X length_so_far = strlen(save_info[iindex].name) + 4; X X fprintf(fd, "%s = ", save_info[iindex].name); X X /** first off, skip till we get past the default list **/ X X for (i = 0; i < weedcount; i++) X if (strcmp(weedlist[i],"*end-of-defaults*") == 0) X break; X X while (i < weedcount) { X if (strcmp(weedlist[i], "*end-of-defaults*") != 0) X break; X i++; /* and get PAST it too! */ X } X X while (i < weedcount) { X if (strlen(weedlist[i]) + length_so_far > 78) { X fprintf(fd, "\n\t"); X length_so_far = 8; X } X fprintf(fd, "\"%s\" ", weedlist[i]); X length_so_far += (strlen(weedlist[i]) + 4); X i++; X } X fprintf(fd, "\t\"*end-of-user-headers*\"\n\n"); X} X Xsave_option_alternatives(iindex, list, fd) Xint iindex; Xstruct addr_rec *list; XFILE *fd; X{ X /** save a list of options to the file **/ X int length_so_far = 0; X struct addr_rec *alternate; X X if (list == NULL) return; /* nothing to do! */ X X add_comment(iindex, fd); X X alternate = list; /* don't LOSE the top!! */ X X length_so_far = strlen(save_info[iindex].name) + 4; X X fprintf(fd, "%s = ", save_info[iindex].name); X X while (alternate != NULL) { X if (strlen(alternate->address) + length_so_far > 78) { X fprintf(fd, "\n\t"); X length_so_far = 8; X } X fprintf(fd, "%s ", alternate->address); X length_so_far += (strlen(alternate->address) + 3); X alternate = alternate->next; X } X fprintf(fd, "\n\n"); X} X Xadd_comment(iindex, fd) Xint iindex; XFILE *fd; X{ X /** get to and add the comment to the file **/ X char buffer[SLEN]; X X /** first off, add the comment from the comment file, if available **/ X X if (save_info[iindex].offset > 0L) { X if (fseek(elminfo, save_info[iindex].offset, 0) == -1) { X dprint(1,(debugfile, X "** error %s seeking to %ld in elm-info file!\n", X error_name(errno), save_info[iindex].offset)); X } X else while (fgets(buffer, SLEN, elminfo) != NULL) { X if (buffer[0] != '#') X break; X else X fprintf(fd, "%s", buffer); X } X } X} X Xbuild_offset_table(elminfo_fd) XFILE *elminfo_fd; X{ X /** read in the info file and build the table of offsets. X This is a rather laborious puppy, but at least we can X do a binary search through the array for each element and X then we have it all at once! X **/ X X char line_buffer[SLEN]; X X while (fgets(line_buffer, SLEN, elminfo_fd) != NULL) { X if (strlen(line_buffer) > 1) X if (line_buffer[0] != '#' && !whitespace(line_buffer[0])) { X no_ret(line_buffer); X if (find_and_store_loc(line_buffer, ftell(elminfo_fd))) { X dprint(1, (debugfile,"** Couldn't find and store \"%s\" **\n", X line_buffer)); X } X } X } X} X Xfind_and_store_loc(name, offset) Xchar *name; Xlong offset; X{ X /** given the name and offset, find it in the table and store it **/ X X int first = 0, last, middle, compare; X X last = NUMBER_OF_SAVEABLE_OPTIONS; X X while (first <= last) { X X middle = (first+last) / 2; X X if ((compare = strcmp(name, save_info[middle].name)) < 0) /* a < b */ X last = middle - 1; X else if (compare == 0) { /* a = b */ X save_info[middle].offset = offset; X return(0); X } X else /* greater */ /* a > b */ X first = middle + 1; X } X X return(-1); X} SHAR_EOF echo "File src/save_opts.c is complete" chmod 0444 src/save_opts.c || echo "restore of src/save_opts.c fails" echo "x - extracting src/savecopy.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/savecopy.c && X Xstatic char rcsid[] = "@(#)$Id: savecopy.c,v 2.16 89/03/25 21:47:11 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.16 $ $State: Exp $ X * X * Copyright (c) 1986, 1987 Dave Taylor X * Copyright (c) 1988, 1989 USENET Community Trust X ******************************************************************************* X * Bug reports, patches, comments, suggestions should be sent to: X * X * Syd Weinstein, Elm Coordinator X * elm@dsinc.UUCP dsinc!elm X * X ******************************************************************************* X * $Log: savecopy.c,v $ X * Revision 2.16 89/03/25 21:47:11 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** Save a copy of the specified message in a folder. X X**/ X X#include "headers.h" X#ifdef TMINSYS X# include <sys/time.h> X#else X# include <time.h> X#endif X X#include <errno.h> X Xchar *format_long(); Xchar *error_name(), *error_description(); Xchar *ctime(); X Xextern char in_reply_to[SLEN]; /* In-Reply-To: string */ Xextern int errno; X Xchar *strcat(), *strcpy(); Xunsigned long sleep(); Xlong time(); X Xsave_copy(to, cc, bcc, filename, copy_file, form) Xchar *to, *cc, *bcc, *filename, *copy_file; Xint form; X{ X /** This routine appends a copy of the outgoing message to the X file specified. **/ X X FILE *save, /* file id for file to save to */ X *message, /* file id for file with message body */ X *write_header_info(); X char buffer[SLEN], /* read buffer */ X savename[SLEN]; /* name of file saving into */ X static char helpmsg[LONG_STRING]; X X X /* presume copy_file is okay as is for now */ X strcpy(savename, copy_file); X X /* if save-by-name wanted */ X if(strcmp(copy_file, "=") == 0) { X X get_return_name(to, buffer, TRUE); /* determine 'to' login */ X if (strlen(buffer) == 0) { X X /* can't get file name from 'to' -- use sent_mail instead */ X dprint(3, (debugfile, X "Warning: get_return_name couldn't break down %s\n", to)); X error1( X"Cannot determine `to' name to save by! Saving to \"sent\" folder %s instead.", X sent_mail); X strcpy(savename, "<"); X sleep(3); X } else X sprintf(savename, "=%s", buffer); /* good! */ X } X X expand_filename(savename, TRUE); /* expand special chars */ X X if ((errno = can_open(savename, "a"))) { X dprint(2, (debugfile, X "Error: attempt to autosave to a file that can't be appended to!\n")); X dprint(2, (debugfile, "\tfilename = \"%s\"\n", savename)); X dprint(2, (debugfile, "** %s - %s **\n", error_name(errno), X error_description(errno))); X X /* Lets try sent_mail before giving up */ X if(strcmp(sent_mail, savename) == 0) { X /* we are ALREADY using sent_mail! */ X error1("Cannot save to %s!", savename); X sleep(3); X return(FALSE); X } X X if ((errno = can_open(sent_mail, "a"))) { X dprint(2, (debugfile, X "Error: attempt to autosave to a file that can't be appended to!\n")); X dprint(2, (debugfile, "\tfilename = \"%s\"\n", sent_mail)); X dprint(2, (debugfile, "** %s - %s **\n", error_name(errno), X error_description(errno))); X error2("Cannot save to %s nor to \"sent\" folder %s!", X savename, sent_mail); X sleep(3); X return(FALSE); X } X error2("Cannot save to %s! Saving to \"sent\" folder %s instead.", X savename, sent_mail); X sleep(3); X strcpy(savename, sent_mail); X } X X save_file_stats(savename); X X /* Write header */ X if ((save = write_header_info(savename, to, cc, bcc, X form == YES, TRUE)) == NULL) X return(FALSE); X X /* Now add file with message as handed to mailer */ X if ((message = fopen(filename, "r")) == NULL) { X fclose(save); X dprint(1, (debugfile, X "Error: Couldn't read folder %s (save_copy)\n", filename)); X dprint(1, (debugfile, "** %s - %s **\n", error_name(errno), X error_description(errno))); X error1("Couldn't read folder %s!", filename); X sleep(3); X return(FALSE); X } X X copy_message_across(message, save, TRUE); X X fclose(save); X fclose(message); X X restore_file_stats(savename); X X return(TRUE); X} Xchar * Xcf_english(fn) Xchar *fn; X{ X /** Return "English" expansion for special copy file name abbreviations X or just the file name **/ X X if(!*fn) X return("<no save>"); X else if(!fn[1]) { X if(*fn == '=') X return("<save by name>"); X else if(*fn == '<') X return("<\"sent\" folder>"); X } X return(fn); X} X X#define NCF_PROMPT "Save copy in (use '?' for help/to list folders): " Xint Xname_copy_file(fn) Xchar *fn; X{ X /** Prompt user for name of file for saving copy of outbound msg to. X Return true if we need a redraw. **/ X X int redraw = 0; /* set when we ask for help = need redraw */ X char buffer[SLEN], origbuffer[SLEN]; X static char helpmsg[LONG_STRING]; X X /* expand passed copy file name into English */ X strcpy(buffer, cf_english(fn)); X X /* prepare screen with instructions */ X MoveCursor(LINES-2, 0); X CleartoEOS(); X PutLine0(LINES-2, 0, NCF_PROMPT); X X while(1) { X X /* get file name from user input */ X strcpy(origbuffer, buffer); X optionally_enter(buffer, LINES-2, strlen(NCF_PROMPT), FALSE, FALSE); X X if(strcmp(buffer, "?") != 0) { /* got what we wanted - non-help choice */ X X if(strcmp(origbuffer, buffer) != 0) X /* user changed from our English expansion X * so we'd better copy user input to fn X */ X strcpy(fn, buffer); X X /* else user presumably left our English expansion - no change in fn */ X X /* display English expansion of new user input a while */ X PutLine1(LINES-2, strlen(NCF_PROMPT), cf_english(fn)); X MoveCursor(LINES, 0); X sleep(1); X MoveCursor(LINES-2, 0); X CleartoEOS(); X X return(redraw); X } X X /* give help and list folders */ X redraw = TRUE; X if(!*helpmsg) /* help message not yet formulated */ X sprintf(helpmsg, "%s%s%s%s%s%s%s%s", X "\n\r\n\rEnter: <nothing> to not save a copy of the message,\n\r", X " '=' to save by name", X " (depends upon whom the message is to, in the end),\n\r", X " '<' to save in your \"sent\" folder (", sent_mail, "),\n\r", X " or a filename", X " (leading '=' denotes your folder directory).\n\r"); X list_folders(4, helpmsg); X PutLine0(LINES-2, 0, NCF_PROMPT); X X /* restore as default to English version of the passed copy file name */ X strcpy(buffer, cf_english(fn)); X X } X} SHAR_EOF chmod 0444 src/savecopy.c || echo "restore of src/savecopy.c fails" echo "x - extracting src/screen.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/screen.c && X Xstatic char rcsid[] = "@(#)$Id: screen.c,v 2.22 89/03/25 21:47:17 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.22 $ $State: Exp $ X * X * Copyright (c) 1986, 1987 Dave Taylor X * Copyright (c) 1988, 1989 USENET Community Trust X ******************************************************************************* X * Bug reports, patches, comments, suggestions should be sent to: X * X * Syd Weinstein, Elm Coordinator X * elm@dsinc.UUCP dsinc!elm X * X ******************************************************************************* X * $Log: screen.c,v $ X * Revision 2.22 89/03/25 21:47:17 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** screen display routines for ELM program X X**/ X X#include "headers.h" X X#define minimum(a,b) ((a) < (b) ? (a) : (b)) X Xstatic int last_current = -1; X Xchar *strcpy(), *strncpy(), *nameof(), *show_status(); X Xextern char version_buff[]; X Xshowscreen() X{ X X ClearScreen(); X X update_title(); X X last_header_page = -1; /* force a redraw regardless */ X show_headers(); X X if (mini_menu) X show_menu(); X X show_last_error(); X X if (hp_terminal) X define_softkeys(MAIN); X} X Xupdate_title() X{ X /** display a new title line, probably due to new mail arriving **/ X X char buffer[SLEN]; X X if (selected) X sprintf(buffer, X "%s is '%s' with %d shown out of %d [ELM %s]", X (folder_type == SPOOL ? "Mailbox" : "Folder"), X nameof(cur_folder), selected, message_count, version_buff); X else X sprintf(buffer, "%s is '%s' with %d message%s [ELM %s]", X (folder_type == SPOOL ? "Mailbox" : "Folder"), X nameof(cur_folder), message_count, X plural(message_count), version_buff); X X ClearLine(1); X X Centerline(1, buffer); X} X Xshow_menu() X{ X /** write main system menu... **/ X X if (user_level == 0) { /* a rank beginner. Give less options */ X Centerline(LINES-7, X "You can use any of the following commands by pressing the first character;"); X Centerline(LINES-6, X"d)elete or u)ndelete mail, m)ail a message, r)eply or f)orward mail, q)uit"); X Centerline(LINES-5, X "To read a message, press <return>. j = move down, k = move up, ? = help"); X } else { X Centerline(LINES-7, X "|=pipe, !=shell, ?=help, <n>=set current to n, /=search pattern"); X Centerline(LINES-6, X"a)lias, C)opy, c)hange folder, d)elete, e)dit, f)orward, g)roup reply, m)ail," X); X Centerline(LINES-5, X "n)ext, o)ptions, p)rint, r)eply, s)ave, t)ag, q)uit, u)ndelete, or e(x)it"); X } X} X Xint Xshow_headers() X{ X /** Display page of headers (10) if present. First check to X ensure that header_page is in bounds, fixing silently if not. X If out of bounds, return zero, else return non-zero X Modified to only show headers that are "visible" to ze human X person using ze program, eh? X **/ X X register int this_msg = 0, line = 4, last = 0, last_line, X displayed = 0, using_to; X char newfrom[SLEN], buffer[SLEN]; X X if (fix_header_page()) X return(FALSE); X X if (selected) { X if ((header_page*headers_per_page) > selected) X return(FALSE); /* too far! too far! */ X X this_msg = visible_to_index(header_page * headers_per_page + 1); X displayed = header_page * headers_per_page; X X last = displayed+headers_per_page; X X } X else { X if (header_page == last_header_page) /* nothing to do! */ X return(FALSE); X X /** compute last header to display **/ X X this_msg = header_page * headers_per_page; X last = this_msg + (headers_per_page - 1); X } X X if (last >= message_count) last = message_count-1; X X /** Okay, now let's show the header page! **/ X X ClearLine(line); /* Clear the top line... */ X X MoveCursor(line, 0); /* and move back to the top of the page... */ X X while ((selected && displayed < last) || this_msg <= last) { X using_to = tail_of(headers[this_msg]->from, newfrom, X headers[this_msg]->to); X X if (this_msg == current-1) X build_header_line(buffer, headers[this_msg], this_msg+1, X TRUE, newfrom, using_to); X else X build_header_line(buffer, headers[this_msg], X this_msg+1, FALSE, newfrom, using_to); X if (selected) X displayed++; X X if (this_msg == current-1 && has_highlighting && ! arrow_cursor) { X StartInverse(); X Write_to_screen("%s\n\r", 1, buffer); /* avoid '%' probs */ X EndInverse(); X } else X Write_to_screen("%s\n\r", 1, buffer); /* avoid '%' probs */ X CleartoEOLN(); X line++; /* for clearing up in a sec... */ X X if (selected) { X if ((this_msg = next_message(this_msg, FALSE)) < 0) X break; /* GET OUTTA HERE! */ X X /* the preceeding looks gross because we're using an INDEX X variable to pretend to be a "current" counter, and the X current counter is always 1 greater than the actual X index. Does that make sense?? X */ X } X else X this_msg++; /* even dumber... */ X } X X /* clear unused lines */ X X if (mini_menu) X last_line = LINES-8; X else X last_line = LINES-4; X X while (line < last_line) { X CleartoEOLN(); X NewLine(); X line++; X } X X display_central_message(); X X last_current = current; X last_header_page = header_page; X X return(TRUE); X} X Xshow_current() X{ X /** Show the new header, with all the usual checks **/ X X register int first = 0, last = 0, last_line, new_line, using_to; X char newfrom[SLEN], old_buffer[SLEN], new_buffer[SLEN]; X X (void) fix_header_page(); /* Who cares what it does? ;-) */ X X /** compute the first and last header on this page **/ X first = header_page * headers_per_page + 1; X last = first + (headers_per_page - 1); X X /* if not a full page adjust last to be the real last */ X if (selected && last > selected) X last = selected; X if (!selected && last > message_count) X last = message_count; X X /** okay, now let's show the pointers... **/ X X /** have we changed??? **/ X if (current == last_current) X return; X X if (selected) { X last_line = ((compute_visible(last_current)-1) % X headers_per_page)+4; X new_line = ((compute_visible(current)-1) % headers_per_page)+4; X } else { X last_line = ((last_current-1) % headers_per_page)+4; X new_line = ((current-1) % headers_per_page)+4; X } X X if (has_highlighting && ! arrow_cursor) { X X using_to = tail_of(headers[current-1]->from, newfrom, X headers[current-1]->to); X build_header_line(new_buffer, headers[current-1], current, X TRUE, newfrom, using_to); X X /* clear last current if it's in proper range */ X if (last_current > 0 /* not a dummy value */ X && compute_visible(last_current) <= last X && compute_visible(last_current) >= first) { X X dprint(5, (debugfile, X "\nlast_current = %d ... clearing [1] before we add [2]\n", X last_current)); X dprint(5, (debugfile, "first = %d, and last = %d\n\n", X first, last)); X X using_to = tail_of(headers[last_current-1]->from, newfrom, X headers[last_current-1]->to); X build_header_line(old_buffer, headers[last_current-1], X last_current, FALSE, newfrom, using_to); X X ClearLine(last_line); X PutLine0(last_line, 0, old_buffer); X } X MoveCursor(new_line, 0); X StartInverse(); X Write_to_screen("%s", 1, new_buffer); X EndInverse(); X } X else { X if (on_page(last_current-1)) X PutLine0(last_line,0," "); /* remove old pointer... */ X if (on_page(current-1)) X PutLine0(new_line, 0,"->"); X } X X last_current = current; X} X Xbuild_header_line(buffer, entry, message_number, highlight, from, really_to) Xchar *buffer; Xstruct header_rec *entry; Xint message_number, highlight, really_to; Xchar *from; X{ Xchar *strchr(); X X /** Build in buffer the message header ... entry is the current X message entry, 'from' is a modified (displayable) from line, X 'highlight' is either TRUE or FALSE, and 'message_number' X is the number of the message. X **/ X X /** Note: using 'strncpy' allows us to output as much of the X subject line as possible given the dimensions of the screen. X The key is that 'strncpy' returns a 'char *' to the string X that it is handing to the dummy variable! Neat, eh? **/ X X int who_width = 18, subj_width; X char *dot = strchr(from, '.'); X char *bang = strchr(from, '!'); X X /* truncate 'from' to 18 characters - X * this includes the leading "To" if really_to is true. X * Note: X * 'from' is going to be of three forms X * - full name (truncate on the right for readability) X * - logname@machine (truncate on the right to preserve X * logname over machine name X * - machine!logname -- a more complex situation X * If this form doesn't fit, either machine X * or logname are long. If logname is long, X * we can stand to loose part of it, so we X * truncate on the right. If machine name is X * long, we'd better truncate on the left, X * to insure we get the logname. Now if the X * machine name is long, it will have "." in X * it. X * Therfore, we truncate on the left if there is a "." and a "!" X * in 'from', else we truncate on the right. X */ X X /* Note that one huge sprintf() is too hard for some compilers. */ X sprintf(buffer, "%s%s%c%-3d %3.3s %-2d ", X (highlight && arrow_cursor)? "->" : " ", X show_status(entry->status), X (entry->status & TAGGED? '+' : ' '), X message_number, X entry->month, X atoi(entry->day)); X X /* show "To " in a way that it can never be truncated. */ X if (really_to) { X strcat(buffer, "To "); X who_width -= 3; X } X X /* truncate 'from' on left if needed. X * sprintf will truncate on right afterward if needed. */ X if ((strlen(from) > who_width) && dot && bang && (dot < bang)) { X from += (strlen(from) - who_width); X } X X /* Set the subject display width. X * If it is too long, truncate it to fit. X * If it is highlighted but not with the arrow cursor, X * expand it to fit so that the reverse video bar extends X * aesthetically the full length of the line. X */ X if ((highlight && !arrow_cursor) X || (COLUMNS-44 < (subj_width =strlen(entry->subject)))) X subj_width = COLUMNS-44; X X /* complete line with sender, length and subject. */ X sprintf(buffer + strlen(buffer), "%-*.*s (%d) %s%-*.*s", X /* give max and min width parameters for 'from' */ X who_width, X who_width, X from, X X entry->lines, X (entry->lines / 1000 > 0? "" : /* spacing the */ X entry->lines / 100 > 0? " " : /* same for the */ X entry->lines / 10 > 0? " " : /* lines in () */ X " "), /* [wierd] */ X X subj_width, subj_width, entry->subject); X} X Xint Xfix_header_page() X{ X /** this routine will check and ensure that the current header X page being displayed contains messages! It will silently X fix 'header-page' if wrong. Returns TRUE if changed. **/ X X int last_page, old_header; X X old_header = header_page; X X last_page = (int) ((message_count-1) / headers_per_page); X X if (header_page > last_page) X header_page = last_page; X else if (header_page < 0) X header_page = 0; X X return(old_header != header_page); X} X Xint Xon_page(message) Xint message; X{ X /** Returns true iff the specified message is on the displayed page. **/ X X if (selected) message = compute_visible(message); X X if (message >= header_page * headers_per_page) X if (message < ((header_page+1) * headers_per_page)) X return(TRUE); X X return(FALSE); X} X Xchar *show_status(status) Xint status; X{ X /** This routine returns a pair of characters indicative of X the status of this message. The first character represents X the interim status of the message (e.g. the status within X the mail system): X X E = Expired message X N = New message X O = Unread old message dsi mailx emulation addition X D = Deleted message X _ = (space) default X X and the second represents the permanent attributes of the X message: X X C = Company Confidential message X U = Urgent (or Priority) message X P = Private message X A = Action associated with message X F = Form letter X _ = (space) default X **/ X X static char mybuffer[3]; X X /** the first character, please **/ X X if (status & DELETED) mybuffer[0] = 'D'; X else if (status & EXPIRED) mybuffer[0] = 'E'; X else if (status & NEW) mybuffer[0] = 'N'; X else if (status & UNREAD) mybuffer[0] = 'O'; X else mybuffer[0] = ' '; X X /** and the second... **/ X X if (status & CONFIDENTIAL) mybuffer[1] = 'C'; X else if (status & URGENT) mybuffer[1] = 'U'; X else if (status & PRIVATE) mybuffer[1] = 'P'; X else if (status & ACTION) mybuffer[1] = 'A'; X else if (status & FORM_LETTER) mybuffer[1] = 'F'; X else mybuffer[1] = ' '; X X mybuffer[2] = '\0'; X X return( (char *) mybuffer); X} SHAR_EOF chmod 0444 src/screen.c || echo "restore of src/screen.c fails" echo "x - extracting src/showmsg.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/showmsg.c && X Xstatic char rcsid[] = "@(#)$Id: showmsg.c,v 2.21 89/03/25 21:47:19 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.21 $ $State: Exp $ X * X * Copyright (c) 1986, 1987 Dave Taylor X * Copyright (c) 1988, 1989 USENET Community Trust X ******************************************************************************* X * Bug reports, patches, comments, suggestions should be sent to: X * X * Syd Weinstein, Elm Coordinator X * elm@dsinc.UUCP dsinc!elm X * X ******************************************************************************* X * $Log: showmsg.c,v $ X * Revision 2.21 89/03/25 21:47:19 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** This file contains all the routines needed to display the specified X message. X**/ X X#include "headers.h" X#include <ctype.h> X#include <errno.h> X X#ifdef BSD X# include <sys/wait.h> X# undef tolower X#endif X Xextern int errno; X Xchar *error_name(), *strcat(), *strcpy(); Xvoid _exit(); X Xint memory_lock = FALSE; /* is it available?? */ Xint pipe_abort = FALSE; /* did we receive a SIGNAL(SIGPIPE)? */ X XFILE *pipe_wr_fp; /* file pointer to write to external pager */ Xextern int lines_displayed, /* defined in "builtin" */ X lines_put_on_screen; /* ditto too! */ X Xint Xshow_msg(number) Xint number; X{ X /*** Display number'th message. Get starting and ending lines X of message from headers data structure, then fly through X the file, displaying only those lines that are between the X two! X X Return 0 to return to the index screen or a character entered X by the user to initiate a command without returning to X the index screen (to be processed via process_showmsg_cmd()). X ***/ X X char title1[SLEN], title2[SLEN], title3[SLEN], titlebuf[SLEN]; X char who[LONG_STRING], buffer[VERY_LONG_STRING]; X#ifdef BSD X union wait wait_stat; X#else X int wait_stat; X#endif X X int crypted = 0; /* encryption */ X int weed_header, weeding_out = 0; /* weeding */ X int using_to, /* misc use */ X pipe_fd[2], /* pipe file descriptors */ X new_pipe_fd, /* dup'ed pipe fil des */ X lines, /* num lines in msg */ X fork_ret, /* fork return value */ X wait_ret, /* wait return value */ X form_letter = FALSE, /* Form ltr? */ X form_letter_section = 0, /* section */ X padding = 0, /* counter */ X builtin = FALSE, /* our pager? */ X val = 0, /* return val */ X buf_len, /* line length */ X whole_line; /* whole line */ X struct header_rec *current_header = headers[number-1]; X X X lines = current_header->lines; X X dprint(4, (debugfile,"displaying %d lines from message %d using %s\n", X lines, number, pager)); X X if (number > message_count || number < 1) X return(val); X X if(ison(current_header->status, NEW)) { X clearit(current_header->status, NEW); /* it's been read now! */ X current_header->status_chgd = TRUE; X } X if(ison(current_header->status, UNREAD)) { X clearit(current_header->status, UNREAD); /* it's been read now! */ X current_header->status_chgd = TRUE; X } X X memory_lock = FALSE; X X /* some explanation for that last one - We COULD use memory locking X to speed up the paging, but the action of "ClearScreen" on a screen X with memory lock turned on seems to vary considerably (amazingly so) X so it's safer to only allow memory lock to be a viable bit of X trickery when dumping text to the screen in scroll mode. X Philosophical arguments should be forwarded to Bruce at the X University of Walamazoo, Australia, via ACSNet *wry chuckle* */ X X if (fseek(mailfile, current_header->offset, 0) == -1) { X dprint(1, (debugfile, X "Error: seek %d bytes into file, errno %s (show_message)\n", X current_header->offset, error_name(errno))); X error2("ELM failed seeking %d bytes into file (%s).", X current_header->offset, error_name(errno)); X return(val); X } X if(current_header->encrypted) X getkey(OFF); X X if(builtin=(first_word(pager,"builtin")||first_word(pager,"internal"))) X X start_builtin(lines); X X else { X X /* put terminal out of raw mode so external pager has normal env */ X Raw(OFF); X X /* create pipe for external pager and fork */ X X if(pipe(pipe_fd) == -1) { X dprint(1, (debugfile, "Error: pipe failed, errno %s (show_msg)\n", X error_name(errno))); X error1("Could not prepare for external pager(pipe()-%s).", X error_name(errno)); X Raw(ON); X return(val); X } X X if((fork_ret = fork()) == -1) { X X dprint(1, (debugfile, "Error: fork failed, errno %s (show_msg)\n", X error_name(errno))); X error1("Could not prepare for external pager(fork()-%s).", X error_name(errno)); X Raw(ON); X return(val); X X } else if (fork_ret == 0) { X X /* child fork */ X X /* close write-only pipe fd and fit read-only pipe fd to stdin */ X X close(pipe_fd[1]); X close(fileno(stdin)); X if((new_pipe_fd = dup(pipe_fd[0])) == -1) { X dprint(1, (debugfile, "Error: dup failed, errno %s (show_msg)\n", X error_name(errno))); X error1("Could not prepare for external pager(dup()-%s).", X error_name(errno)); X _exit(errno); X } X close(pipe_fd[0]); /* original pipe fd no longer needed */ X X /* use stdio on new pipe fd */ X if(fdopen(new_pipe_fd, "r") == NULL) { X dprint(1, X (debugfile, "Error: child fdopen failed, errno %s (show_msg)\n", X error_name(errno))); X error1("Could not prepare for external pager(child fdopen()-%s).", X error_name(errno)); X _exit(errno); X } X X /* now execute pager and exit */ X X /* system_call() will return user to user's normal permissions. X * This is what makes this pipe secure - user won't have elm's X * special setgid permissions (if so configured) and will only X * be able to execute a pager that user normally has permission X * to execute */ X X _exit(system_call(pager, SH, TRUE)); X X } /* else this is the parent fork */ X X /* close read-only pipe fd and do write-only with stdio */ X close(pipe_fd[0]); X X if((pipe_wr_fp = fdopen(pipe_fd[1], "w")) == NULL) { X dprint(1, X (debugfile, "Error: parent fdopen failed, errno %s (show_msg)\n", X error_name(errno))); X error1("Could not prepare for external pager(parent fdopen()-%s).", X error_name(errno)); X X /* Failure - must close pipe and wait for child */ X close(pipe_fd[1]); X while ((wait_ret = wait(&wait_stat)) != fork_ret && wait_ret!= -1) X ; X X Raw(OFF); X return(val); /* pager may have already touched the screen */ X } X X /* and that's it! */ X lines_displayed = 0; X } X X ClearScreen(); X X if (cursor_control) transmit_functions(OFF); X X pipe_abort = FALSE; X X if (form_letter = (current_header->status&FORM_LETTER)) { X if (filter) X form_letter_section = 1; /* initialize to section 1 */ X } X X if (title_messages && filter) { X X using_to = X tail_of(current_header->from, who, current_header->to); X X sprintf(title1, "%s %d/%d ", X headers[current-1]->status & DELETED ? "[deleted]" : X form_letter ? "Form": "Message", X number, message_count); X sprintf(title2, "%s %s", using_to? "To" : "From", who); X sprintf(title3, " %s %s '%d at %s", X current_header->month, current_header->day, X atoi(current_header->year), current_header->time); X X /* truncate or pad title2 portion on the right X * so that line fits exactly */ X padding = X COLUMNS - X (strlen(title1) + (buf_len=strlen(title2)) + strlen(title3)); X X sprintf(titlebuf, "%s%-*.*s%s\n", title1, buf_len+padding, X buf_len+padding, title2, title3); X X if (builtin) X display_line(titlebuf); X else X fprintf(pipe_wr_fp, "%s", titlebuf); X X /** if there's a subject, let's output it next, X centered if it fits on a single line. **/ X X if ((buf_len = strlen(current_header->subject)) > 0 && X matches_weedlist("Subject:")) { X padding = (buf_len < COLUMNS ? COLUMNS - buf_len : 0); X sprintf(buffer, "%*s%s\n", padding/2, "", current_header->subject); X } else X strcpy(buffer, "\n"); X X if (builtin) X display_line(buffer); X else X fprintf(pipe_wr_fp, "%s", buffer); X X /** was this message address to us? if not, then to whom? **/ X X if (! using_to && matches_weedlist("To:") && filter && X strcmp(current_header->to,username) != 0 && X strlen(current_header->to) > 0) { X if (strlen(current_header->to) > 60) X sprintf(buffer, "%s(message addressed to %.60s)\n", X strlen(current_header->subject) > 0 ? "\n" : "", X current_header->to); X else X sprintf(buffer, "%s(message addressed to %s)\n", X strlen(current_header->subject) > 0 ? "\n" : "", X current_header->to); X if (builtin) X display_line(buffer); X else X fprintf(pipe_wr_fp, "%s", buffer); X } X X /** The test above is: if we didn't originally send the mail X (e.g. we're not reading "mail.sent") AND the user is currently X weeding out the "To:" line (otherwise they'll get it twice!) X AND the user is actually weeding out headers AND the message X wasn't addressed to the user AND the 'to' address is non-zero X (consider what happens when the message doesn't HAVE a "To:" X line...the value is NULL but it doesn't match the username X either. We don't want to display something ugly like X "(message addressed to )" which will just clutter up the X screen!). X X And you thought programming was EASY!!!! X **/ X X /** one more friendly thing - output a line indicating what sort X of status the message has (e.g. Urgent etc). Mostly added X for X.400 support, this is nonetheless generally useful to X include... X **/ X X buffer[0] = '\0'; X X /* we want to flag Urgent, Confidential, Private and Expired tags */ X X if (current_header->status & PRIVATE) X strcpy(buffer, "\n(** This message is tagged Private"); X else if (current_header->status & CONFIDENTIAL) X strcpy(buffer, "\n(** This message is tagged Company Confidential"); X X if (current_header->status & URGENT) { X if (buffer[0] == '\0') X strcpy(buffer, "\n(** This message is tagged Urgent"); X else if (current_header->status & EXPIRED) X strcat(buffer, ", Urgent"); X else X strcat(buffer, " and Urgent"); X } X X if (current_header->status & EXPIRED) { X if (buffer[0] == '\0') X strcpy(buffer, "\n(** This message has Expired"); X else X strcat(buffer, ", and has Expired"); X } X X if (buffer[0] != '\0') { X strcat(buffer, " **)\n"); X if (builtin) X display_line(buffer); X else X fprintf(pipe_wr_fp, buffer); X } X X if (builtin) /* this is for a one-line blank */ X display_line("\n"); /* separator between the title */ X else /* stuff and the actual message */ X fprintf(pipe_wr_fp, "\n"); /* we're trying to display */ X X } X X weed_header = filter; /* allow us to change it after header */ X X while (lines > 0 && pipe_abort == FALSE) { X X if (fgets(buffer, VERY_LONG_STRING, mailfile) == NULL) { X X dprint(1, (debugfile, X "Premature end of file! Lines left = %d msg = %s (show_msg)\n", X lines, number)); X X error("Premature end of file!"); X sleep(2); X break; X } X if ((buf_len=strlen(buffer)) > 0) { X if(buffer[buf_len - 1] == '\n') X whole_line = TRUE; X else X whole_line = FALSE; X no_ret(buffer); X } X X if (strlen(buffer) == 0) { X weed_header = 0; /* past header! */ X weeding_out = 0; X } X X if (form_letter && weed_header) X /* skip it. NEVER display random headers in forms! */; X else if (weed_header && matches_weedlist(buffer)) X weeding_out = 1; /* aha! We don't want to see this! */ X else if (buffer[0] == '[') { X if (strcmp(buffer, START_ENCODE)==0) X crypted = ON; X else if (strcmp(buffer, END_ENCODE)==0) X crypted = OFF; X else if (crypted) { X encode(buffer); X val = show_line(buffer, builtin); X } X else X val = show_line(buffer, builtin); X } X else if (crypted) { X encode(buffer); X val = show_line(buffer, builtin); X } X else if (weeding_out) { X weeding_out = (whitespace(buffer[0])); /* 'n' line weed */ X if (! weeding_out) /* just turned on! */ X val = show_line(buffer, builtin); X } X else if (form_letter && first_word(buffer,"***") && filter) { X strcpy(buffer, X"\n------------------------------------------------------------------------------\n"); X val = show_line(buffer, builtin); /* hide '***' */ X form_letter_section++; X } X else if (form_letter_section == 1 || form_letter_section == 3) X /** skip this stuff - we can't deal with it... **/; X else X val = show_line(buffer, builtin); X X if (val != 0) /* discontinue the display */ X break; X X if(whole_line) lines--; X lines_displayed++; X } X X if (cursor_control) transmit_functions(ON); X X if (!builtin) { X fclose(pipe_wr_fp); X while ((wait_ret = wait(&wait_stat)) != fork_ret X && wait_ret!= -1) X ; X /* turn raw on **after** child terminates in case child X * doesn't put us back to cooked mode after we return ourselves X * to raw. X */ X Raw(ON); X } X X /* If we are to prompt for a user input command and we don't X * already have one */ X if ((prompt_after_pager || builtin) && val == 0) { X MoveCursor(LINES,0); X StartBold(); X Write_to_screen(" Command ('i' to return to index): ", 0); X EndBold(); X fflush(stdout); X val = ReadCh(); X } X X if (memory_lock) EndMemlock(); /* turn it off!! */ X X /* 'q' means quit current operation and pop back up to previous level - X * in this case it therefore means return to index screen. X */ X return(val == 'i' || val == 'q' ? 0 : val); X} X Xint Xshow_line(buffer, builtin) Xchar *buffer; Xint builtin; X{ X /** Hands the given line to the output pipe. 'builtin' is true if X we're using the builtin pager. X Return the character entered by the user to indicate X a command other than continuing with the display (only possible X with the builtin pager), otherwise 0. **/ X X if (builtin) { X strcat(buffer, "\n"); X return(display_line(buffer)); X } X errno = 0; X fprintf(pipe_wr_fp, "%s\n", buffer); X if (errno != 0) X dprint(1, (debugfile, "\terror %s hit!\n", error_name(errno))); X return(0); X} SHAR_EOF chmod 0444 src/showmsg.c || echo "restore of src/showmsg.c fails" echo "x - extracting src/showmsg_c.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/showmsg_c.c && X Xstatic char rcsid[] = "@(#)$Id: showmsg_c.c,v 2.20 89/03/25 21:47:21 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.20 $ $State: Exp $ X * X * Copyright (c) 1986, 1987 Dave Taylor X * Copyright (c) 1988, 1989 USENET Community Trust X ******************************************************************************* X * Bug reports, patches, comments, suggestions should be sent to: X * X * Syd Weinstein, Elm Coordinator X * elm@dsinc.UUCP dsinc!elm X * X ******************************************************************************* X * $Log: showmsg_c.c,v $ X * Revision 2.20 89/03/25 21:47:21 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** This is an interface for the showmsg command line. The possible X functions that could be invoked from the showmsg command line are X almost as numerous as those from the main command line and include X the following; X X | = pipe this message to command... X ! = call Unix command X < = scan message for calendar info X b = bounce (remail) message X d = mark message for deletion X f = forward message X g = group reply X h = redisplay this message from line #1, showing headers X <CR> = redisplay this message from line #1, weeding out headers X i,q = move back to the index page (simply returns from function) X J = move to body of next message X j,n = move to body of next undeleted message X K = move to body of previous message X k = move to body of previous undeleted message X m = mail a message out to someone X p = print this (all tagged) message X r = reply to this message X s = save this message to a maibox/folder X t = tag this message X u = undelete message X x = Exit Elm NOW X X all commands not explicitly listed here are beeped at. Use i)ndex X to get back to the main index page, please. X X This function returns when it is ready to go back to the index X page. X**/ X X#include "headers.h" X Xint screen_mangled = 0; Xchar msg_line[SLEN]; X#define store_msg(a) (void)strcpy(msg_line,a) X#define put_prompt() PutLine0(LINES-3, 0, "Command:") X#define put_help() PutLine0(LINES-3, 45, "(Use 'i' to return to index.)") X#define POST_PROMPT_COL strlen("Command: ") X X Xint Xprocess_showmsg_cmd(command) Xint command; X{ X int i, intbuf; /* for dummy parameters...etc */ X int ch; /* for arrow keys */ X int key_offset; /* for arrow keys */ X int istagged; /* for tagging and subsequent msg */ X X Raw(ON); X X while (TRUE) { X clear_error(); X switch (command) { X case '?' : if (help(TRUE)) { X ClearScreen(); X build_bottom(); X } else screen_mangled = TRUE; X break; X X case '|' : put_cmd_name("Pipe", TRUE); X (void) do_pipe(); /* do pipe - ignore return val */ X ClearScreen(); X build_bottom(); X break; X X case '!' : put_cmd_name("System call", TRUE); X (void) subshell(); /* do shell regardless */ X ClearScreen(); X build_bottom(); X break; X X case '<' : X#ifdef ENABLE_CALENDAR X put_cmd_name("Scan messages for calendar entries", TRUE); X scan_calendar(); SHAR_EOF echo "End of part 20" echo "File src/showmsg_c.c is continued in part 21" echo "21" > s2_seq_.tmp exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.