rsalz@uunet.uu.net (Rich Salz) (04/13/89)
Submitted-by: dsinc!syd@uunet.UU.NET (Syd Weinstein) Posting-number: Volume 18, Issue 96 Archive-name: elm2.2/part17 #!/bin/sh # this is part 17 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file src/mailmsg2.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/mailmsg2.c" sed 's/^X//' << 'SHAR_EOF' >> src/mailmsg2.c X that... 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 FILE *reply, *real_reply; /* second is post-input buffer */ X char *whole_msg_file, *tempnam(); X char filename[SLEN], filename2[SLEN], fname[SLEN], copy_file[SLEN], X very_long_buffer[VERY_LONG_STRING], mailerflags[NLEN]; X int ch; X register int retransmit = FALSE; X int already_has_text = FALSE; /* we need an ADDRESS */ X int need_redraw = 0; X X static int cancelled_msg = 0; X X dprint(4, (debugfile, "\nMailing to \"%s\" (with%s editing)\n", X expanded_to, edit_message? "" : "out")); X X gotten_key = 0; /* ignore previously gotten encryption key */ X X /** first generate the temporary filename **/ X X sprintf(filename,"%s%d",temp_file, getpid()); X X /** if possible, let's try to recall the last message? **/ X X if (! batch_only && copy_msg != FORM && user_level != 0) X retransmit = recall_last_msg(filename, copy_msg, &cancelled_msg, X &already_has_text); X X /** if we're not retransmitting, create the file.. **/ X X if (! retransmit) X if ((reply = fopen(filename,"w")) == NULL) { X dprint(1, (debugfile, X "Attempt to write to temp file %s failed with error %s (mail)\n", X filename, error_name(errno))); X if(batch_only) X printf("Could not create file %s (%s).\n",filename, X error_name(errno)); X else X error2("Could not create file %s (%s).",filename, X error_name(errno)); X return(need_redraw); X } X X chown (filename, userid, groupid); X X /* copy the message from standard input */ X if (batch_only) { X while (fgets(very_long_buffer, VERY_LONG_STRING, stdin) != NULL) X fprintf(reply, "%s", very_long_buffer); X } X X if (copy_msg == FORM) { X sprintf(fname, "%s%d", temp_form_file, getpid()); X fclose(reply); /* we can't retransmit a form! */ X if (access(fname,ACCESS_EXISTS) != 0) { X if(batch_only) X printf("Couldn't find forms file!\n"); X else X error("Couldn't find forms file!"); X return(need_redraw); X } X dprint(4, (debugfile, "-- renaming existing file %s to file %s --\n", X fname, filename)); X rename(fname, filename); X } X else if (copy_msg && ! retransmit) /* if retransmit we have it! */ X if (edit_message) { X copy_message(prefixchars, reply, noheader, FALSE, FALSE); X already_has_text = TRUE; /* we just added it, right? */ X } X else X copy_message("", reply, noheader, FALSE, FALSE); X X if (!batch_only && !retransmit && copy_msg != FORM) { X char *sig; X X if (chloc(expanded_to, '!') == -1 && chloc(expanded_to, '@') == -1) X sig = local_signature; X else X sig = remote_signature; X X if (sig[0]) { X fprintf(reply, "\n-- \n"); /* News 2.11 compatibility? */ X if (sig[0] != '/') X sprintf(filename2, "%s/%s", home, sig); X else X strcpy(filename2, sig); X (void) append(reply, filename2); X already_has_text = TRUE; /* added signature... */ X } X } X X if (! retransmit && copy_msg != FORM) X if (reply != NULL) X (void) fclose(reply); /* on replies, it won't be open! */ X X /** Edit the message **/ X X if (edit_message) X create_readmsg_file(); /* for "readmsg" routine */ X X ch = edit_message? 'e' : ' '; /* drop through if needed... */ X X /* calculate default save_file name */ X if(auto_cc) { X if(save_by_name) X strcpy(copy_file, "="); /* signals save by 'to' logname */ X else X strcpy(copy_file, "<"); /* signals save to sentmail */ X } else *copy_file = '\0'; /* signals to not save a copy */ X X X if (! batch_only) { X do { X switch (ch) { X case 'e': if (edit_the_message(filename, already_has_text)) { X cancelled_msg = TRUE; X return(need_redraw); X } X need_redraw++; X break; X X case 'c': need_redraw += name_copy_file(copy_file); X break; X X case 'h': edit_headers(); X need_redraw++; X break; X X default : /* do nothing */ ; X } X X /** ask that silly question again... **/ X X if ((ch = verify_transmission(filename, &form)) == 'f') { X cancelled_msg = TRUE; X return(need_redraw); X } X X } while (ch != 's'); X X if (form == YES) X if (format_form(filename) < 1) { X cancelled_msg = TRUE; X return(need_redraw); X } X X if ((reply = fopen(filename,"r")) == NULL) { X dprint(1, (debugfile, X "Attempt to open file %s for reading failed with error %s (mail)\n", X filename, error_name(errno))); X error1("Could not open reply file (%s).", error_name(errno)); X return(need_redraw); X } X } X else if ((reply = fopen(filename,"r")) == NULL) { X dprint(1, (debugfile, X "Attempt to open file %s for reading failed with error %s (mail)\n", X filename, error_name(errno))); X printf("Could not open reply file (%s).\n", error_name(errno)); X return(need_redraw); X } X X cancelled_msg = FALSE; /* it ain't cancelled, is it? */ X X /** ask about bounceback if the user wants us to.... **/ X X if (uucp_hops(to) > bounceback && bounceback > 0 && copy_msg != FORM) X if (verify_bounceback() == TRUE) { X if (strlen(cc) > 0) strcat(expanded_cc, ", "); X strcat(expanded_cc, bounce_off_remote(to)); X } X X /** grab a copy if the user so desires... **/ X X if (*copy_file) /* i.e. if copy_file contains a name */ X save_copy(expanded_to, expanded_cc, expanded_bcc, X filename, copy_file, form); X X /** write all header information into whole_msg_file **/ X X if((whole_msg_file=tempnam(temp_dir, "snd.")) == NULL) { X dprint(1, (debugfile, "couldn't make temp file nam! (mail)\n")); X if(batch_only) X printf("Sorry - couldn't make temp file name!\n"); X else if(mail_only) X error("Sorry - couldn't make temp file name."); X else X set_error("Sorry - couldn't make temp file name."); X return(need_redraw); X } X X /** try to write headers to new temp file **/ X X dprint(6, (debugfile, "Composition file='%s' and mail buffer='%s'\n", X filename, whole_msg_file)); X X dprint(2,(debugfile,"--\nTo: %s\nCc: %s\nBcc: %s\nSubject: %s\n---\n", X expanded_to, expanded_cc, expanded_bcc, subject)); X X if ((real_reply= X write_header_info(whole_msg_file, expanded_to, X expanded_cc, expanded_bcc, form == YES, FALSE)) == NULL) { X X /** IT FAILED!! MEIN GOTT! Use a dumb mailer instead! **/ X X dprint(3, (debugfile, "** write_header failed: %s\n", X error_name(errno))); X X if (cc[0] != '\0') /* copies! */ X sprintf(expanded_to,"%s %s", expanded_to, expanded_cc); X X sprintf(very_long_buffer, "( (%s -s \"%s\" %s ; %s %s) & ) < %s", X mailx, subject, strip_parens(strip_commas(expanded_to)), X remove_cmd, filename, filename); X X if(batch_only) X printf("Message sent using dumb mailer %s.\n", mailx); X else X error1("Message sent using dumb mailer %s.", mailx); X sleep(2); /* ensure time to see this prompt! */ X X } X else { X copy_message_across(reply, real_reply, FALSE); X X fclose(real_reply); X X if (cc[0] != '\0') /* copies! */ X sprintf(expanded_to,"%s %s", expanded_to, expanded_cc); X X if (bcc[0] != '\0') { X strcat(expanded_to, " "); X strcat(expanded_to, expanded_bcc); X } X X if (strcmp(sendmail, mailer) == 0 X#ifdef SITE_HIDING X && ! is_a_hidden_user(username)) X#else X ) X#endif X X strcpy(mailerflags, (sendmail_verbose ? smflagsv : smflags)); X else X mailerflags[0] ='\0'; X X sprintf(very_long_buffer,"( (%s %s %s ; %s %s) & ) < %s", X mailer, mailerflags, strip_parens(strip_commas(expanded_to)), X remove_cmd, whole_msg_file, whole_msg_file); X } X X fclose(reply); X X if(batch_only) X printf("Sending mail...\n"); X else { X PutLine0(LINES,0,"Sending mail..."); X CleartoEOLN(); X } X X system_call(very_long_buffer, SH, FALSE); X X /* Unlink temp file now. X * This is a precaution in case the message was encrypted. X * I.e. even though this file is readable by the owner only, X * encryption is supposed to hide things even from someone X * with root privelges. The best we can do is not let this X * file just hang after we're finished with it. X */ X (void)unlink(filename); X X if(batch_only) X printf("Mail sent!\n"); X else if(mail_only) X error("Mail sent!"); X else X set_error("Mail sent!"); X X return(need_redraw); X} X Xmail_form(address, subj) Xchar *address, *subj; X{ X /** copy the appropriate variables to the shared space... */ X X strcpy(subject, subj); X strcpy(to, address); X strcpy(expanded_to, address); X X return(mail(FORM, NO, NO)); X} X Xint Xrecall_last_msg(filename, copy_msg, cancelled_msg, already_has_text) Xchar *filename; Xint copy_msg, *cancelled_msg, *already_has_text; X{ X char ch; X X /** If filename exists and we've recently cancelled a message, X the ask if the user wants to use that message instead! This X routine returns TRUE if the user wants to retransmit the last X message, FALSE otherwise... X **/ X X register int retransmit = FALSE; X X if (access(filename, EDIT_ACCESS) == 0 && *cancelled_msg) { X Raw(ON); X CleartoEOLN(); X if (copy_msg) X PutLine1(LINES-1,0,"Recall last kept message instead? (y/n) y%c", X BACKSPACE); X else X PutLine1(LINES-1,0,"Recall last kept message? (y/n) y%c", X BACKSPACE); X fflush(stdout); X ch = ReadCh(); X if (tolower(ch) != 'n') { X Write_to_screen("Yes.",0); X retransmit++; X *already_has_text = TRUE; X } X else X Write_to_screen("No.",0); X X fflush(stdout); X X *cancelled_msg = 0; X } X X return(retransmit); X} X Xint Xverify_transmission(filename, form_letter) Xchar *filename; Xint *form_letter; X{ X /** Ensure the user wants to send this. This routine returns X the character entered. Modified compliments of Steve Wolf X to add the'dead.letter' feature. X Also added form letter support... X **/ X X FILE *deadfd, *messagefd; X char ch, buffer[SLEN], fname[SLEN]; X int x_coord, y_coord; X X while(1) { X /* clear bottom part of screen */ X MoveCursor(LINES-2,0); X CleartoEOS(); X X /* display prompt and menu according to X * user level and what's available on the menu */ X if (user_level == 0) { X PutLine0(LINES-2,0, X "Please choose one of the following options by parenthesized letter: s"); X GetXYLocation(&x_coord, &y_coord); X y_coord--; /* backspace over default answer */ X Centerline(LINES-1, X "e)dit message, edit h)eaders, s)end it, or f)orget it."); X } else { X PutLine0(LINES-2, 0, "And now: s"); X GetXYLocation(&x_coord, &y_coord); X y_coord--; /* backspace over default answer */ X if (*form_letter == PREFORMATTED) { X Centerline(LINES-1, "Choose h)eaders, s)end, c)opy file, or f)orget."); X } else if (*form_letter == YES) { X Centerline(LINES-1, X "Choose e)dit form, h)eaders, s)end, c)opy file, or f)orget."); X } else if (*form_letter == MAYBE) { X Centerline(LINES-1, X "Choose e)dit message, h)eaders, m)ake form, s)end, c)opy file, or f)orget."); X } else { X Centerline(LINES-1, X "Choose e)dit message, h)eaders, s)end, c)opy file, or f)orget."); X } X } X X /* wait for answer */ X fflush(stdin); X fflush(stdout); X Raw(ON); /* double check... testing only... */ X MoveCursor(x_coord, y_coord); X ch = ReadCh(); X ch = tolower(ch); X X /* process answer */ X switch (ch) { X case 'f': Write_to_screen("Forget",0); X if (mail_only) { X /** try to save it as a dead letter file **/ X save_file_stats(fname); X sprintf(fname, "%s/%s", home, dead_letter); X if ((deadfd = fopen(fname,"a")) == NULL) { X dprint(1, (debugfile, X "\nAttempt to append to deadletter file '%s' failed: %s\n\r", X fname, error_name(errno))); X error("Message not saved, Sorry."); X } X else if ((messagefd = fopen(filename, "r")) == NULL) { X dprint(1, (debugfile, X "\nAttempt to read reply file '%s' failed: %s\n\r", X filename, error_name(errno))); X error("Message not saved, Sorry."); X } else { X /* if we get here we're okay for everything */ X while (fgets(buffer, SLEN, messagefd) != NULL) X fputs(buffer, deadfd); X X fclose(messagefd); X fclose(deadfd); X restore_file_stats(fname); X X error1("Message saved in file \"$HOME/%s\".", X dead_letter); X X } X } else set_error( X "Message kept. Can be restored at next f)orward, m)ail or r)eply."); X break; X X case '\n' : X case '\r' : X case 's' : Write_to_screen("Send",0); X ch = 's'; /* make sure! */ X break; X X case 'm' : if (*form_letter == MAYBE) { X *form_letter = YES; X switch (check_form_file(filename)) { X case -1 : return('f'); X case 0 : *form_letter = MAYBE; /* check later!*/ X error("No fields in form!"); X return('e'); X default : continue; X } X } X else { X Write_to_screen("%c??", 1, 07); /* BEEP */ X sleep(1); X continue; X } X case 'e' : if (*form_letter != PREFORMATTED) { X Write_to_screen("Edit",0); X if (*form_letter == YES) X *form_letter = MAYBE; X } X else { X Write_to_screen("%c??", 1, 07); /* BEEP */ X sleep(1); X continue; X } X break; X X case 'h' : Write_to_screen("Headers",0); X break; X X case 'c' : Write_to_screen("Copy file",0); X break; X X default : Write_to_screen("%c??", 1, 07); /* BEEP */ X sleep(1); X continue; X } X X return(ch); X } X} X XFILE * Xwrite_header_info(filename, long_to, long_cc, long_bcc, form, copy) Xchar *filename, *long_to, *long_cc, *long_bcc; Xint form, copy; X{ X /** Try to open filedesc as the specified filename. If we can, X then write all the headers into the file. The routine returns X 'filedesc' if it succeeded, NULL otherwise. Added the ability X to have backquoted stuff in the users .elmheaders file! X If copy is TRUE, then treat this as the saved copy of outbound X mail. X **/ X X char opentype[2]; X long time(), thetime; X char *ctime(); X static FILE *filedesc; /* our friendly file descriptor */ X X#ifdef SITE_HIDING X char buffer[SLEN]; X int is_hidden_user; /* someone we should know about? */ X#endif X X char *get_arpa_date(); X X if(copy) X strcpy(opentype, "a"); X else X strcpy(opentype, "w"); X X save_file_stats(filename); X if ((filedesc = fopen(filename, opentype)) == NULL) { X dprint(1, (debugfile, X "Attempt to open file %s for writing failed! (write_header_info)\n", X filename)); X dprint(1, (debugfile, "** %s - %s **\n\n", error_name(errno), X error_description(errno))); X error2("Error %s encountered trying to write to %s.", X error_name(errno), filename); X sleep(2); X return(NULL); /* couldn't open it!! */ X } X X restore_file_stats(filename); X X if(copy) { /* Add top line that mailer would add */ X thetime = time((long *) 0); X fprintf(filedesc,"From %s %s", username, ctime(&thetime)); X } X X#ifdef SITE_HIDING X if ( !copy && (is_hidden_user = is_a_hidden_user(username))) { X /** this is the interesting part of this trick... **/ X sprintf(buffer, "From %s!%s %s\n", HIDDEN_SITE_NAME, X username, get_ctime_date()); X fprintf(filedesc, "%s", buffer); X dprint(1,(debugfile, "\nadded: %s", buffer)); X /** so is this perverted or what? **/ X } X#endif X X X /** Subject moved to top of headers for mail because the X pure System V.3 mailer, in its infinite wisdom, now X assumes that anything the user sends is part of the X message body unless either: X 1. the "-s" flag is used (although it doesn't seem X to be supported on all implementations??) X 2. the first line is "Subject:". If so, then it'll X read until a blank line and assume all are meant X to be headers. X So the gory solution here is to move the Subject: line X up to the top. I assume it won't break anyone elses program X or anything anyway (besides, RFC-822 specifies that the *order* X of headers is irrelevant). Gahhhhh.... X **/ X X fprintf(filedesc, "Subject: %s\n", subject); X X fprintf(filedesc, "To: %s\n", format_long(long_to, strlen("To:"))); X X fprintf(filedesc,"Date: %s\n", get_arpa_date()); X X#ifndef DONT_ADD_FROM X# ifdef SITE_HIDING X if (is_hidden_user) X fprintf(filedesc,"From: %s <%s!%s!%s>\n", full_username, X hostname, HIDDEN_SITE_NAME, username); X else X# else X# ifdef INTERNET X# ifdef USE_DOMAIN X fprintf(filedesc,"From: %s <%s@%s%s>\n", full_username, X username, hostname, hostdomain); X# else X fprintf(filedesc,"From: %s <%s@%s>\n", full_username, X username, hostname); X# endif X# else X fprintf(filedesc,"From: %s <%s!%s>\n", full_username, X hostname, username); X# endif X# endif X#endif X X if (cc[0] != '\0') X fprintf(filedesc, "Cc: %s\n", format_long(long_cc, strlen("Cc: "))); X X if (copy && (bcc[0] != '\0')) X fprintf(filedesc, "Bcc: %s\n", format_long(long_bcc, strlen("Bcc: "))); X X if (strlen(action) > 0) X fprintf(filedesc, "Action: %s\n", action); X X if (strlen(priority) > 0) X fprintf(filedesc, "Priority: %s\n", priority); X X if (strlen(expires) > 0) X fprintf(filedesc, "Expires: %s\n", expires); X X if (strlen(reply_to) > 0) X fprintf(filedesc, "Reply-To: %s\n", reply_to); X X if (strlen(in_reply_to) > 0) X fprintf(filedesc, "In-Reply-To: %s\n", in_reply_to); X X if (strlen(user_defined_header) > 0) X fprintf(filedesc, "%s\n", user_defined_header); X X add_mailheaders(filedesc); X X if (form) X fprintf(filedesc, "Content-Type: mailform\n"); X X fprintf(filedesc, "X-Mailer: ELM [version %s]\n\n", version_buff); X X return((FILE *) filedesc); X} X Xcopy_message_across(source, dest, copy) XFILE *source, *dest; Xint copy; X{ X /** Copy the message in the file pointed to by source to the X file pointed to by dest. X If copy is TRUE, treat as a saved copy of outbound mail. **/ X X int crypted = FALSE; /* are we encrypting? */ X int encoded_lines = 0; /* # lines encoded */ X char buffer[SLEN]; /* file reading buffer */ X X while (fgets(buffer, SLEN, source) != NULL) { X if (buffer[0] == '[') { X if (strncmp(buffer, START_ENCODE, strlen(START_ENCODE))==0) X crypted = TRUE; X else if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE))==0) X crypted = FALSE; X else if ((strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0) X || (strncmp(buffer, DONT_SAVE2, strlen(DONT_SAVE2)) == 0)) { X if(copy) break; /* saved copy doesn't want anything after this */ X else continue; /* next line? */ X } X } X else if (crypted) { X if (batch_only) { X printf( X "Sorry. Cannot send encrypted mail in \"batch mode\".\n\r"); X leave(); X } else if (! gotten_key++) X getkey(ON); X else if (! encoded_lines) X get_key_no_prompt(); /* reinitialize.. */ X encode(buffer); X encoded_lines++; X } X X if (copy && (strncmp(buffer, "From ", 5) == 0)) X /* Add in the > to a From on our copy */ X fprintf(dest, ">%s", buffer); X X else if (!copy && strcmp(buffer, ".\n") == 0) X /* Because some mail transport agents take a lone period to X * mean EOF, we add a blank space on outbound message. X */ X fputs(". \n", dest); X else X fputs(buffer, dest); X } X if (copy) fputs("\n", dest); /* ensure a blank line at the end */ X} X Xint Xverify_bounceback() X{ X char ch; X X /** Ensure the user wants to have a bounceback copy too. (This is X only called on messages that are greater than the specified X threshold hops and NEVER for non-uucp addresses.... Returns X TRUE iff the user wants to bounce a copy back.... X **/ X X MoveCursor(LINES,0); X CleartoEOLN(); X PutLine1(LINES,0, X "\"Bounce\" a copy off the remote machine? (y/n) y%c", X BACKSPACE); X fflush(stdin); /* wait for answer! */ X fflush(stdout); X ch = ReadCh(); X if (tolower(ch) != 'y') { X Write_to_screen("No.", 0); X fflush(stdout); X return(FALSE); X } X Write_to_screen("Yes.", 0); X fflush(stdout); X X return(TRUE); X} SHAR_EOF echo "File src/mailmsg2.c is complete" chmod 0444 src/mailmsg2.c || echo "restore of src/mailmsg2.c fails" echo "x - extracting src/mailtime.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/mailtime.c && X Xstatic char rcsid[] = "@(#)$Id: mailtime.c,v 2.6 89/03/25 21:46:46 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.6 $ $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: mailtime.c,v $ X * Revision 2.6 89/03/25 21:46:46 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** This set of routines is used to figure out when the user last read X their mail and to also figure out if a given message is new or not. X X**/ X X#include "headers.h" X X#include <sys/types.h> X#include <sys/stat.h> X#ifdef BSD X# ifdef TMINSYS X# include <sys/time.h> X# else X# include <time.h> X# include <sys/timeb.h> X# endif X#else X# include <time.h> X#endif X Xresolve_received(entry) Xstruct header_rec *entry; X{ X /** Entry has the data for computing the time and date the X message was received. Fix it and return **/ X X switch (tolower(entry->month[0])) { X case 'j' : if (tolower(entry->month[1]) == 'a') X entry->received.month = JANUARY; X else if (tolower(entry->month[2]) == 'n') X entry->received.month = JUNE; X else X entry->received.month = JULY; X break; X case 'f' : entry->received.month = FEBRUARY; X break; X case 'm' : if (tolower(entry->month[2]) == 'r') X entry->received.month = MARCH; X else X entry->received.month = MAY; X break; X case 'a' : if (tolower(entry->month[1]) == 'p') X entry->received.month = APRIL; X else X entry->received.month = AUGUST; X break; X case 's' : entry->received.month = SEPTEMBER; X break; X case 'o' : entry->received.month = OCTOBER; X break; X case 'n' : entry->received.month = NOVEMBER; X break; X case 'd' : entry->received.month = DECEMBER; X break; X } X X sscanf(entry->day, "%d", &(entry->received.day)); X X sscanf(entry->year, "%d", &(entry->received.year)); X if (entry->received.year > 100) entry->received.year -= 1900; X X sscanf(entry->time, "%d:%d", &(entry->received.hour), X &(entry->received.minute)); X} SHAR_EOF chmod 0444 src/mailtime.c || echo "restore of src/mailtime.c fails" echo "x - extracting src/mkhdrs.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/mkhdrs.c && X Xstatic char rcsid[] = "@(#)$Id: mkhdrs.c,v 2.7 89/03/25 21:46:48 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.7 $ $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: mkhdrs.c,v $ X * Revision 2.7 89/03/25 21:46:48 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** This contains all the header generating routines for the ELM X program. X X**/ X X#include "headers.h" X Xextern char in_reply_to[SLEN]; X Xchar *strcpy(); Xunsigned long sleep(); X Xgenerate_reply_to(msg) Xint msg; X{ X /** Generate an 'in-reply-to' message... **/ X char buffer[SLEN]; X X X if (msg == -1) /* not a reply! */ X in_reply_to[0] = '\0'; X else { X if (chloc(headers[msg]->from, '!') != -1) X tail_of(headers[msg]->from, buffer, 0); X else X strcpy(buffer, headers[msg]->from); X sprintf(in_reply_to, "%s; from \"%s\" at %s %s, %s %s", X headers[msg]->messageid, X buffer, X headers[msg]->month, X headers[msg]->day, X headers[msg]->year, X headers[msg]->time); X } X} X Xadd_mailheaders(filedesc) XFILE *filedesc; X{ X /** Add the users .mailheaders file if available. Allow backquoting X in the file, too, for fortunes, etc...*shudder* X **/ X X FILE *fd; X char filename[SLEN], buffer[SLEN]; X X sprintf(filename, "%s/%s", home, mailheaders); X X if ((fd = fopen(filename, "r")) != NULL) { X while (fgets(buffer, SLEN, fd) != NULL) X if (strlen(buffer) < 2) { X dprint(2, (debugfile, X "Strlen of line from .elmheaders is < 2 (write_header_info)")); X error1("Warning: blank line in %s ignored!", filename); X sleep(2); X } X else if (occurances_of(BACKQUOTE, buffer) >= 2) X expand_backquote(buffer, filedesc); X else X fprintf(filedesc, "%s", buffer); X X fclose(fd); X } X} X Xexpand_backquote(buffer, filedesc) Xchar *buffer; XFILE *filedesc; X{ X /** This routine is called with a line of the form: X Fieldname: `command` X and is expanded accordingly.. X **/ X X FILE *fd; X char command[SLEN], command_buffer[SLEN], fname[SLEN], X prefix[SLEN]; X register int i, j = 0; X X for (i=0; buffer[i] != BACKQUOTE; i++) X prefix[j++] = buffer[i]; X prefix[j] = '\0'; X X j = 0; X X for (i=chloc(buffer, BACKQUOTE)+1; buffer[i] != BACKQUOTE;i++) X command[j++] = buffer[i]; X command[j] = '\0'; X X sprintf(fname,"%s%d", temp_print, getpid()); X X sprintf(command_buffer, "%s > %s", command, fname); X X system_call(command_buffer, SH, FALSE); X X if ((fd = fopen(fname, "r")) == NULL) { X error1("Backquoted command \"%s\" in elmheaders failed.", command); X return; X } X X /* If we get a line that is less than 80 - length of prefix then we X can toss it on the same line, otherwise, simply prepend each line X *starting with this line* with a leading tab and cruise along */ X X if (fgets(command_buffer, SLEN, fd) == NULL) X fprintf(filedesc, prefix); X else { X if (strlen(command_buffer) + strlen(prefix) < 80) X fprintf(filedesc, "%s%s", prefix, command_buffer); X else X fprintf(filedesc, "%s\n\t%s", prefix, command_buffer); X X while (fgets(command_buffer, SLEN, fd) != NULL) X fprintf(filedesc, "\t%s", command_buffer); X X fclose(fd); X } X X unlink(fname); /* don't leave the temp file laying around! */ X} SHAR_EOF chmod 0444 src/mkhdrs.c || echo "restore of src/mkhdrs.c fails" echo "x - extracting src/newmbox.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/newmbox.c && X Xstatic char rcsid[] = "@(#)$Id: newmbox.c,v 2.35 89/03/29 09:07:09 syd Exp $"; X X/******************************************************************************* X * The Elm Mail System - $Revision: 2.35 $ $State: Exp $ X * X * Copyright (c) 1988, USENET Community Trust 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: newmbox.c,v $ X * Revision 2.35 89/03/29 09:07:09 syd X * Fix problem with assuming headers start file X * From: Rob X * X * Revision 2.34 89/03/25 21:46:49 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** read new folder **/ X X#include <ctype.h> X#include "headers.h" X X#ifdef BSD X#undef tolower /* we have our own "tolower" routine instead! */ X#endif X X#include <sys/types.h> X#include <sys/stat.h> X#include <errno.h> X X#ifdef BSD /* Berkeley has library elsewhere... */ X# ifdef TMINSYS X# include <sys/time.h> X# else X# include <time.h> X# endif Xchar *malloc(), *realloc(); X#else X# include <time.h> X#endif X Xextern int errno; X Xchar *error_name(), *error_description(); Xchar *malloc(), *realloc(), *strcpy(), *strncpy(), *strrchr(), *strchr(); Xunsigned long sleep(); Xvoid rewind(); Xvoid exit(); Xlong bytes(); X Xint Xnewmbox(new_file, adds_only) Xchar *new_file; Xint adds_only; X{ X /** Read a folder. X X new_file - name of folder to read. It is up to the calling X function to make sure that the file can be X read by the user. This is not checked in this X function. The reason why it is not checked here X is due to the situation where the user wants to X change folders: the new folder must be checked X for access *before* leaving the old one, which X is before this function gets called. X adds_only - set if we only want to read newly added messages to X same old folder. X X **/ X X int same_file; X int new_folder_type; X char new_tempfile[SLEN], buffer[SLEN]; X X /* determine type of new mailfile and calculate temp file name */ X if((new_folder_type = get_folder_type(new_file)) == SPOOL) X mk_temp_mail_fn(new_tempfile, new_file); X else X *new_tempfile = '\0'; X X /* determine whether we are changing files */ X same_file = !(strcmp(new_file, cur_folder)); X X /* If we are changing files and we are changing to a spool file, X * make sure there isn't a temp file for it, because if X * there is, someone else is using ELM to read the new file, X * and we don't want to be reading it at the same time. X */ X if((new_folder_type == SPOOL) && (!same_file)) { X if (access(new_tempfile, ACCESS_EXISTS) != -1) { X if(folder_type != NO_NAME) ClearScreen(); X Centerline(15, X "Hey! An instantiation of ELM is already reading this mail!"); X Centerline(17, X "If this is in error, then you'll need to remove the file:"); X Centerline(18, new_tempfile); X MoveCursor(LINES, 0); /* so shell prompt upon exit is on newline */ X silently_exit(); X } X } X X /* If we were reading a spool file and we are not just reading X * in the additional new messages to the same file, we need to X * remove the corresponding tempfile. X */ X X if((folder_type == SPOOL) && !adds_only) { X if (access(cur_tempfolder, ACCESS_EXISTS) != -1) { X if (unlink(cur_tempfolder) != 0) { X error2("Sorry, can't unlink the temp file %s [%s]!\n\r", X cur_tempfolder, error_name(errno)); X silently_exit(); X } X } X } X X /* Okay! Now establish this new file as THE file */ X strcpy(cur_folder, new_file); X folder_type = new_folder_type; X strcpy(cur_tempfolder, new_tempfile); X X clear_error(); X clear_central_message(); X X if (mailfile != NULL) X (void) fclose(mailfile); /* close it first, to avoid too many open */ X X if ((mailfile = fopen(cur_folder,"r")) == NULL) { X mailfile_size = 0; /* must be empty folder */ X message_count = 0; X } else { X read_headers(adds_only); X } X X if(!same_file) /* limit mode off if this is a new file */ X selected = 0; X X dprint(1, (debugfile, X "New folder %s type %s temp file %s (%s)\n", cur_folder, X (folder_type == SPOOL ? "spool" : "non-spool"), X (*cur_tempfolder ? cur_tempfolder : "none"), "newmbox")); X X return(0); X} X Xint Xget_folder_type(filename) Xchar *filename; X{ X /** returns the type of mailfile filename is X NO_NAME = no name X SPOOL = consisting only of mailhome plus base file name X (no intervening directory name) X NON_SPOOL = a name that is not SPOOL type above X **/ X X char *last_slash; X X /* if filename is null or is of zero length */ X if((filename == NULL) || (*filename == '\0')) X return(NO_NAME); X X /* if filename begins with mailhome, X * and there is a slash in filename, X * and there is a filename after it (i.e. last slash is not last char), X * and the last character of mailhome is last slash in filename, X * it's a spool file . X */ X if((first_word(filename, mailhome)) && X ((last_slash = strrchr(filename, '/')) != NULL) && X (*(last_slash+1) != '\0') && X (filename + strlen(mailhome) - 1 == last_slash)) X return(SPOOL); X /* if file name == default mailbox, its a spool file also X * even if its not in the spool directory. (SVR4) X */ X if (strcmp(filename, defaultfile) == 0) X return(SPOOL); X X return(NON_SPOOL); X} X Xmk_temp_mail_fn(tempfn, mbox) Xchar *tempfn, *mbox; X{ X /** create in tempfn the name of the temp file corresponding to X mailfile mbox. Mbox is presumed to be a file in mailhome; X Strangeness may result if it is not! X **/ X X char *cp; /* will point to '/' preceding filename part of mbox */ X strcpy(tempfn, temp_mbox); X if((cp = strrchr(mbox, '/')) != NULL) X strcat(tempfn, cp+1); X} X Xint Xread_headers(add_new_only) Xint add_new_only; X{ X /** Reads the headers into the headers[] array and leaves the X file rewound for further I/O requests. If the file being X read is a mail spool file (ie incoming) then it is copied to X a temp file and closed, to allow more mail to arrive during X the elm session. If 'add_new_only' is set, the program will copy X the status flags from the previous data structure to the new X one if possible and only read in newly added messages. X **/ X X FILE *temp; X struct header_rec *current_header = NULL; X char buffer[LONG_STRING], *c; X long fbytes = 0L, line_bytes = 0L; X register int line = 0, count = 0, another_count, X subj = 0, copyit = 0, in_header = 0; X int count_x, count_y = 17, new_messages = 0, err; X int in_to_list = FALSE, forwarding_mail = FALSE, first_line = TRUE; X X static int first_read = 0; X X if (folder_type == SPOOL) { X lock(INCOMING); /* ensure no mail arrives while we do this! */ X if (! add_new_only) { X if (access(cur_tempfolder, ACCESS_EXISTS) != -1) { X /* Hey! What the hell is this? The temp file already exists? */ X /* Looks like a potential clash of processes on the same file! */ X unlock(); /* so remove lock file! */ X error("What's this? The temp folder already exists??"); X sleep(2); X error("Ahhhh... I give up."); X silently_exit(); /* leave without tampering with it! */ X } X if ((temp = fopen(cur_tempfolder,"w")) == NULL) { X err = errno; X unlock(); /* remove lock file! */ X Raw(OFF); X Write_to_screen( X "\n\rCouldn't open file %s for use as temp file.\n\r", X 1, cur_tempfolder); X Write_to_screen("** %s - %s. **\n\r", 2, X error_name(err), error_description(err)); X dprint(1, (debugfile, X "Error: Couldn't open file %s as temp mbox. errno %s (%s)\n", X cur_tempfolder, error_name(err), "read_headers")); X leave(); X } X copyit++; X chown(cur_tempfolder, userid, groupid); X chmod(cur_tempfolder, 0700); /* shut off file for other people! */ X } X else { X if ((temp = fopen(cur_tempfolder,"a")) == NULL) { X err = errno; X unlock(); /* remove lock file! */ X Raw(OFF); X Write_to_screen( X "\n\rCouldn't reopen file %s for use as temp file.\n\r", X 1, cur_tempfolder); X Write_to_screen("** %s - %s. **\n\r", 2, X error_name(err), error_description(err)); X dprint(1, (debugfile, X "Error: Couldn't reopen file %s as temp mbox. errno %s (%s)\n", X cur_tempfolder, error_name(err), "read_headers")); X emergency_exit(); X } X copyit++; X } X } X X if (! first_read++) { X ClearLine(LINES-1); X ClearLine(LINES); X if (add_new_only) X PutLine2(LINES, 0, "Reading in %s, message: %d", cur_folder, X message_count); X else X PutLine1(LINES, 0, "Reading in %s, message: 0", cur_folder); X count_x = LINES; X count_y = 22 + strlen(cur_folder); X } X else { X count_x = LINES-2; X PutLine0(LINES-2, 0, "Reading message: 0"); X } X X if (add_new_only) { X if (fseek(mailfile, mailfile_size, 0) == -1) { X err = errno; X Write_to_screen( X "\n\rCouldn't seek to %ld (end of folder) in %s!\n\r", 2, X mailfile_size, cur_folder); X Write_to_screen("** %s - %s. **\n\r", 2, X error_name(err), error_description(err)); X dprint(1, (debugfile, X "Error: Couldn't seek to end of folder %s: (offset %ld) Errno %s (%s)\n", X cur_folder, mailfile_size, error_name(err), "read_headers")); X emergency_exit(); X } X count = message_count; /* next available */ X fbytes = mailfile_size; /* start correctly */ X } X X /** find the size of the folder then unlock the file **/ X X mailfile_size = bytes(cur_folder); X unlock(); X X /** now let's copy it all across accordingly... **/ X X while (fbytes < mailfile_size) { X X if (fgets(buffer, LONG_STRING, mailfile) == NULL) break; X X if (copyit) fputs(buffer, temp); X line_bytes = (long) strlen(buffer); X X /* Fix below to increment line count ONLY if we got a full line. X * Input lines longer than the fgets buffer size would X * get counted each time a subsequent part of them was X * read in. This meant that when the faulty line count was used X * to display the message, part of the next message X * was displayed at the end of the message. X */ X if(buffer[strlen(buffer)-1] == '\n') line++; X X if (fbytes == 0L || first_line) { /* first line of file... */ X if (folder_type == SPOOL) { X if (first_word(buffer, "Forward to ")) { X set_central_message("Mail being forwarded to %s", X (char *) (buffer + 11)); X forwarding_mail = TRUE; X } X } X X /** flush leading blank lines before next test... **/ X if (strlen(buffer) == 1) { X fbytes++; X continue; X } X else X first_line = FALSE; X X if (! first_word(buffer, "From ") && !forwarding_mail) { X PutLine0(LINES, 0, X "\n\rFolder is corrupt!! I can't read it!!\n\r\n\r"); X fflush(stderr); X dprint(1, (debugfile, X "\n\n**** First mail header is corrupt!! ****\n\n")); X dprint(1, (debugfile, "Line is;\n\t%s\n\n", buffer)); X mail_only++; /* to avoid leave() cursor motion */ X leave(); X } X } X X if (first_word(buffer,"From ")) { X X /** allocate new header pointers, if needed... **/ X X if (count >= max_headers) { X struct header_rec **new_headers; X int new_max; X X new_max = max_headers + KLICK; X if (max_headers == 0) { X new_headers = (struct header_rec **) X malloc(new_max * sizeof(struct header_rec *)); X } X else { X new_headers = (struct header_rec **) X realloc(headers, new_max * sizeof(struct header_rec *)); X } X if (new_headers == NULL) { X error1( X "\n\r\n\rCouldn't allocate enough memory! Message #%d.\n\r\n\r", X count); X leave(); X } X headers = new_headers; X while (max_headers < new_max) X headers[max_headers++] = NULL; X } X X /** allocate new header structure, if needed... **/ X X if (headers[count] == NULL) { X struct header_rec *h; X X if ((h = (struct header_rec *) X malloc(sizeof(struct header_rec))) == NULL) { X error1( X "\n\r\n\rCouldn't allocate enough memory! Message #%d.\n\r\n\r", X count); X leave(); X } X headers[count] = h; X } X X if (real_from(buffer, headers[count])) { X current_header = headers[count]; X X current_header->offset = (long) fbytes; X current_header->index_number = count+1; X if (! add_new_only || count >= message_count) { X /* set default status - always 'visible' - and X * if a spool file, presume 'new', otherwise X * 'read', for the time being until overridden X * by a Status: header. X * We presume 'read' for nonspool mailfile messages X * to be compatible messages stored with older versions of elm, X * which didn't support a Status: header. X */ X if(folder_type == SPOOL) X current_header->status = VISIBLE | NEW | UNREAD; X else X current_header->status = VISIBLE; X } X X strcpy(current_header->subject, ""); /* clear subj */ X strcpy(current_header->to, ""); /* clear to */ X strcpy(current_header->mailx_status, ""); /* clear status flags */ X current_header->encrypted = 0; /* clear encrypted */ X current_header->exit_disposition = UNSET; X current_header->status_chgd = FALSE; X X /* Set the number of lines for the _preceding_ message, X * but only if there was a preceding message and X * only if it wasn't calculated already. It would X * have been calculated already if we are only X * reading headers of new messages that have just arrived, X * and the preceding message was one of the old ones. X */ X if ((count) && (!add_new_only || count > message_count)) X headers[count-1]->lines = line; X X count++; X subj = 0; X line = 0; X in_header = 1; X PutLine1(count_x, count_y, "%d", count); X } else if (count == 0) { X /* if this is the first "From" in file but the "From" line is X * not of the proper format, we've got a corrupt folder. X */ X PutLine0(LINES, 0, X "\n\rFolder is corrupt!! I can't read it!!\n\r\n\r"); X fflush(stderr); X dprint(1, (debugfile, X "\n\n**** First mail header is corrupt!! ****\n\n")); X dprint(1, (debugfile, "Line is;\n\t%s\n\n", buffer)); X mail_only++; /* to avoid leave() cursor motion */ X leave(); X } X } X else if (in_header) { X if (first_word(buffer,">From:")) X parse_arpa_who(buffer, current_header->from, FALSE); X else if (first_word(buffer,">From")) X forwarded(buffer, current_header); /* return address */ X else if (first_word(buffer,"Subject:") || X first_word(buffer,"Subj:") || X first_word(buffer,"Re:")) { X if (! subj++) { X remove_first_word(buffer); X copy_sans_escape(current_header->subject, buffer, STRING); X remove_possible_trailing_spaces(current_header->subject); X } X } X else if (first_word(buffer,"From:")) X parse_arpa_who(buffer, current_header->from, FALSE); X X else if (first_word(buffer, "Message-Id:") || X first_word(buffer, "Message-ID:")) { X buffer[strlen(buffer)-1] = '\0'; X strcpy(current_header->messageid, X (char *) buffer + 12); X } X X else if (first_word(buffer, "Expires:")) X process_expiration_date((char *) buffer + 9, X &(current_header->status)); X X /** when it was sent... **/ X X else if (first_word(buffer, "Date:")) X parse_arpa_date(buffer, current_header); X X /** some status things about the message... **/ X X else if (first_word(buffer, "Priority:") || X first_word(buffer, "Importance: 2")) X current_header->status |= URGENT; X else if (first_word(buffer, "Sensitivity: 2")) X current_header->status |= PRIVATE; X else if (first_word(buffer, "Sensitivity: 3")) X current_header->status |= CONFIDENTIAL; X else if (first_word(buffer, "Content-Type: mailform")) X current_header->status |= FORM_LETTER; X else if (first_word(buffer, "Action:")) X current_header->status |= ACTION; X X /** next let's see if it's to us or not... **/ X X else if (first_word(buffer, "To:")) { X in_to_list = TRUE; X current_header->to[0] = '\0'; /* nothing yet */ X figure_out_addressee((char *) buffer +3, X current_header->to); X } X else if (first_word(buffer, "Status:")) { X remove_first_word(buffer); X strncpy(current_header->mailx_status, buffer, WLEN-1); X current_header->mailx_status[WLEN-1] ='\0'; X X c = strchr(current_header->mailx_status, '\n'); X if (c != NULL) X *c = '\0'; X remove_possible_trailing_spaces(current_header->mailx_status); X X /* Okay readjust the status. If there's an 'R', message X * is read; if there is no 'R' but there is an 'O', message X * is unread. In any case it isn't new because a new message X * wouldn't have a Status: header. X */ X if (strchr(current_header->mailx_status, 'R') != NULL) X current_header->status = VISIBLE; X else if (strchr(current_header->mailx_status, 'O') != NULL) X current_header->status = VISIBLE | UNREAD; X } X X else if (buffer[0] == LINE_FEED || buffer[0] == '\0') { X if (in_header) { X in_header = 0; /* in body of message! */ X fix_date(current_header); X } X } X else if (in_header) { X if ((!whitespace(buffer[0])) && strchr(buffer, ':') == NULL) { X in_header = 0; /* in body of message! */ X fix_date(current_header); X } X } X else if (in_to_list == TRUE) { X if (whitespace(buffer[0])) X figure_out_addressee(buffer, current_header->to); X else in_to_list = FALSE; X } X } X if (!in_header && first_word(buffer, START_ENCODE)) X current_header->encrypted = 1; X fbytes += (long) line_bytes; X } X X if (count) X headers[count-1]->lines = line + 1; X X if (folder_type == SPOOL) { X unlock(); /* remove lock file! */ X fclose(mailfile); X fclose(temp); X if ((mailfile = fopen(cur_tempfolder,"r")) == NULL) { X err = errno; X MoveCursor(LINES,0); X Raw(OFF); X Write_to_screen( X "\n\rAugh! Couldn't reopen %s as temp file.\n\r", X 1, cur_tempfolder); X Write_to_screen("** %s - %s. **\n\r", 2, error_name(err), X error_description(err)); X dprint(1, (debugfile, X "Error: Reopening %s as temp file failed! errno %s (%s)\n", X cur_tempfolder, error_name(errno), "read_headers")); X leave(); X } X } X else X rewind(mailfile); X X /* Sort folder *before* we establish the current message, so that X * the current message is based on the post-sort order. X * Note that we have to set the global variable message_count X * before the sort for the sort to correctly keep the correct X * current message if we are only adding new messages here. */ X X message_count = count; X sort_mailbox(count, 1); X X /* Now lets figure what the current message should be. X * If we are only reading in newly added messages from a mailfile X * that already had some messages, current should remain the same. X * If we have a folder of no messages, current should be zero. X * Otherwise, if we have point_to_new on then the current message X * is the first message of status NEW if there is one. X * If we don't have point_to_new on or if there are no messages of X * of status NEW, then the current message is the first message. X */ X if(!(add_new_only && current != 0)) { X if(count == 0) X current = 0; X else { X current = 1; X if (point_to_new) { X for(another_count = 0; another_count < count; another_count++) { X if(ison(headers[another_count]->status, NEW)) { X current = another_count+1; X break; /* first one found give up */ X } X } X } X } X } X get_page(current); X return(count); X} SHAR_EOF chmod 0444 src/newmbox.c || echo "restore of src/newmbox.c fails" echo "x - extracting src/opt_utils.c (Text)" sed 's/^X//' << 'SHAR_EOF' > src/opt_utils.c && X Xstatic char rcsid[] = "@(#)$Id: opt_utils.c,v 2.16 89/03/25 21:46:52 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: opt_utils.c,v $ X * Revision 2.16 89/03/25 21:46:52 syd X * Initial 2.2 Release checkin X * X * X ******************************************************************************/ X X/** This file contains routines that might be needed for the various X machines that the mailer can run on. Please check the Makefile X for more help and/or information. X X**/ X X#include "headers.h" X#include <ctype.h> X X#ifdef BSD X# include <pwd.h> X#undef tolower X#undef toupper X#endif X X#ifndef GETHOSTNAME X# include <sys/types.h> X# include <sys/utsname.h> X#endif X X#ifndef GETHOSTNAME X Xgethostname(hostname,size) /* get name of current host */ Xchar *hostname; SHAR_EOF echo "End of part 17" echo "File src/opt_utils.c is continued in part 18" echo "18" > s2_seq_.tmp exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.