syd@dsinc.UUCP (Syd Weinstein) (12/12/88)
---- Cut Here and unpack ----
#!/bin/sh
# this is part 11 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file src/edit.c continued
#
CurArch=11
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/edit.c"
sed 's/^X//' << 'SHAR_EOF' >> src/edit.c
X Raw(ON);
X sleep(2);
X return(0);
X }
X
X Raw(ON);
X
X if (mbox_specified == 0) { /* uh oh... now the toughie... */
X
X#ifdef NOSU
X sprintf(temp_infile, "%s%s.temp", temp_lock_dir, username);
X#else
X sprintf(temp_infile, "%s%s.temp", mailhome, username);
X#endif
X unlink(temp_infile); /* remove it if it's there... */
X
X if (bytes(infile) != mailfile_size) {
X
X /* SIGH. We've received mail since we invoked the editor
X on the mailbox. We'll have to do some strange stuff to
X remedy the problem... */
X
X PutLine0(LINES, 0, "Warning: new mail received...");
X CleartoEOLN();
X
X if ((temp_mailbox = fopen(filename, "a")) == NULL) {
X dprint(1, (debugfile,
X "Attempt to open \"%s\" to append failed in %s\n",
X filename, "edit_mailbox"));
X set_error("Couldn't reopen tempfile. Edit LOST!");
X return(1);
X }
X /** Now let's lock the mailbox up and stream the new stuff
X into the temp file... **/
X
X chown( filename, userid, groupid);
X
X lock(OUTGOING);
X if ((real_mailbox = fopen(infile, "r")) == NULL) {
X dprint(1, (debugfile,
X "Attempt to open \"%s\" for reading new mail failed in %s\n",
X infile, "edit_mailbox"));
X sprintf(buffer, "Couldn't open %s for reading! Edit LOST!",
X infile);
X set_error(buffer);
X unlock();
X return(1);
X }
X if (fseek(real_mailbox, mailfile_size, 0) == -1) {
X dprint(1, (debugfile,
X "Couldn't seek to end of infile (offset %ld) (%s)\n",
X mailfile_size, "edit_mailbox"));
X set_error("Couldn't seek to end of mailbox. Edit LOST!");
X unlock();
X return(1);
X }
X
X /** Now we can finally stream the new mail into the tempfile **/
X
X while (fgets(buffer, LONG_SLEN, real_mailbox) != NULL)
X fprintf(temp_mailbox, "%s", buffer);
X
X fclose(real_mailbox);
X fclose(temp_mailbox);
X }
X else
X lock(OUTGOING); /* create a lock file if we're replacing mailbox */
X
X /** link to the temporary mailbox in the mailhome directory... **/
X
X if (link(filename, temp_infile) != 0)
X if (errno == EXDEV || errno == EEXIST) { /* attempt to link across file systems */
X if (copy(filename, temp_infile) != 0) {
X error("Couldn't copy temp file to mailbox!");
X unlock(); /* ciao!*/
X emergency_exit();
X }
X }
X else {
X Write_to_screen("\n\rCouldn't link %s to mailfile %s...\n\r",2,
X filename, temp_infile);
X Write_to_screen("** %s - %s **\n\r", 2,
X error_name(errno), error_description(errno));
X emergency_exit();
X }
X
X /*** G U L P ... let's remove the incoming mail file... ***/
X
X if (unlink(infile) != 0) {
X if (copy(temp_infile, infile) != 0) {
X error("Couldn't copy temp file to mailbox!");
X unlock(); /* ciao!*/
X emergency_exit();
X }
X }
X else {
X /** and quickly now... **/
X
X if (link(temp_infile, infile) != 0) {
X Write_to_screen(
X "\n\rCouldn't internally link %s to mailfile %s...\n\r",
X 2, temp_infile, infile);
X Write_to_screen(
X "\n\rYou'll need to check out %s for your mail...\n\r",
X 1, temp_infile);
X Write_to_screen("** %s - %s **\n\r", 2,
X error_name(errno), error_description(errno));
X emergency_exit();
X }
X }
X
X /** And let's remove the lock file! We're DONE!!! **/
X
X unlock();
X unlink(temp_infile); /* remove the temp file too */
X unlink(filename); /* remove the temp file too */
X error("edit changes incorporated into new mail...");
X }
X else
X error("Resynchronizing with new version of mailbox...");
X
X sleep(2);
X newmbox(3, TRUE, TRUE);
X mailfile_size = bytes(infile);
X showscreen();
X
X current = 1; /* don't leave the user hanging! */
X
X /** finally restore the permissions... **/
X
X if (loaded_stat_buffer) { /* if not, it's junk! */
X chown(infile, stat_buffer.st_uid, stat_buffer.st_gid);
X chmod(infile, stat_buffer.st_mode);
X }
X
X return(1);
X}
X
X#endif
SHAR_EOF
echo "File src/edit.c is complete"
chmod 0444 src/edit.c || echo "restore of src/edit.c fails"
echo "x - extracting src/editmsg.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/editmsg.c &&
X
Xstatic char rcsid[] = "@(#)$Id: editmsg.c,v 2.1 88/09/15 20:28:00 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: editmsg.c,v $
X * Revision 2.1 88/09/15 20:28:00 syd
X * checked in with -k by syd at 88.09.15.20.28.00.
X *
X * 88/08/27 ssw
X * add deluth patches
X *
X * Revision 2.1 88/07/21 09:58:15 edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X *
X * Revision 2.0 88/06/27 17:24:58 edc
X * The original 2.0 gamma sources as leaked from HP
X *
X *
X *
X ******************************************************************************/
X
X/** This contains routines to do with starting up and using an editor (or two)
X from within Elm. This stuff used to be in mailmsg2.c...
X**/
X
X#include "headers.h"
X#include <errno.h>
X#include <setjmp.h>
X#include <signal.h>
X#include <ctype.h>
X
X#ifdef BSD
X#undef tolower
X#endif
X
Xextern int errno;
X
Xchar *error_name(), *error_description(), *strcpy(), *format_long();
Xunsigned long sleep();
X
Xint
Xedit_the_message(filename, already_has_text)
Xchar *filename;
Xint already_has_text;
X{
X /** Invoke the users editor on the filename. Return when done.
X If 'already has text' then we can't use the no-editor option
X and must use 'alternative editor' (e.g. $EDITOR or default_editor)
X instead... **/
X
X char buffer[SLEN];
X register int stat, return_value = 0;
X int old_raw;
X
X buffer[0] = '\0';
X
X if (strcmp(editor, "builtin") == 0 || strcmp(editor, "none") == 0) {
X if (already_has_text)
X sprintf(buffer, "%s %s", alternative_editor, filename);
X else
X return(no_editor_edit_the_message(filename));
X }
X
X PutLine0(LINES, 0, "invoking editor..."); fflush(stdout);
X if (strlen(buffer) == 0)
X sprintf(buffer,"%s %s", editor, filename);
X
X if( (old_raw = RawState()) == ON)
X Raw(OFF);
X
X chown(filename, userid, groupid); /* file was owned by root! */
X
X if (cursor_control)
X transmit_functions(OFF); /* function keys are local */
X
X if ((stat = system_call(buffer, SH)) != 0) {
X dprint(1,(debugfile,
X "System call failed with stat %d (edit_the_message)\n",
X stat));
X dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
X error_description(errno)));
X ClearLine(LINES-1);
X error1("Can't invoke editor '%s' for composition", editor);
X sleep(2);
X return_value = 1;
X }
X
X if (cursor_control)
X transmit_functions(ON); /* function keys are local */
X
X if ( old_raw == ON)
X Raw(ON);
X
X return(return_value);
X}
X
Xextern char to[VERY_LONG_STRING], cc[VERY_LONG_STRING],
X expanded_to[VERY_LONG_STRING], expanded_cc[VERY_LONG_STRING],
X subject[SLEN];
X
Xint interrupts_while_editing; /* keep track 'o dis stuff */
Xjmp_buf edit_location; /* for getting back from interrupt */
X
X#ifdef ALLOW_BCC
Xextern char bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING];
X#endif
X
Xchar *strip_commas();
Xlong fsize();
X
Xint
Xno_editor_edit_the_message(filename)
Xchar *filename;
X{
X /** If the current editor is set to either "builtin" or "none", then
X invoke this program instead. As it turns out, this and the
X routine above have a pretty incestuous relationship (!)...
X **/
X
X FILE *edit_fd;
X char buffer[SLEN], editor_name[SLEN], buf[SLEN];
X int edit_interrupt(), (*oldint)(), (*oldquit)();
X int old_raw;
X
X if (( old_raw = RawState()) == ON)
X Raw(OFF);
X
X if ((edit_fd = fopen(filename, "a")) == NULL) {
X error2("Couldn't open %s for appending [%s]", filename,
X error_name(errno));
X sleep (2);
X dprint(1, (debugfile,
X "Error encountered trying to open file %s;\n", filename));
X dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
X error_description(errno)));
X if ( old_raw == ON)
X Raw(ON);
X return(1);
X }
X
X /** is there already text in this file? **/
X
X if (fsize(edit_fd) > 0L)
X printf(
X "\nPlease continue message, ^D or '.' to end, %cp to list, %c? for help;\n\n",
X escape_char, escape_char);
X else
X printf(
X "\nPlease enter message, ^D or '.' to end, or %c? <RETURN> for help;\n\n",
X escape_char);
X
X oldint = signal(SIGINT, edit_interrupt);
X oldquit = signal(SIGQUIT, edit_interrupt);
X
X interrupts_while_editing = 0;
X
X if (setjmp(edit_location) != 0) {
X if (interrupts_while_editing > 1) {
X Raw(ON);
X
X (void) signal(SIGINT, oldint);
X (void) signal(SIGQUIT, oldquit);
X
X if (edit_fd != NULL) /* insurance... */
X fclose(edit_fd);
X return(1);
X }
X goto more_input; /* read input again, please! */
X }
X
Xraw_input : Raw(ON);
X
Xmore_input: buffer[0] = '\0';
X
X while (optionally_enter(buffer, -1,-1, FALSE) == 0) {
X
X interrupts_while_editing = 0; /* reset to zero... */
X
X if (strcmp(buffer, ".") == 0)
X break; /* '.' is as good as a ^D to us dumb programs :-) */
X if (buffer[0] == escape_char) {
X Raw(OFF);
X switch (tolower(buffer[1])) {
X case '?' : tilde_help();
X printf("(continue)\n"); goto raw_input;
X
X case TILDE: move_left(buffer, 1);
X Raw(ON); goto tilde_input; /*!!*/
X
X case 't' : get_with_expansion("\nTo: ", to, expanded_to, buffer);
X goto raw_input;
X#ifdef ALLOW_BCC
X case 'b' : get_with_expansion("\nBcc: ",bcc,expanded_bcc, buffer);
X goto raw_input;
X#endif
X case 'c' : get_with_expansion("\nCc: ", cc, expanded_cc, buffer);
X goto raw_input;
X case 's' : get_with_expansion("\nSubject: ", subject,NULL,buffer);
X goto raw_input;
X
X case 'h' : get_with_expansion("\nTo: ", to, expanded_to, NULL);
X get_with_expansion("Cc: ", cc, expanded_cc, NULL);
X#ifdef ALLOW_BCC
X get_with_expansion("Bcc: ", bcc,expanded_bcc, NULL);
X#endif
X get_with_expansion("Subject: ", subject,NULL, NULL);
X goto raw_input;
X
X case 'r' : read_in_file(edit_fd, (char *) buffer + 2, 1);
X goto raw_input;
X case 'e' : if (strlen(emacs_editor) > 0)
X if (access(emacs_editor, ACCESS_EXISTS) == 0) {
X strcpy(buffer, editor);
X strcpy(editor, emacs_editor);
X fclose(edit_fd);
X (void) edit_the_message(filename,0);
X strcpy(editor, buffer);
X edit_fd = fopen(filename, "a");
X printf("(continue entering message, ^D or '.' to end)\n");
X goto raw_input;
X }
X else
X printf(
X "\n(Can't find Emacs on this system! continue)\n");
X else
X printf(
X "\n(Don't know where Emacs would be...continue)\n");
X goto raw_input;
X
X case 'v' : strcpy(buffer, editor);
X strcpy(editor, default_editor);
X fclose(edit_fd);
X (void) edit_the_message(filename,0);
X strcpy(editor, buffer);
X edit_fd = fopen(filename, "a");
X printf("(continue entering message, ^D or '.' to end)\n");
X goto raw_input;
X
X case 'o' : printf("\nPlease enter the name of the editor : ");
X editor_name[0] = '\0';
X optionally_enter(editor_name,-1,-1, FALSE);
X if (strlen(editor_name) > 0) {
X strcpy(buffer, editor);
X strcpy(editor, editor_name);
X fclose(edit_fd);
X (void) edit_the_message(filename,0);
X strcpy(editor, buffer);
X edit_fd = fopen(filename, "a");
X printf("(continue entering message, ^D or '.' to end)\n");
X goto raw_input;
X }
X printf("(continue)\n");
X goto raw_input;
X
X case '<' : putchar('\n');
X if (strlen(buffer) < 3)
X (void) printf(
X "(you need to use a specific command here. Continue...)\n");
X else {
X sprintf(buf, " > %s.%d 2>&1", temp_edit, getpid());
X strcat(buffer, buf);
X (void) system_call((char *) buffer+2, SH);
X sprintf(buffer, "~r %s.%d", temp_edit, getpid());
X read_in_file(edit_fd, (char *) buffer + 3, 0);
X (void) unlink((char *) buffer+3);
X }
X goto raw_input;
X
X case '!' : putchar('\n');
X if (strlen(buffer) < 3)
X (void) system_call(shell, USER_SHELL);
X else
X (void) system_call((char *) buffer+2, USER_SHELL);
X printf("(continue)\n");
X goto raw_input;
X
X case 'm' : /* same as 'f' but with leading prefix added */
X
X case 'f' : /* this can be directly translated into a
X 'readmsg' call with the same params! */
X putchar('\n');
X read_in_messages(edit_fd, (char *) buffer + 1);
X goto raw_input;
X
X case 'p' : /* print out message so far. Soooo simple! */
X print_message_so_far(edit_fd, filename);
X goto raw_input;
X
X default : printf(
X "\n(don't know what %c%c is. Try %c? for help)\n",
X escape_char, buffer[1], escape_char);
X }
X }
X else {
Xtilde_input:
X fprintf(edit_fd, "%s\n", buffer);
X printf("\r\n");
X }
X buffer[0] = '\0';
X }
X
X printf("\n<end-of-message>\n\r\n\r");
X
X if ( old_raw == ON)
X Raw(ON);
X
X (void) signal(SIGINT, oldint);
X (void) signal(SIGQUIT, oldquit);
X
X if (edit_fd != NULL) /* insurance... */
X fclose(edit_fd);
X
X return(0);
X}
X
Xtilde_help()
X{
X /* a simple routine to print out what is available at this level */
X
X int old_raw;
X
X if ( (old_raw = RawState()) == ON)
X Raw(OFF);
X
X printf("\n(Available options at this point are;\n\n");
X printf("\t%c?\tPrint this help menu\n", escape_char);
X if (escape_char == TILDE)
X printf("\t~~\tAdd line prefixed by a single '~' character\n");
X /* doesn't make sense otherwise... */
X
X#ifdef ALLOW_BCC
X printf("\
X\t%cb\tChange the addresses in the Blind-carbon-copy list\n", escape_char);
X#endif
X
X printf("\t%cc\tChange the addresses in the Carbon-copy list\n",
X escape_char);
X printf("\t%ce\tInvoke the Emacs editor on the message, if possible\n",
X escape_char);
X printf("\t%cf\tadd the specified list of messages, or current\n",
X escape_char);
X printf("\t%ch\tchange all available headers (to,cc%s,subject)\n",
X escape_char,
X#ifdef ALLOW_BCC
X ",bcc");
X#else
X "");
X#endif
X printf("\t%cm\tsame as '%cf', but with the current 'prefix'\n",
X escape_char, escape_char);
X printf("\t%co\tInvoke a user specified editor on the message\n",
X escape_char);
X printf("\t%cp\tprint out message as typed in so far\n", escape_char);
X printf("\t%cr\tRead in the specified file\n", escape_char);
X printf("\t%cs\tChange the subject of the message\n", escape_char);
X printf("\t%ct\tChange the addresses in the To list\n",
X escape_char);
X printf("\t%cv\tInvoke the Vi visual editor on the message\n\n",
X escape_char);
X printf(
X "\t%c!\texecute a unix command (or give a shell if no command)\n",
X escape_char);
X printf(
X "\t%c<\texecute a unix command adding the output to the message\n\n",
X escape_char);
X printf(
X "\t. \tby itself on a line (or a control-D) ends the message\n\n");
X
X if ( old_raw == ON)
X Raw(ON);
X}
X
Xread_in_file(fd, filename, show_user_filename)
XFILE *fd;
Xchar *filename;
Xint show_user_filename;
X{
X /** Open the specified file and stream it in to the already opened
X file descriptor given to us. When we're done output the number
X of lines we added, if any... **/
X
X FILE *myfd;
X char myfname[SLEN], buffer[LONG_SLEN];
X register int lines = 0;
X
X while (whitespace(filename[lines])) lines++;
X
X /** expand any shell variables, '~' or other notation... **/
X
X expand_env(myfname, (char *) filename + lines);
X
X if (strlen(myfname) == 0) {
X printf("\n(no filename specified for file read! Continue...)\n");
X return;
X }
X
X if ((myfd = fopen(myfname,"r")) == NULL) {
X printf("\n(Couldn't read file '%s'! Continue...)\n",
X myfname);
X return;
X }
X
X lines = 0;
X
X while (fgets(buffer, LONG_SLEN, myfd) != NULL) {
X lines++;
X fputs(buffer, fd);
X fflush(stdout);
X }
X
X fclose(myfd);
X
X if (show_user_filename)
X printf("\n(added %d line%s from file %s. Please continue...)\n",
X lines, plural(lines), myfname);
X else
X printf("\n(added %d line%s to message. Please continue...)\n",
X lines, plural(lines));
X
X return;
X}
X
Xprint_message_so_far(edit_fd, filename)
XFILE *edit_fd;
Xchar *filename;
X{
X /** This prints out the message typed in so far. We accomplish
X this in a cheap manner - close the file, reopen it for reading,
X stream it to the screen, then close the file, and reopen it
X for appending. Simple, but effective!
X
X A nice enhancement would be for this to -> page <- the message
X if it's sufficiently long. Too much work for now, though.
X **/
X
X char buffer[LONG_SLEN];
X
X fclose(edit_fd);
X
X if ((edit_fd = fopen(filename, "r")) == NULL) {
X printf("\nMayday! Mayday! Mayday!\n");
X printf("\nPanic: Can't open file for reading! Bail!\n");
X emergency_exit();
X }
X
X printf("\nTo: %s\n", format_long(to, 4));
X printf("Cc: %s\n", format_long(cc, 4));
X#ifdef ALLOW_BCC
X printf("Bcc: %s\n", format_long(bcc, 5));
X#endif
X printf("Subject: %s\n\n", subject);
X
X while (fgets(buffer, LONG_SLEN, edit_fd) != NULL)
X printf("%s", buffer);
X
X fclose(edit_fd);
X
X if ((edit_fd = fopen(filename, "a")) == NULL) {
X printf("Mayday! Mayday! Abandon Ship! Aiiieeeeee\n");
X printf("\nPanic: Can't reopen file for appending!\n");
X emergency_exit();
X }
X
X printf("\n(continue entering message, please)\n");
X}
X
Xread_in_messages(fd, buffer)
XFILE *fd;
Xchar *buffer;
X{
X /** Read the specified messages into the open file. If the
X first character of "buffer" is 'm' then prefix it, other-
X wise just stream it in straight...Since we're using the
X pipe to 'readmsg' we can also allow the user to specify
X patterns and such too...
X **/
X
X FILE *myfd, *popen();
X char local_buffer[LONG_SLEN];
X register int lines = 0, add_prefix=0, mindex;
X
X add_prefix = (tolower(buffer[0]) == 'm');
X
X if (strlen(++buffer) == 0) {
X if (mail_only) {
X printf("(you need to specify what message(s) to include)\n");
X return;
X }
X /** else give us the current message number as a default **/
X }
X
X if (mail_only || ! isdigit(buffer[0]))
X sprintf(local_buffer, "%s %s", readmsg, buffer);
X else {
X mindex = atoi(buffer); /* map requested # to real # */
X sprintf(local_buffer, "%s %d", readmsg,
X header_table[mindex-1].index_number);
X }
X
X if ((myfd = popen(local_buffer, "r")) == NULL) {
X printf("(can't get to 'readmsg' command. Sorry...)\n");
X return;
X }
X
X dprint(5, (debugfile, "** readmsg call: \"%s\" **\n", local_buffer));
X
X while (fgets(local_buffer, LONG_SLEN, myfd) != NULL) {
X lines++;
X if (add_prefix)
X fprintf(fd, "%s%s", prefixchars, local_buffer);
X else
X fputs(local_buffer, fd);
X }
X
X pclose(myfd);
X
X if (lines == 0)
X printf("(Couldn't add the requested message. Continue)\n");
X else
X printf("(added %d line%s to message... Please continue)\n",
X lines, plural(lines));
X
X return;
X}
X
Xget_with_expansion(prompt, buffer, expanded_buffer, sourcebuf)
Xchar *prompt, *buffer, *expanded_buffer, *sourcebuf;
X{
X /** This is used to prompt for a new value of the specified field.
X If expanded_buffer == NULL then we won't bother trying to expand
X this puppy out! (sourcebuf could be an initial addition)
X **/
X
X char mybuffer[VERY_LONG_STRING];
X int old_raw;
X
X if ( (old_raw = RawState()) == OFF)
X Raw(ON);
X
X printf(prompt); fflush(stdout); /* output! */
X
X if (sourcebuf != NULL) {
X while (!whitespace(*sourcebuf) && *sourcebuf != '\0')
X sourcebuf++;
X if (*sourcebuf != '\0') {
X while (whitespace(*sourcebuf))
X sourcebuf++;
X if (strlen(sourcebuf) > 0) {
X strcat(buffer, " ");
X strcat(buffer, sourcebuf);
X }
X }
X }
X
X strcpy(mybuffer, buffer);
X
X optionally_enter(buffer, -1, -1, TRUE); /* already data! */
X
X if ( old_raw == OFF)
X Raw(OFF);
X
X putchar('\n');
X
X /** if it's changed and we're supposed to expand addresses... **/
X
X if (strcmp(buffer, mybuffer) != 0 && expanded_buffer != NULL)
X if (build_address(strip_commas(buffer), expanded_buffer))
X printf("%s%s\n\r", prompt, expanded_buffer);
X
X return;
X}
X
Xedit_interrupt()
X{
X /** This routine is called when the user hits an interrupt key
X while in the builtin editor...it increments the number of
X times an interrupt is hit and returns it.
X **/
X
X signal(SIGINT, edit_interrupt);
X signal(SIGQUIT, edit_interrupt);
X
X if (interrupts_while_editing++ == 0)
X printf("(Interrupt. One more to cancel this letter...)\n\r");
X
X longjmp(edit_location, 1); /* get back */
X}
SHAR_EOF
chmod 0444 src/editmsg.c || echo "restore of src/editmsg.c fails"
echo "x - extracting src/elm.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/elm.c &&
X
Xstatic char rcsid[] = "@(#)$Id: elm.c,v 2.1.1.2 88/09/23 syd Exp $";
X
X/*******************************************************************************
X * The Elm Mail System - $Revision: 2.1 $ $State: Exp $
X *
X * This file and all associated files and documentation:
X * (C) Copyright 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: elm.c,v $
X * Revision 2.1 88/09/15 20:28:04 syd
X * checked in with -k by syd at 88.09.15.20.28.04.
X *
X * 88/09/01 Rob Bernardo <gatech!pbhyf.PacBell.COM!rob>
X * 1. It allows the user to use the -z flags and -f [filename] flag
X * 2. Puts elm in raw mode earlier on than before so that any commands
X * 3. forward a message without editing it, fix redraw after no edit
X *
X * 88/08/27 ssw
X * add deluth patches
X *
X * 88/08/27 various
X * changes for j,k commands
X *
X * Revision 2.1 88/07/21 09:58:17 edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X *
X * Revision 2.0 88/06/27 17:24:59 edc
X * The original 2.0 gamma sources as leaked from HP
X *
X *
X *
X ******************************************************************************/
X
X/* Main program of the ELM mail system!
X*/
X
X#include "elm.h"
X
X#ifdef BSD
X#undef toupper
X#undef tolower
X#endif
X
Xlong bytes();
Xchar *format_long();
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X {
X int ch;
X char address[SLEN], to_whom[LONG_SLEN];
X int key_offset; /** Position offset within keyboard string **/
X int redraw, /** do we need to rewrite the entire screen? **/
X nuhead, /** or perhaps just the headers section... **/
X nucurr, /** or just the current message pointer... **/
X nufoot; /** clear lines 16 thru bottom and new menu **/
X int i; /** Random counting variable (etc) **/
X int pageon, /** for when we receive new mail... **/
X last_in_mailbox; /** for when we receive new mail too... **/
X long num; /** another variable for fun.. **/
X
X parse_arguments(argc, argv, to_whom);
X
X if (mail_only) {
X
X initialize(FALSE);
X
X dprint(3, (debugfile, "Mail-only: mailing to\n-> \"%s\"\n",
X format_long(to_whom, 3)));
X (void) sendmsg(to_whom, "", "", TRUE, NO, FALSE);
X leave(0);
X }
X
X initialize(TRUE);
X
X ScreenSize(&LINES, &COLUMNS);
X
X showscreen();
X
X /* mailfile_size = bytes(infile); */ /* done in newmbox.c now */
X
X while (1) {
X redraw = 0;
X nuhead = 0;
X nufoot = 0;
X nucurr = 0;
X if ((num = bytes(infile)) != mailfile_size) {
X dprint(2, (debugfile, "Just received %d bytes more mail (elm)\n",
X num - mailfile_size));
X error("New mail has arrived! Hang on...");
X fflush(stdin); /* just to be sure... */
X last_in_mailbox = message_count;
X pageon = header_page;
X newmbox(2, FALSE, TRUE); /* last won't be touched! */
X clear_error();
X header_page = pageon;
X
X if (on_page(current)) /* do we REALLY have to rewrite? */
X showscreen();
X else {
X update_title();
X ClearLine(LINES-1); /* remove reading message... */
X error2("%d new message%s received",
X message_count - last_in_mailbox,
X plural(message_count - last_in_mailbox));
X }
X /* mailfile_size = num; */
X if (cursor_control)
X transmit_functions(ON); /* insurance */
X }
X
X prompt("Command: ");
X
X CleartoEOLN();
X ch = GetPrompt();
X CleartoEOS();
X#ifdef DEBUG
X if (! movement_command(ch))
X dprint(4, (debugfile, "\nCommand: %c [%d]\n\n", ch, ch));
X#endif
X
X set_error(""); /* clear error buffer */
X
X MoveCursor(LINES-3,strlen("Command: "));
X
X switch (ch) {
X
X case '?' : if (help())
X redraw++;
X else
X nufoot++;
X break;
X
X case '$' : resync(); break;
X
X case ' ' :
X case '+' : header_page++; nuhead++;
X if (move_when_paged && header_page <=
X ((message_count-1) / headers_per_page)) {
X current = header_page*headers_per_page + 1;
X if (selected)
X current = visible_to_index(current)+1;
X }
X break;
X
X case '-' : header_page--; nuhead++;
X if (move_when_paged && header_page >= 0) {
X current = header_page*headers_per_page + 1;
X if (selected)
X current = visible_to_index(current)+1;
X }
X break;
X
X case '=' : if (selected)
X current = visible_to_index(1)+1;
X else
X current = 1;
X if (get_page(current))
X nuhead++;
X else
X nucurr++; break;
X
X case '*' : if (selected)
X current = (visible_to_index(selected)+1);
X else
X current = message_count;
X if (get_page(current))
X nuhead++;
X else
X nucurr++; break;
X
X case '|' : Writechar('|');
X softkeys_off();
X redraw = do_pipe();
X softkeys_on(); break;
X
X case '!' : Writechar('!');
X softkeys_off();
X redraw = subshell();
X softkeys_on(); break;
X
X case '%' : get_return(address);
X clear_error();
X PutLine1(LINES,(COLUMNS-strlen(address))/2,
X "%.78s", address);
X break;
X
X case '/' : if (pattern_match()) {
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X }
X else {
X error("pattern not found!");
X fflush(stdin);
X }
X break;
X
X case '<' : /* scan current message for calendar information */
X#ifdef ENABLE_CALENDAR
X PutLine0(LINES-3, strlen("Command: "),
X "Scan message for calendar entries...");
X scan_calendar();
X#else
X error("Sorry - calendar function disabled");
X fflush(stdin);
X#endif
X break;
X
X case 'a' : if(alias()) redraw++;
X else nufoot++;
X define_softkeys(MAIN); break;
X
X case 'b' : PutLine0(LINES-3, strlen("Command: "),
X "Bounce message");
X fflush(stdout);
X if (message_count < 1) {
X error("No mail to bounce!");
X fflush(stdin);
X }
X else
X nufoot = remail();
X break;
X
X case 'c' : PutLine0(LINES-3, strlen("Command: "),
X "Change mailbox");
X if ((file_changed = leave_mbox(FALSE)) != -1) {
X define_softkeys(CHANGE);
X redraw = newmbox(0, TRUE, TRUE);
X /* mailfile_size = bytes(infile); */
X }
X else {
X file_changed = 0;
X sort_mailbox(message_count, FALSE);
X }
X define_softkeys(MAIN);
X break;
X
X case '^' :
X case 'd' : if (message_count < 1) {
X error("No mail to delete!");
X fflush(stdin);
X }
X else {
X delete_msg((ch == 'd'), TRUE);
X if (resolve_mode) /* move after mail resolved */
X if (current < message_count) {
X i = current;
X while (++current <= message_count &&
X (header_table[current-1].status & DELETED))
X /** continue moving forward, please **/ ;
X if (current > message_count)
X current = i; /* reset - overshot! */
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X }
X }
X break;
X
X case ctrl('D') : if (message_count < 1) {
X error("No mail to delete!");
X fflush(stdin);
X }
X else
X meta_match(DELETED);
X break;
X
X#ifdef ALLOW_MAILBOX_EDITING
X case 'e' : PutLine0(LINES-3,strlen("Command: "),"Edit mailbox");
X if (current > 0) {
X edit_mailbox();
X if (cursor_control)
X transmit_functions(ON); /* insurance */
X }
X else {
X error("Mailbox is empty!");
X fflush(stdin);
X }
X break;
X#else
X case 'e' : error(
X "mailbox editing isn't configured in this version of Elm");
X fflush(stdin);
X break;
X#endif
X
X case 'f' : PutLine0(LINES-3, strlen("Command: "), "Forward");
X define_softkeys(YESNO);
X if (current > 0) {
X if(forward()) redraw++;
X else nufoot++;
X } else {
X error("No mail to forward!");
X fflush(stdin);
X }
X define_softkeys(MAIN);
X break;
X
X case 'g' : PutLine0(LINES-3,strlen("Command: "), "Group reply");
X fflush(stdout);
X if (current > 0) {
X if (header_table[current-1].status & FORM_LETTER) {
X error("Can't group reply to a Form!!");
X fflush(stdin);
X }
X else {
X PutLine0(LINES-3,COLUMNS-40,
X "building addresses...");
X define_softkeys(YESNO);
X redraw = reply_to_everyone();
X define_softkeys(MAIN);
X }
X }
X else {
X error("No mail to reply to!");
X fflush(stdin);
X }
X break;
X
X case 'h' : if (filter)
X PutLine0(LINES-3, strlen("Command: "),
X "Message with headers...");
X else
X PutLine0(LINES-3, strlen("Command: "),"Read message");
X fflush(stdout);
X i = filter;
X filter = FALSE;
X redraw = show_msg(current);
X if (((redraw == ESCAPE) || ( redraw > ' ')) &&
X redraw < 127) {
X while ((redraw == ESCAPE) || (redraw > ' '))
X redraw = process_showmsg_cmd(redraw);
X }
X redraw = 1; /* always */
X filter = i;
X break;
X
X case 'J' : if (selected) {
X if ((current = next_visible(current)) < 0)
X current = visible_to_index(selected)+1;
X }
X else
X current++;
X
X if (get_page(current))
X nuhead++;
X else
X nucurr++; break;
X
X case 'j' : if (selected) {
X if ((current = next_visible(current)) < 0)
X current = visible_to_index(selected)+1;
X }
X else {
X i = current;
X while (++current <= message_count &&
X (header_table[current-1].status & DELETED))
X /** continue moving forward, please **/ ;
X if (current > message_count)
X current = i; /* reset - overshot! */
X }
X if (get_page(current))
X nuhead++;
X else
X nucurr++; break;
X
X case 'K' : if (selected)
X current = previous_visible(current);
X else
X current--;
X
X if (get_page(current))
X nuhead++;
X else
X nucurr++; break;
X
X case 'k' : if (selected)
X current = previous_visible(current);
X else {
X i = current;
X while ( --current > 0 &&
X header_table[current-1].status & DELETED)
X /* continue searching */ ;
X if (current <= 0) current = i;/* reset */
X }
X if (get_page(current))
X nuhead++;
X else
X nucurr++; break;
X
X case 'l' : PutLine0(LINES-3, strlen("Command: "),
X "Limit displayed messages by...");
X if (limit() != 0) {
X nuhead++;
X update_title(); /* poof! */
X }
X else
X nufoot++;
X redraw++;
X break;
X
X case 'm' : PutLine0(LINES-3, strlen("Command: "), "Mail");
X redraw = sendmsg("", "", "", TRUE,allow_forms,FALSE);
X break;
X
X case ctrl('J'):
X case ctrl('M'):PutLine0(LINES-3,strlen("Command: "),"Read Message");
X fflush(stdout);
X define_softkeys(READ);
X
X redraw = show_msg(current);
X if (((redraw == ESCAPE) || (redraw > ' ')) &&
X redraw < 127) {
X
X while ((redraw == ESCAPE) || (redraw > ' '))
X redraw = process_showmsg_cmd(redraw);
X }
X redraw = 1; /* always.. */
X (void) get_page(current);
X break;
X
X case 'n' : PutLine0(LINES-3,strlen("Command: "),"Next Message");
X fflush(stdout);
X define_softkeys(READ);
X
X redraw = show_msg(current);
X if (((redraw == ESCAPE) || (redraw > ' ')) &&
X redraw < 127) {
X
X while ((redraw == ESCAPE) || (redraw > ' '))
X redraw = process_showmsg_cmd(redraw);
X }
X redraw = 1; /* always.. */
X current += redraw;
X if (current > message_count)
X current = message_count;
X (void) get_page(current); /* rewrites ANYway */
X break;
X
X case 'o' : PutLine0(LINES-3, strlen("Command: "), "Options");
X options();
X redraw++; /* always fix da screen... */
X break;
X
X case 'p' : PutLine0(LINES-3, strlen("Command: "), "Print mail");
X fflush(stdout);
X if (message_count < 1) {
X error("No mail to print!");
X fflush(stdin);
X }
X else
X print_msg();
X break;
X
X case 'q' : PutLine0(LINES-3, strlen("Command: "), "Quit");
X
X if (mailfile_size != bytes(infile)) {
X error("New Mail! Quit cancelled...");
X fflush(stdin);
X if (mbox_specified == 0) unlock();
X }
X else
X quit();
X
X break;
X
X case 'r' : PutLine0(LINES-3, strlen("Command: "),
X "Reply to message");
X if (current > 0)
X redraw = reply();
X else {
X error("No mail to reply to!");
X fflush(stdin);
X }
X softkeys_on();
X break;
X
X case '>' : /** backwards compatibility **/
X
X case 's' : if (message_count < 1) {
X error("No mail to save!");
X fflush(stdin);
X }
X else {
X PutLine0(LINES-3, strlen("Command: "),
X "Save Message");
X PutLine0(LINES-3,COLUMNS-40,
X "(Use '?' to list your folders)");
X if (save(&redraw, FALSE) && resolve_mode) {
X
X i = current;
X while (++current <= message_count &&
X (header_table[current-1].status & DELETED))
X /** continue moving forward, please **/ ;
X if (current > message_count)
X current = i; /* reset - overshot! */
X
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X }
X }
X ClearLine(LINES-2);
X break;
X
X case ctrl('T') :
X case 't' : if (message_count < 1) {
X error("no mail to tag!");
X fflush(stdin);
X }
X else if (ch == 't')
X tag_message();
X else
X meta_match(TAGGED);
X break;
X
X case 'u' : if (message_count < 1) {
X error("no mail to mark as undeleted!");
X fflush(stdin);
X }
X else {
X undelete_msg(TRUE);
X if (resolve_mode) /* move after mail resolved */
X if (current < message_count) {
X current++;
X/*************************************************************************
X ** What we've done here is to special case the "U)ndelete" command to
X ** ignore whether the next message is marked for deletion or not. The
X ** reason is obvious upon usage - it's a real pain to undelete a series
X ** of messages without this quirk. Thanks to Jim Davis @ HPLabs for
X ** suggesting this more intuitive behaviour.
X **
X ** The old code, for those people that might want to see what the previous
X ** behaviour was, is:
X
X i = current;
X while (++current <= message_count &&
X (header_table[current-1].status & DELETED))
X /** continue moving forward, please ** / ;
X if (current > message_count)
X current = i; /* reset - we overshot! * /
X**************************************************************************/
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X }
X }
X break;
X
X case ctrl('U') : if (message_count < 1) {
X error("No mail to undelete!");
X fflush(stdin);
X }
X else
X meta_match(UNDELETE);
X break;
X
X case ctrl('Q') :
X case '\177' :
X case 'x' : PutLine0(LINES-3, strlen("Command: "), "Exit");
X fflush(stdout); leave();
X
X case ctrl('L') : redraw++; break;
X
X case EOF : leave(); /* Read failed, control tty died? */
X break;
X
X case '@' : debug_screen(); redraw++; break;
X
X case '#' : if (message_count) {
X debug_message();
X redraw++;
X }
X else {
X error("no mail to check");
X fflush(stdin);
X }
X break;
X
X case NO_OP_COMMAND : break; /* noop for timeout loop */
X
X case ESCAPE : if (cursor_control) {
X key_offset = 1;
X ch = ReadCh();
X
X if ( ch == '[' || ch == 'O')
X {
X ch = ReadCh();
X key_offset++;
X }
X
X if (ch == up[key_offset]) {
X if (selected)
X current = previous_visible(current);
X else {
X i = current;
X while ( --current > 0 &&
X header_table[current-1].status & DELETED)
X /* continue searching */ ;
X if (current <= 0) current = i;/* reset */
X }
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X }
X else if (ch == down[key_offset]) {
X if (selected) {
X if ((current = next_visible(current)) < 0)
X current = visible_to_index(selected)+1;
X }
X else {
X i = current;
X while (++current <= message_count &&
X (header_table[current-1].status & DELETED))
X /** continue moving forward, please **/ ;
X if (current > message_count)
X current = i; /* reset - overshot! */
X }
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X }
X else if (hp_terminal) {
X
X switch (ch) {
X case 'U' : /* <NEXT> */
X header_page++;
X nuhead++;
X if (move_when_paged && header_page
X <= (message_count / headers_per_page)) {
X current = header_page*headers_per_page + 1;
X if (selected)
X current = visible_to_index(current)+1;
X }
X break;
X
X case 'V' : /* <PREV> */
X header_page--;
X nuhead++;
X if (move_when_paged && header_page >= 0) {
X current = header_page*headers_per_page + 1;
X if (selected)
X current = visible_to_index(current)+1;
X }
X break;
X
X case 'h' :
X case 'H' : /* <HOME UP> */
X if (selected)
X current = visible_to_index(1)+1;
X else
X current = 1;
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X break;
X
X case 'F' : /* <HOME DOWN> */
X if (selected)
X current = visible_to_index(selected)+1;
X else
X current = message_count;
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X break;
X
X /** let's continue, what the heck... **/
X
X case 'A' : /* <UP> */
X case 'D' : /* <BACKTAB> */
X case 'i' : /* <LEFT> */
X if (selected)
X current = previous_visible(current);
X else {
X i = current;
X while ( --current > 0 &&
X header_table[current-1].status & DELETED)
X /* continue searching */ ;
X if (current <= 0) current = i;/* reset */
X }
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X break;
X
X case 'B' : /* <UP> */
X case 'I' : /* <BACKTAB> */
X case 'C' : /* <LEFT> */
X if (selected) {
X if ((current = next_visible(current)) < 0)
X current = visible_to_index(selected)+1;
X }
X else {
X i = current;
X while (++current <= message_count &&
X (header_table[current-1].status & DELETED))
X /** continue moving forward, please **/ ;
X if (current > message_count)
X current = i; /* reset - overshot! */
X }
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X break;
X
X default: PutLine2(LINES-3, strlen("Command: "),
X "%c%c", ESCAPE, ch);
X }
X }
X else /* false hit - output */
X PutLine2(LINES-3, strlen("Command: "),
X "%c%c", ESCAPE, ch);
X break;
X }
X
X /* else fall into the default error message! */
X
X default : if (ch > '0' && ch <= '9') {
X PutLine0(LINES-3, strlen("Command: "),
X "New Current Message");
X current = read_number(ch);
X if (selected) {
X if ((current = visible_to_index(current)+1) >
X message_count)
X goto too_big;
X }
X if (get_page(current))
X nuhead++;
X else
X nucurr++;
X }
X else {
X error("Unknown command: Use '?' for commands");
X fflush(stdin);
X }
X }
X
X if (redraw)
X showscreen();
X
X if (current < 1) {
X if (message_count > 0) {
X error("already at message #1!");
X fflush(stdin);
X if (selected)
X current = compute_visible(0); /* get to #0 */
X else
X current = 1;
X }
X else if (current < 0) {
X error("No messages to read!");
X fflush(stdin);
X current = 0;
X }
X }
X else if (current > message_count) {
X if (message_count > 0) {
Xtoo_big:
X if (selected) {
X error2("only %d message%s selected!", selected,
X plural(selected));
X fflush(stdin);
X current = compute_visible(selected);
X }
X else {
X error2("only %d message%s!", message_count,
X plural(message_count));
X dprint(8, (debugfile,"[user selected %d, but only %d msgs]\n",
X current, message_count));
X fflush(stdin);
X current = message_count;
X }
X }
X else {
X error("No messages to read!");
X fflush(stdin);
X current = 0;
X }
X }
X else if (selected && (i=visible_to_index(selected)) > message_count) {
X error2("only %d message%s selected!", selected, plural(selected));
X fflush(stdin);
X current = i+1; /* FIXED! Phew! */
X }
X
X if (nuhead)
X show_headers();
X else if (nucurr)
X show_current();
X else if (nufoot) {
X if (mini_menu) {
X MoveCursor(LINES-7, 0);
X CleartoEOS();
X show_menu();
X }
X else {
X MoveCursor(LINES-4, 0);
X CleartoEOS();
X }
X }
X
X } /* the BIG while loop! */
X}
X
Xdebug_screen()
X{
X /**** spit out all the current variable settings and the table
X entries for the current 'n' items displayed. ****/
X
X register int i, j;
X char buffer[SLEN];
X
X ClearScreen();
X Raw(OFF);
X
X PutLine2(0,0,"Current message number = %d\t\t%d message(s) total\n",
X current, message_count);
X PutLine2(2,0,"Header_page = %d \t\t%d possible page(s)\n",
X header_page, (int) (message_count / headers_per_page) + 1);
X
X PutLine1(4,0,"\nCurrent mailfile is %s.\n\n", infile);
X
X i = header_page*headers_per_page; /* starting header */
X
X if ((j = i + (headers_per_page-1)) >= message_count)
X j = message_count-1;
X
X Write_to_screen(
X"Num From Subject Lines Offset\n\n",0);
X
X while (i <= j) {
X sprintf(buffer,
X "%3d %-16.16s %-40.40s %4d %d\n",
X i+1,
X header_table[i].from,
X header_table[i].subject,
X header_table[i].lines,
X header_table[i].offset);
X Write_to_screen(buffer, 0);
X i++;
X }
X
X Raw(ON);
X
X PutLine0(LINES,0,"Press any key to return: ");
X (void) ReadCh();
X}
X
X
Xdebug_message()
X{
X /**** Spit out the current message record. Include EVERYTHING
X in the record structure. **/
X
X char buffer[SLEN];
X
X ClearScreen();
X Raw(OFF);
X
X Write_to_screen("\t\t\t----- Message %d -----\n\n\n\n", 1,
X current);
X
X Write_to_screen("Lines : %-5d\t\t\tStatus: A C D E F N P T U V\n", 1,
X header_table[current-1].lines);
X Write_to_screen(" \t\t\t c o e x o e r a r i\n", 0);
X Write_to_screen(" \t\t\t t n l p r w i g g s\n", 0);
X Write_to_screen(" \t\t\t n f d d m v d n i\n", 0);
X
X sprintf(buffer,
X "\nOffset: %ld\t\t\t %d %d %d %d %d %d %d %d %d %d\n",
X header_table[current-1].offset,
X (header_table[current-1].status & ACTION) != 0,
X (header_table[current-1].status & CONFIDENTIAL) != 0,
X (header_table[current-1].status & DELETED) != 0,
X (header_table[current-1].status & EXPIRED) != 0,
X (header_table[current-1].status & FORM_LETTER) != 0,
X (header_table[current-1].status & NEW) != 0,
X (header_table[current-1].status & PRIVATE) != 0,
X (header_table[current-1].status & TAGGED) != 0,
X (header_table[current-1].status & URGENT) != 0,
SHAR_EOF
echo "End of part 11"
echo "File src/elm.c is continued in part 12"
echo "12" > 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