syd@dsinc.UUCP (Syd Weinstein) (12/14/88)
---- Cut Here and unpack ---- #!/bin/sh # this is part 17 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file src/reply.c continued # CurArch=17 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/reply.c" sed 's/^X//' << 'SHAR_EOF' >> src/reply.c X int return_value, form_letter; X X form_letter = (header_table[current-1].status & FORM_LETTER); X X get_return(return_address); X X if (first_word(header_table[current-1].from, "To:")) { X strcpy(subject, header_table[current-1].subject); X if (form_letter) X return_value = mail_filled_in_form(return_address, subject); X else X return_value = sendmsg(return_address, "", subject, TRUE, NO, TRUE); X } X else if (header_table[current-1].subject[0] != '\0') { X if ((strncmp("Re:", header_table[current-1].subject, 3) == 0) || X (strncmp("RE:", header_table[current-1].subject, 3) == 0) || X (strncmp("re:", header_table[current-1].subject, 3) == 0)) X strcpy(subject, header_table[current-1].subject); X else { X strcpy(subject,"Re: "); X strcat(subject,header_table[current-1].subject); X } X if (form_letter) X return_value = mail_filled_in_form(return_address, subject); X else X return_value = sendmsg(return_address, "", subject, TRUE, NO, TRUE); X } X else X if (form_letter) X return_value = mail_filled_in_form(return_address, X "Filled in Form"); X else X return_value = sendmsg(return_address, "", "Re: your mail", X TRUE, NO, TRUE); X X return(return_value); X} X Xint Xreply_to_everyone() X{ X /** Reply to everyone who received the current message. X This includes other people in the 'To:' line and people X in the 'Cc:' line too. Returns non-zero iff the screen X has to be rewritten. **/ X X char return_address[LONG_SLEN], subject[SLEN]; X char full_address[VERY_LONG_STRING]; X int return_value; X X get_return(return_address); X X#ifndef HAVE_GROUP_REPLIES_BE_A_CC X full_address[0] = '\0'; /* no copies yet */ X#else X strcpy(full_address, return_address); /* sender gets copy */ X#endif X X get_and_expand_everyone(return_address, full_address); X X if (header_table[current-1].subject[0] != '\0') { X if ((strncmp("Re:", header_table[current-1].subject, 3) == 0) || X (strncmp("RE:", header_table[current-1].subject, 3) == 0) || X (strncmp("re:", header_table[current-1].subject, 3) == 0)) X strcpy(subject, header_table[current-1].subject); X else { X strcpy(subject,"Re: "); X strcat(subject,header_table[current-1].subject); X } X return_value = sendmsg(return_address, full_address, subject, X TRUE, NO, TRUE); X } X else X return_value = sendmsg(return_address, full_address, X "Re: your mail", TRUE, NO, TRUE); X X return(return_value); X X} X Xint Xforward() X{ X /** Forward the current message. What this actually does is X to set auto_copy to true, then call 'send' to get the X address and route the mail. Modified to also set X 'noheader' to FALSE also, so that the original headers X of the message sent are included in the message body also. X Return TRUE if the main part of the screen has been changed X (useful for knowing whether a redraw is needed. X **/ X X char subject[SLEN], address[VERY_LONG_STRING]; X int original_cc, results, edit_msg = FALSE; X X forwarding = TRUE; X X original_cc = auto_copy; X address[0] = '\0'; X X if (header_table[current-1].status & FORM_LETTER) X PutLine0(LINES-3,COLUMNS-40,"<no editing allowed>"); X else { X edit_msg = (want_to("Edit outgoing message (y/n) ? ",'y',FALSE)!='n'); X Write_to_screen("%s", 1, edit_msg? "Yes" : "No"); X } X X auto_cc = TRUE; /* we want a copy */ X X if (strlen(header_table[current-1].subject) > 0) { X X strcpy(subject, header_table[current-1].subject); X X /* this next strange compare is to see if the last few chars are X already '(fwd)' before we tack another on */ X X if (strlen(subject) < 6 || (strcmp((char *) subject+strlen(subject)-5, X "(fwd)") != 0)) X strcat(subject, " (fwd)"); X X results = sendmsg(address, "", subject, edit_msg, X header_table[current-1].status & FORM_LETTER? X PREFORMATTED : allow_forms, FALSE); X } X else X results = sendmsg(address, "", "Forwarded mail...", edit_msg, X header_table[current-1].status & FORM_LETTER? X PREFORMATTED : allow_forms, FALSE); X X auto_copy = original_cc; X forwarding = FALSE; X X return(results); X} X Xget_and_expand_everyone(return_address, full_address) Xchar *return_address, *full_address; X{ X /** Read the current message, extracting addresses from the 'To:' X and 'Cc:' lines. As each address is taken, ensure that it X isn't to the author of the message NOR to us. If neither, X prepend with current return address and append to the X 'full_address' string. X **/ X X char ret_address[LONG_SLEN], buf[LONG_SLEN], new_address[LONG_SLEN], X address[LONG_SLEN], comment[LONG_SLEN]; X int in_message = 1, first_pass = 0, index, line_pending = 0; X X /** First off, get to the first line of the message desired **/ X X if (fseek(mailfile, header_table[current-1].offset, 0) == -1) { X dprint(1,(debugfile,"Error: seek %ld resulted in errno %s (%s)\n", X header_table[current-1].offset, error_name(errno), X "get_and_expand_everyone")); X error2("ELM [seek] couldn't read %d bytes into file (%s)", X header_table[current-1].offset, error_name(errno)); X return; X } X X /** okay! Now we're there! **/ X X /** let's fix the ret_address to reflect the return address of this X message with '%s' instead of the persons login name... **/ X X translate_return(return_address, ret_address); X X /** now let's parse the actual message! **/ X X while (in_message) { X if (! line_pending) X in_message = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL); X line_pending = 0; X if (first_word(buf, "From ") && first_pass++ != 0) X in_message = FALSE; X else if (first_word(buf, "To:") || first_word(buf, "Cc:") || X first_word(buf, "CC:") || first_word(buf, "cc:")) { X do { X no_ret(buf); X X /** we have a buffer with a list of addresses, each of either the X form "address (name)" or "name <address>". Our mission, should X we decide not to be too lazy, is to break it into the two parts. X **/ X X if (!whitespace(buf[0])) X index = chloc(buf, ':')+1; /* skip header field */ X else X index = 0; /* skip whitespace */ X X while (break_down_tolist(buf, &index, address, comment)) { X X if (okay_address(address, return_address)) { X sprintf(new_address, ret_address, address); X optimize_and_add(new_address, full_address); X } X } X X in_message = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL); X X if (in_message) dprint(2, (debugfile, "> %s", buf)); X X } while (in_message && whitespace(buf[0])); X line_pending++; X } X else if (strlen(buf) < 2) /* done with header */ X in_message = FALSE; X } X} X Xint Xokay_address(address, return_address) Xchar *address, *return_address; X{ X /** This routine checks to ensure that the address we just got X from the "To:" or "Cc:" line isn't us AND isn't the person X who sent the message. Returns true iff neither is the case **/ X X char our_address[SLEN]; X struct addr_rec *alternatives; X X if (in_string(address, return_address)) X return(FALSE); X X sprintf(our_address, "%s!%s", hostname, username); X X if (in_string(address, our_address)) X return(FALSE); X X sprintf(our_address, "%s@%s", username, hostname); X X if (in_string(address, our_address)) X return(FALSE); X X alternatives = alternative_addresses; X X while (alternatives != NULL) { X if (in_string(address, alternatives->address)) X return(FALSE); X alternatives = alternatives->next; X } X X return(TRUE); X} X Xoptimize_and_add(new_address, full_address) Xchar *new_address, *full_address; X{ X /** This routine will add the new address to the list of addresses X in the full address buffer IFF it doesn't already occur. It X will also try to fix dumb hops if possible, specifically hops X of the form ...a!b...!a... and hops of the form a@b@b etc X **/ X X register int len, host_count = 0, i; X char hosts[MAX_HOPS][SLEN]; /* array of machine names */ X char *host, *addrptr; X X if (in_string(full_address, new_address)) X return(1); /* duplicate address */ X X /** optimize **/ X /* break down into a list of machine names, checking as we go along */ X X addrptr = (char *) new_address; X X while ((host = get_token(addrptr, "!", 1)) != NULL) { X for (i = 0; i < host_count && ! equal(hosts[i], host); i++) X ; X X if (i == host_count) { X strcpy(hosts[host_count++], host); X if (host_count == MAX_HOPS) { X dprint(2, (debugfile, X "Error: hit max_hops limit trying to build return address (%s)\n", X "optimize_and_add")); X error("Can't build return address - hit MAX_HOPS limit!"); X return(1); X } X } X else X host_count = i + 1; X addrptr = NULL; X } X X /** fix the ARPA addresses, if needed **/ X X if (chloc(hosts[host_count-1], '@') > -1) X fix_arpa_address(hosts[host_count-1]); X X /** rebuild the address.. **/ X X new_address[0] = '\0'; X X for (i = 0; i < host_count; i++) X sprintf(new_address, "%s%s%s", new_address, X new_address[0] == '\0'? "" : "!", X hosts[i]); X X if (full_address[0] == '\0') X strcpy(full_address, new_address); X else { X len = strlen(full_address); X full_address[len ] = ','; X full_address[len+1] = ' '; X full_address[len+2] = '\0'; X strcat(full_address, new_address); X } X X return(0); X} X Xget_return_name(address, name, trans_to_lowercase) Xchar *address, *name; Xint trans_to_lowercase; X{ X /** Given the address (either a single address or a combined list X of addresses) extract the login name of the first person on X the list and return it as 'name'. Modified to stop at X any non-alphanumeric character. **/ X X /** An important note to remember is that it isn't vital that this X always returns just the login name, but rather that it always X returns the SAME name. If the persons' login happens to be, X for example, joe.richards, then it's arguable if the name X should be joe, or the full login. It's really immaterial, as X indicated before, so long as we ALWAYS return the same name! **/ X X /** Another note: modified to return the argument as all lowercase X always, unless trans_to_lowercase is FALSE... **/ X X char single_address[LONG_SLEN]; X register int i, loc, index = 0; X X dprint(6, (debugfile,"get_return_name called with (%s, <>, shift=%s)\n", X address, onoff(trans_to_lowercase))); X X /* First step - copy address up to a comma, space, or EOLN */ X X for (i=0; address[i] != ',' && ! whitespace(address[i]) && X address[i] != '\0'; i++) X single_address[i] = address[i]; X single_address[i] = '\0'; X X /* Now is it an ARPA address?? */ X X if ((loc = chloc(single_address, '@')) != -1) { /* Yes */ X X /* At this point the algorithm is to keep shifting our copy X window left until we hit a '!'. The login name is then X located between the '!' and the first metacharacter to X it's right (ie '%', ':' or '@'). */ X X for (i=loc; single_address[i] != '!' && i > -1; i--) X if (single_address[i] == '%' || X single_address[i] == ':' || X single_address[i] == '.' || /* no domains */ X single_address[i] == '@') loc = i-1; X X if (i < 0 || single_address[i] == '!') i++; X X for (index = 0; index < loc - i + 1; index++) X if (trans_to_lowercase) X name[index] = tolower(single_address[index+i]); X else X name[index] = single_address[index+i]; X name[index] = '\0'; X } X else { /* easier - standard USENET address */ X X /* This really is easier - we just cruise left from the end of X the string until we hit either a '!' or the beginning of the X line. No sweat. */ X X loc = strlen(single_address)-1; /* last char */ X X for (i = loc; single_address[i] != '!' && single_address[i] != '.' X && i > -1; i--) X if (trans_to_lowercase) X name[index++] = tolower(single_address[i]); X else X name[index++] = single_address[i]; X name[index] = '\0'; X reverse(name); X } X} X Xint Xbreak_down_tolist(buf, index, address, comment) Xchar *buf, *address, *comment; Xint *index; X{ X /** This routine steps through "buf" and extracts a single address X entry. This entry can be of any of the following forms; X X address (name) X name <address> X address X X Once it's extracted a single entry, it will then return it as X two tokens, with 'name' (e.g. comment) surrounded by parens. X Returns ZERO if done with the string... X **/ X X char buffer[LONG_STRING]; X register int i, loc = 0, hold_index; X X if (*index > strlen(buf)) return(FALSE); X X while (whitespace(buf[*index])) (*index)++; X X if (*index > strlen(buf)) return(FALSE); X X /** Now we're pointing at the first character of the token! **/ X X hold_index = *index; X X while (buf[*index] != ',' && buf[*index] != '\0') X buffer[loc++] = buf[(*index)++]; X X (*index)++; X buffer[loc] = '\0'; X X while (whitespace(buffer[loc])) /* remove trailing whitespace */ X buffer[--loc] = '\0'; X X if (strlen(buffer) == 0) return(FALSE); X X dprint(5, (debugfile, "\n* got \"%s\"\n", buffer)); X X if (buffer[loc-1] == ')') { /* address (name) format */ X for (loc = 0;buffer[loc] != '(' && loc < strlen(buffer); loc++) X /* get to the opening comment character... */ ; X X loc--; /* back up to just before the paren */ X while (whitespace(buffer[loc])) loc--; /* back up */ X X /** get the address field... **/ X X for (i=0; i <= loc; i++) X address[i] = buffer[i]; X address[i] = '\0'; X X /** now get the comment field, en toto! **/ X X loc = 0; X X for (i = chloc(buffer, '('); i < strlen(buffer); i++) X comment[loc++] = buffer[i]; X comment[loc] = '\0'; X } X else if (buffer[loc-1] == '>') { /* name <address> format */ X dprint(7, (debugfile, "\tcomment <address>\n")); X for (loc = 0;buffer[loc] != '<' && loc < strlen(buffer); loc++) X /* get to the opening comment character... */ ; X while (whitespace(buffer[loc])) loc--; /* back up */ X X /** get the comment field... **/ X X comment[0] = '('; X for (i=1; i < loc; i++) X comment[i] = buffer[i-1]; X comment[i++] = ')'; X comment[i] = '\0'; X X /** now get the address field, en toto! **/ X X loc = 0; X X for (i = chloc(buffer,'<') + 1; i < strlen(buffer) - 1; i++) X address[loc++] = buffer[i]; X X address[loc] = '\0'; X } X else { X /** the next section is added so that all To: lines have commas X in them accordingly **/ X X for (i=0; buffer[i] != '\0'; i++) X if (whitespace(buffer[i])) break; X if (i < strlen(buffer)) { /* shouldn't be whitespace */ X buffer[i] = '\0'; X *index = hold_index + strlen(buffer) + 1; X } X strcpy(address, buffer); X comment[0] = '\0'; X } X X dprint(5, (debugfile, "-- returning '%s' '%s'\n", address, comment)); X X return(TRUE); X} SHAR_EOF echo "File src/reply.c is complete" chmod 0444 src/reply.c || echo "restore of src/reply.c fails" echo "x - extracting src/returnadd.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/returnadd.c && X Xstatic char rcsid[] = "@(#)$Id: returnadd.c,v 2.1 88/09/15 20:29:32 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.1 $ $State: Exp $ X * X * Copyright (c) 1986 Dave Taylor X ******************************************************************************* X * Bug reports, patches, comments, suggetions should be sent to: X * X * Syd Weinstein, Elm Corrdinator X * syd@dsinc.UUCP dsinc!syd X * X ******************************************************************************* X * $Log: returnadd.c,v $ X * Revision 2.1 88/09/15 20:29:32 syd X * checked in with -k by syd at 88.09.15.20.29.32. X * X * 88/08/27 ssw X * changes due to alpha testing at dsinc X * add strip off of our site name on reply X * so that local sigs appear on local replies - elm was putting X * fill sig on replies to local messages X * X * Revision 2.1 88/07/21 09:59:24 edc X * Final hacks and cleanup to the 2.1 alpha test release. X * X * Revision 2.0 88/06/27 17:29:18 edc X * Original 2.0 gamma sources as leaked from HP X * X ******************************************************************************/ X X/** This set of routines is used to generate real return addresses X and also return addresses suitable for inclusion in a users X alias files (ie optimized based on the pathalias database). X X Added: the ability to respond to messages that were originally X sent by the user (That is, the "savemail" file format messages) X by reading the return address, seeing the "To:" prefix and then X calling the "get_existing_return()" routine. Currently this does X NOT include any "Cc" lines in the message, just the "To:" line(s). X X Also added the PREFER_UUCP stuff for listing reasonable addresses X and such...*sigh* X X**/ X X#include "headers.h" X X#include <errno.h> X X#include <sys/types.h> X#include <sys/stat.h> X Xchar *shift_lower(); X Xextern int errno; X Xchar *error_name(), *strcat(), *strcpy(); X X#ifdef OPTIMIZE_RETURN X Xoptimize_return(address) Xchar *address; X{ X /** This routine tries to create an optimized address, that is, X an address that has the minimal information needed to X route a message to this person given the current path X database... X **/ X X#ifdef PREFER_UUCP X X /** first off, let's see if we need to strip off the localhost X address crap... **/ X X /** if we have a uucp part (e.g.a!b) AND the bogus address...**/ X X if (chloc(address,'!') != -1 && in_string(address, BOGUS_INTERNET)) X address[strlen(address)-strlen(BOGUS_INTERNET)] = '\0'; X#endif X X /** next step is to figure out what sort of address we have... **/ X X if (chloc(address, '%') != -1) X optimize_cmplx_arpa(address); X else if (chloc(address, '@') != -1) X optimize_arpa(address); X else X optimize_usenet(address); X} X Xoptimize_cmplx_arpa(address) Xchar *address; X{ X /** Try to optimize a complex ARPA address. A Complex address is one X that contains '%' (deferred '@'). For example: X veeger!hpcnof!hplabs!joe%sytech@syte X is a complex address (no kidding, right?). The algorithm for X trying to resolve it is to move all the way to the right, then X back up left until the first '!' then from there to the SECOND X metacharacter on the right is the name@host address...(in this X example, it would be "joe%sytech"). Check this in the routing X table. If not present, keep backing out to the right until we X find a host that is present, or we hit the '@' sign. Once we X have a 'normal' ARPA address, hand it to optimize_arpa(). X **/ X X char name[SHORT_SLEN], buffer[SLEN], junk[SLEN]; X char host[SHORT_SLEN], old_host[SHORT_SLEN]; X register int i, loc, nloc = 0, hloc = 0, passes = 1; X X /** first off, get the name%host... **/ X X for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--) X ; X X while (address[loc] != '\0') { X X if (passes == 1) { X loc++; X X while (address[loc] != '%' && address[loc] != '@') X name[nloc++] = address[loc++]; X } X else { X for (i=0; old_host[i] != '\0'; i++) X name[nloc++] = old_host[i]; X } X X loc++; X X while (address[loc] != '%' && address[loc] != '@') X host[hloc++] = address[loc++]; X X host[hloc] = name[nloc] = '\0'; X X strcpy(old_host, host); X X sprintf(buffer, "%s@%s", name, shift_lower(host)); X X if (expand_site(buffer, junk) == 0) { X strcpy(address, buffer); X return; X } X else if (address[loc] == '@') { X optimize_arpa(address); X return; X } X else X name[nloc++] = '%'; /* for next pass through */ X X } X} X Xoptimize_arpa(address) Xchar *address; X{ X /** Get an arpa address and simplify it to the minimal X route needed to get mail to this person... **/ X X char name[SHORT_SLEN], buffer[SLEN], junk[SLEN]; X char host[SHORT_SLEN]; X register int loc, nloc = 0, hloc = 0, at_sign = 0; X X for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--) { X if (address[loc] == '@') X at_sign++; /* remember this spot! */ X else if (at_sign) X name[nloc++] = address[loc]; X else X host[hloc++] = address[loc]; X } X X name[nloc] = host[hloc] = '\0'; X X reverse(name); X reverse(host); X X sprintf(buffer,"%s@%s", name, shift_lower(host)); X X if (expand_site(buffer, junk) == 0) { X strcpy(address, buffer); X return; X } X X optimize_usenet(address); /* that didn't work... */ X} X Xoptimize_usenet(address) Xchar *address; X{ X /** optimize the return address IFF it's a standard usenet X address... X **/ X X char name[SHORT_SLEN], new_address[SLEN], buffer[SLEN], junk[SLEN]; X register int loc, nloc = 0, aloc = 0, passes = 1; X X for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--) X name[nloc++] = address[loc]; X name[nloc] = '\0'; X X reverse(name); X X new_address[0] = '\0'; X X /* got name, now get machine until we can get outta here */ X X while (loc > -1) { X X new_address[aloc++] = address[loc--]; /* the '!' char */ X X while (address[loc] != '!' && loc > -1) X new_address[aloc++] = address[loc--]; X X new_address[aloc] = '\0'; X X strcpy(buffer, new_address); X reverse(buffer); X X if (expand_site(buffer, junk) == 0) { X if (passes == 1 && chloc(name, '@') == -1) { X buffer[strlen(buffer) - 1] = '\0'; /* remove '!' */ X sprintf(address, "%s@%s", name, buffer); X } X else X sprintf(address, "%s%s", buffer, name); X return; /* success! */ X } X passes++; X } X X return; /* nothing to do! */ X} X X#endif OPTIMIZE_RETURN X Xget_return(buffer) Xchar *buffer; X{ X /** reads 'current' message again, building up the full return X address including all machines that might have forwarded X the message. **/ X X char buf[LONG_SLEN], name1[SLEN], name2[SLEN], lastname[SLEN]; X char hold_return[LONG_SLEN], alt_name2[SLEN], *cptr; X int ok = 1, lines; X X /* now initialize all the char buffers [thanks Keith!] */ X X buf[0] = name1[0] = name2[0] = lastname[0] = '\0'; X hold_return[0] = alt_name2[0] = '\0'; X X /** get to the first line of the message desired **/ X X if (fseek(mailfile, header_table[current-1].offset, 0) == -1) { X dprint(1, (debugfile, X "Error: seek %ld bytes into file hit errno %s (%s)", X header_table[current-1].offset, error_name(errno), X "get_return")); X error2("couldn't seek %d bytes into file (%s)", X header_table[current-1].offset, error_name(errno)); X return; X } X X /** okay! Now we're there! **/ X X lines = header_table[current-1].lines; X X buffer[0] = '\0'; X X while (ok && lines--) { X ok = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL); X if (first_word(buf, "From ")) X sscanf(buf, "%*s %s", hold_return); X else if (first_word(buf, ">From")) { X sscanf(buf,"%*s %s %*s %*s %*s %*s %*s %*s %*s %s %s", X name1, name2, alt_name2); X if (strcmp(name2, "from") == 0) /* remote from xyz */ X strcpy(name2, alt_name2); X else if (strcmp(name2, "by") == 0) /* forwarded by xyz */ X strcpy(name2, alt_name2); X add_site(buffer, name2, lastname); X } X X#ifdef USE_EMBEDDED_ADDRESSES X X else if (first_word(buf, "From:")) { X get_address_from("From:", buf, hold_return); X buffer[0] = '\0'; X } X else if (first_word(buf, "Reply-To:")) { X get_address_from("Reply-To:", buf, buffer); X return; X } X X#endif X X else if (strlen(buf) < 2) /* done with header */ X lines = 0; /* let's get outta here! We're done!!! */ X } X X if (buffer[0] == '\0') X strcpy(buffer, hold_return); /* default address! */ X else X add_site(buffer, name1, lastname); /* get the user name too! */ X X if ((ok = chloc(buffer, '!')) >= 0) X { X cptr = BOGUS_INTERNET; X strcpy(buf, cptr + 1); X if ((lines = chloc(buf, '.')) >= 0) X buf[lines] = '\0'; X strcat(buf, "!"); X if (strncmp(buf, buffer, strlen(buf)) == 0) /* strip off our node name */ X { X strcpy(buf, buffer); X strcpy(buffer, &buf[ok + 1]); X } X } X X if ((ok = chloc(buffer, '@')) >= 0) X { X strcpy(buf, BOGUS_INTERNET); X if (strcmp(&buffer[ok], buf) == 0) X buffer[ok] = '\0'; /* strip off our node name on reply */ X else X { X if ((lines = chloc(buf, '.')) >= 0) X buf[lines] = '\0'; X if (strcmp(&buffer[ok], buf) == 0) X buffer[ok] = '\0'; /* strip off our node name on reply */ X } X } X X if (first_word(buffer, "To:")) /* response to savecopy! */ X get_existing_address(buffer); X else X /* if we have a space character, or we DON'T have '!' or '@' chars */ X X if (chloc(header_table[current-1].from, ' ') >= 0 || X (chloc(header_table[current-1].from, '!') < 0 && X chloc(header_table[current-1].from, '@') < 0)) { X sprintf(name2, " (%s)", header_table[current-1].from); X strcat(buffer, name2); X } X} X Xget_existing_address(buffer) Xchar *buffer; X{ X /** This routine is called when the message being responded to has X "To:xyz" as the return address, signifying that this message is X an automatically saved copy of a message previously sent. The X correct to address can be obtained fairly simply by reading the X To: header from the message itself and (blindly) copying it to X the given buffer. Note that this header can be either a normal X "To:" line (Elm) or "Originally-To:" (previous versions e.g.Msg) X **/ X X char mybuf[LONG_STRING]; X register char ok = 1, in_to = 0; X X buffer[0] = '\0'; X X /** first off, let's get to the beginning of the message... **/ X X if (fseek(mailfile, header_table[current-1].offset, 0) == -1) { X dprint(1, (debugfile, X "Error: seek %ld bytes into file hit errno %s (%s)", X header_table[current-1].offset, error_name(errno), X "get_existing_address")); X error2("couldn't seek %d bytes into the file (%s)", X header_table[current-1].offset, error_name(errno)); X return; X } X X /** okay! Now we're there! **/ X X while (ok) { X ok = (int) (fgets(mybuf, LONG_STRING, mailfile) != NULL); X no_ret(mybuf); /* remove return character */ X X if (first_word(mybuf, "To: ")) { X in_to = TRUE; X strcpy(buffer, (char *) mybuf + strlen("To: ")); X } X else if (first_word(mybuf, "Original-To:")) { X in_to = TRUE; X strcpy(buffer, (char *) mybuf + strlen("Original-To:")); X } X else if (in_to && whitespace(mybuf[0])) { X strcat(buffer, " "); /* tag a space in */ X strcat(buffer, (char *) mybuf + 1); /* skip 1 whitespace */ X } X else if (strlen(mybuf) < 2) X return; /* we're done for! */ X else X in_to = 0; X } X} SHAR_EOF chmod 0444 src/returnadd.c || echo "restore of src/returnadd.c fails" echo "x - extracting src/save_opts.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/save_opts.c && X Xstatic char rcsid[] = "@(#)$Id: save_opts.c,v 2.1 88/09/15 20:29:35 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.1 $ $State: Exp $ X * X * Copyright (c) 1986, 1987, 1988 Dave Taylor X ******************************************************************************* X * Bug reports, patches, comments, suggetions should be sent to: X * X * Syd Weinstein, Elm Corrdinator X * syd@dsinc.UUCP dsinc!syd X * X ******************************************************************************* X * $Log: save_opts.c,v $ X * Revision 2.1 88/09/15 20:29:35 syd X * checked in with -k by syd at 88.09.15.20.29.35. X * X * 88/09/13 Chip Rosenthal <chip@vector> X * fix crash with segmentation violation if you try to save the X * configuration while in the options menu and the "elmrc-info" X * file is not found. X * X * 88/09/02 Syd Weinstein X * added version buffer X * X * 88/09/01 Rob Bernardo <rob@pbhyf.PacBell.COM> X * fixes not saving ask_cc option in elmrc X * X * 88/08/27 ssw X * add deluth patches X * X * Revision 2.1 88/07/21 09:59:28 edc X * Final hacks and cleanup to the 2.1 alpha test release. X * X * Revision 2.0 88/06/27 17:25:32 edc X * The original 2.0 gamma sources as leaked from HP X * 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 if (access(newfname, ACCESS_EXISTS) != -1) { X /** YES! Copy it to the file ".old.elmrc".. **/ X if (unlink(oldfname) < 0) X dprint(1, (debugfile, "Unable to unlink %s\n", oldfname)); X if (link(newfname, oldfname) < 0) X dprint(2, (debugfile, "Unable to link %s to %s\n", X newfname, oldfname)); X if (unlink(newfname) < 0) X dprint(1, (debugfile, "Unable to unlink %s\n", newfname)); X (void) chown(oldfname, userid, groupid); 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 chown(newfname, userid, groupid); X X save_user_options(elminfo, newelmrc); 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 X save_option_string(CALENDAR, calendar_file, newelmrc, FALSE); X save_option_string(EDITOR, editor, newelmrc, FALSE); X save_option_string(FULLNAME, full_username, newelmrc, FALSE); X save_option_string(MAILBOX, mailbox, newelmrc, FALSE); X save_option_string(MAILDIR, folders, newelmrc, FALSE); X save_option_string(PAGER, pager, newelmrc, FALSE); X save_option_string(PREFIX, prefixchars, newelmrc, TRUE); X save_option_string(PRINT, printout, newelmrc, FALSE); X save_option_string(SAVEMAIL, savefile, newelmrc, FALSE); X save_option_string(SHELL, shell, newelmrc, FALSE); X X save_option_string(LOCALSIGNATURE, local_signature, newelmrc, FALSE); X save_option_string(REMOTESIGNATURE, remote_signature, newelmrc, FALSE); X X save_option_sort(SORTBY, newelmrc); X X save_option_on_off(ALWAYSDELETE, always_del, newelmrc); X save_option_on_off(ALWAYSLEAVE, always_leave, 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(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(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 if ( elminfo_fd != NULL ) { X fflush(elminfo_fd); /* make sure we're clear... */ X fclose(elminfo_fd); X } X} X Xsave_option_string(index, value, fd, underscores) Xint index, 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 if (strlen(value) == 0) return; /* why bother? */ X X add_comment(index, 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[index].name, buffer); X} X Xsave_option_sort(index, fd) Xint index; XFILE *fd; X{ X /** save the current sorting option to a file **/ X X add_comment(index, fd); X X fprintf(fd, "%s = %s\n\n", save_info[index].name, X sort_name(SHORT)); X} X Xsave_option_number(index, value, fd) Xint index, value; XFILE *fd; X{ X /** Save a binary option to the file - boy is THIS easy!! **/ X X add_comment(index, fd); X X fprintf(fd, "%s = %d\n\n", save_info[index].name, value); X} X Xsave_option_on_off(index, value, fd) Xint index, value; XFILE *fd; X{ X /** Save a binary option to the file - boy is THIS easy!! **/ X X add_comment(index, fd); X X fprintf(fd, "%s = %s\n\n", save_info[index].name, onoff(value)); X} X Xsave_option_weedlist(index, fd) Xint index; 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(index, fd); X X length_so_far = strlen(save_info[index].name) + 4; X X fprintf(fd, "%s = ", save_info[index].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(index, list, fd) Xint index; 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(index, fd); X X alternate = list; /* don't LOSE the top!! */ X X length_so_far = strlen(save_info[index].name) + 4; X X fprintf(fd, "%s = ", save_info[index].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(index, fd) Xint index; 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[index].offset > 0L) { X if (fseek(elminfo, save_info[index].offset, 0) == -1) { X dprint(1,(debugfile, X "** error %s seeking to %ld in elm-info file!\n", X error_name(errno), save_info[index].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 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.1 88/09/16 10:17:08 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.1 $ $State: Exp $ X * X * Copyright (c) 1986 Dave Taylor X ******************************************************************************* X * Bug reports, patches, comments, suggetions should be sent to: X * X * Syd Weinstein, Elm Corrdinator X * syd@dsinc.UUCP dsinc!syd X * X ******************************************************************************* X * $Log: savecopy.c,v $ X * Revision 2.1 88/09/16 10:17:08 syd X * From Rob: X * when saving to named files, the send version didn't lowercase the name, X * the received one did. This fix makes the send use the lowercase name X * X * Revision 2.0 88/07/21 09:59:30 edc X * checked in with -k by syd at 88.09.15.20.29.37. X * X * Revision 2.1 88/07/21 09:59:30 edc X * Final hacks and cleanup to the 2.1 alpha test release. X * X * Revision 2.0 88/06/27 17:25:33 edc X * The original 2.0 gamma sources as leaked from HP X * X * X * X ******************************************************************************/ X X/** Save a copy of the specified message in the users savemail mailbox. X X**/ X X#include "headers.h" X#ifdef BSD X# ifdef BSD4_1 X# include <time.h> X# else X# include <sys/time.h> X# endif X#else X# include <time.h> X#endif X X#include <errno.h> X X#define metachar(c) (c == '+' || c == '%' || c == '=') X Xchar *format_long(), *get_arpa_date(); Xchar *error_name(), *error_description(); Xchar *ctime(); X Xextern char in_reply_to[SLEN]; /* In-Reply-To: string */ Xextern int gotten_key; /* for encryption */ Xextern int errno; X Xchar *strcat(), *strcpy(); Xunsigned long sleep(); Xlong time(); X Xsave_copy(subject, to, cc, filename, original_to) Xchar *subject, *to, *cc, *filename, *original_to; X{ X /** This routine appends a copy of the outgoing message to the X file specified by the SAVEFILE environment variable. **/ X X FILE *save, /* file id for file to save to */ X *message; /* the actual message body */ X long thetime, /* variable holder for time */ X time(); X char buffer[SLEN], /* read buffer */ X savename[SLEN], /* name of file saving into */ X newbuffer[SLEN]; /* first name in 'to' line */ X register int i; /* for chopping 'to' line up */ X int crypted=0; /* are we encrypting? */ X X savename[0] = '\0'; X X if (save_by_name) { X get_return_name(to, buffer, TRUE); X if (strlen(buffer) == 0) { X dprint(3, (debugfile, X "Warning: get_return_name couldn't break down %s\n", to)); X savename[0] = '\0'; X } X else { X sprintf(savename, "%s%s%s", folders, X lastch(folders) == '/'? "" : "/", buffer); X X if (can_access(savename, READ_ACCESS) != 0) X savename[0] = '\0'; X } X } X X if (strlen(savename) == 0) { X if (strlen(savefile) == 0) { X error("variable 'SAVEFILE' not defined!"); X return(FALSE); X } X if (metachar(savefile[0])) { X sprintf(savename, "%s%s%s", folders, X lastch(folders) == '/' ? "" : "/", savefile+1); X } X else X strcpy(savename, savefile); X } 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 error1("permission to append to %s denied!", savename); X sleep(2); X return(FALSE); X } X X if ((save = fopen(savename, "a")) == NULL) { X dprint(1, (debugfile, X "Error: Couldn't append message to file %s (%s)\n", X savename, "save_copy")); X dprint(1, (debugfile,"** %s - %s **\n", error_name(errno), X error_description(errno))); X error1("couldn't append to %s", savename); X sleep(2); X return(FALSE); X } X X if ((message = fopen(filename, "r")) == NULL) { X fclose(save); X dprint(1, (debugfile, X "Error: Couldn't read file %s (save_copy)\n", filename)); X dprint(1, (debugfile, "** %s - %s **\n", error_name(errno), X error_description(errno))); X error1("couldn't read file %s!", filename); X sleep(2); X return(FALSE); X } X X for (i=0; original_to[i] != '\0' && ! whitespace(original_to[i]); i++) X newbuffer[i] = original_to[i]; X X newbuffer[i] = '\0'; X X tail_of(newbuffer, buffer, FALSE); X X thetime = time((long *) 0); /* dumb dumb dumb routine */ X X fprintf(save,"From To:%s %s", buffer, ctime(&thetime)); X X fprintf(save, "Date: %s\n", get_arpa_date()); X X fprintf(save,"To: %s\nSubject: %s\n", X format_long(to,strlen("To: ")), subject); X X if (strlen(cc) > 0) X fprintf(save,"Cc: %s\n", X format_long(cc, strlen("Cc:"))); X X if (strlen(in_reply_to) > 0) X fprintf(save, "In-Reply-To: %s\n", in_reply_to); X X (void) putc('\n', save); /* put another return, please! */ X X /** now copy over the message... **/ X X while (fgets(buffer, SLEN, message) != NULL) { X if (buffer[0] == '[') { X if (strncmp(buffer, START_ENCODE, strlen(START_ENCODE))==0) X crypted = 1; X else if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE))==0) X crypted = 0; X else if (strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0 || X strncmp(buffer, DONT_SAVE2, strlen(DONT_SAVE2)) == 0) { X X /* second test added due to an imcompatability between the X documentation and the software! (Thanks Bill!) */ X X fclose(message); X fclose(save); X chown(savename, userid, groupid); X return(TRUE); X } X } X else if (crypted) { X if (! gotten_key++) X getkey(ON); X encode(buffer); X } X if (strncmp(buffer, "From ", 5) == 0) X fprintf(save, ">%s", buffer); X else X fputs(buffer, save); X } X X fprintf(save, "\n"); /* ensure a blank line at the end */ X X fclose(message); X fclose(save); X X /* make sure save file isn't owned by root! */ X chown(savename, userid, groupid); X X return(TRUE); 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.1 88/09/16 13:51:16 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.1 $ $State: Exp $ X * X * Copyright (c) 1985 Dave Taylor X ******************************************************************************* X * Bug reports, patches, comments, suggetions should be sent to: X * X * Syd Weinstein, Elm Corrdinator X * syd@dsinc.UUCP dsinc!syd X * X ******************************************************************************* X * $Log: screen.c,v $ X * Revision 2.1 88/09/16 13:51:16 syd X * changes from Rob re Dave Taylor's comments on Magic Cookie terminals doing move X * after attribute changes X * X * Revision 2.0 88/09/15 20:29:40 syd X * checked in with -k by syd at 88.09.15.20.29.40. X * X * 88/09/02 Syd Weinstein X * added version buffer X * X * 88/09/01 Rob Bernardo <gatech!pbhyf.PacBell.COM!rob> X * There were *two* bugs causing the "command:" prompt to appear X * on the wrong line under a limited set of circumstances. X * X * Revision 2.1 88/07/21 09:59:32 edc X * Final hacks and cleanup to the 2.1 alpha test release. X * X * Revision 2.0 88/06/27 17:25:34 edc X * The original 2.0 gamma sources as leaked from HP X * 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 char buffer[SLEN]; X X ClearScreen(); X X if (selected) X sprintf(buffer, X "Mailbox is '%s' with %d shown out of %d [Elm %s]", X nameof(infile), selected, message_count, version_buff); X else X sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]", X nameof(infile), message_count, plural(message_count), version_buff); X Centerline(1, buffer); 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 "Mailbox is '%s' with %d shown out of %d [Elm %s]", X nameof(infile), selected, message_count, version_buff); X else X sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]", X nameof(infile), message_count, 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)hange mailbox, D)elete, E)dit, F)orward, G)roup reply, M)ail,"); X Centerline(LINES-5, X "N)ext, O)ptions, P)rint, R)eply, S)ave, T)ag, Q)uit, U)ndelete, or eX)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; 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 if (header_page == 0) { X this_msg = visible_to_index(1); X displayed = 0; X } X else { X this_msg = visible_to_index(header_page * headers_per_page + 1); X displayed = header_page * headers_per_page; X } X X last = displayed+headers_per_page; X X } X else { X if (header_page == last_header_page) /* nothing to do! */ SHAR_EOF echo "End of part 17" echo "File src/screen.c is continued in part 18" echo "18" > s2_seq_.tmp exit 0 -- ===================================================================== Sydney S. Weinstein, CDP, CCP Elm Coordinator Datacomp Systems, Inc. Voice: (215) 947-9900 {allegra,bellcore,bpa,vu-vlsi}!dsinc!syd FAX: (215) 938-0235