From: sources@anomaly.sbs.risc.net
Subject: v01i115 - ELM v2.3.11, Electronic Mail Reader - Part 15/20
Reply-To: sources@anomaly.sbs.risc.net
Organization: The Rhode Island Internet Systems Cooperative Network
Date: Sun, 13 Sep 1992 17:44:44 GMT
Followup-To: biz.sco.general


#!/bin/sh
# this is part.15 (part 15 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file src/newmbox.c continued
#
if test ! -r shar3_seq_.tmp; then
	echo "Please unpack part 1 first!"
	exit 1
fi
(read Scheck
 if test "$Scheck" != 15; then
	echo "Please unpack part $Scheck next!"
	exit 1
 else
	exit 0
 fi
) < shar3_seq_.tmp || exit 1
echo "x - Continuing file src/newmbox.c"
sed 's/^X//' << 'SHAR_EOF' >> src/newmbox.c &&
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	  if (errno != ENOENT ) { /* error on anything but file not exist */
X	    err = errno;
X	    Write_to_screen("\n\rfail on open in newmbox, open %s failed!!\n\r", 1,
X		    cur_folder);
X	    Write_to_screen("** %s - %s. **\n\r", 2,
X		    error_name(err), error_description(err));
X	    dprint(1, (debugfile, "fail on open in newbox, file %s!!\n",
X		    cur_folder));
X	    rm_temps_exit();
X	  }
X	  else {
X	    mailfile_size = 0;         /* must non-existant folder */
X	    message_count = 0;
X	    selected = 0;
X	  }
X	} else {                          /* folder exists, read headers */
X	  read_headers(adds_only);
X	}
X
X	if(!same_file)		/* limit mode off if this is a new file */
X	  selected = 0;
X	if (!adds_only)		/* limit mode off if recreating headers */
X	  selected = 0;		/* because we loose the 'Visible' flag */
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 = rindex(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;
X
X	sprintf(tempfn, "%s%s", default_temp, temp_mbox);
X	if((cp = rindex(mbox, '/')) != NULL) {
X	  cp++;
X	  if (strcmp(cp, "mbox") == 0 || strcmp(cp, "mailbox") == 0 ||
X		strcmp(cp, "inbox") == 0 || *cp == '.')
X	    strcat(tempfn, username);
X	  else
X	    strcat(tempfn, cp);
X	}
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, err;
X	int in_to_list = FALSE, forwarding_mail = FALSE, first_line = TRUE;
X
X	static int first_read = 0;
X#ifdef MMDF
X        int newheader = 0;
X#endif /* MMDF */
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	     rm_temps_exit();
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	     rm_temps_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)
X	    if (fputs(buffer, temp) == EOF) {
X		err = errno;
X		Write_to_screen("\n\rWrite to tempfile %s failed!!\n\r", 1,
X				cur_tempfolder);
X		Write_to_screen("** %s - %s. **\n\r", 2,
X				error_name(err), error_description(err));
X		dprint(1, (debugfile, "Can't write to tempfile %s!!\n",
X			   cur_tempfolder));
X		rm_temps_exit();
X	    }
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#ifdef MMDF
X	    if (!forwarding_mail && strcmp(buffer, MSG_SEPERATOR) != 0 ) {
X#else
X	    if (! first_word(buffer, "From ") && !forwarding_mail) {
X#endif /* MMDF */
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#ifdef MMDF
X	  if (strcmp(buffer, MSG_SEPERATOR) == 0) {
X            newheader = !newheader;
X#else
X	  if (first_word(buffer,"From ")) {
X#endif /* MMDF */
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	      /* 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	      strcpy(current_header->subject, "");	/* clear subj    */
X	      strcpy(current_header->to, "");		/* clear to    */
X	      strcpy(current_header->mailx_status, "");	/* clear status flags */
X	      strcpy(current_header->time_zone, "");	/* clear time zone name */
X	      strcpy(current_header->messageid, "<no.id>"); /* set no id into message id */
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#ifdef MMDF
X	    } else if (newheader) {
X	      current_header = headers[count];
X
X	      current_header->offset = (long) fbytes;
X	      current_header->index_number = count+1;
X
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	      strcpy(current_header->from, "");		/* clear from    */
X	      strcpy(current_header->dayname, "");	/* clear dayname */
X	      strcpy(current_header->month, "");	/* clear month   */
X	      strcpy(current_header->day, "");		/* clear day     */
X	      strcpy(current_header->time, "");		/* clear time    */
X	      strcpy(current_header->year, "");		/* clear year    */
X	      strcpy(current_header->subject, "");	/* clear subj    */
X	      strcpy(current_header->to, "");		/* clear to    */
X	      strcpy(current_header->mailx_status, "");	/* clear status flags */
X	      strcpy(current_header->messageid, "<no.id>"); /* set no id into message id */
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	      dprint(1, (debugfile, 
X			   "\n\n**** Added header record ****\n\n"));
X#endif /* MMDF */
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#ifdef MMDF
X	    if (first_word(buffer,"From "))
X	      real_from(buffer, current_header);
X#endif /* MMDF */
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#ifdef MMDF
X	      parse_arpa_who(buffer, current_header->from, TRUE);
X	      dprint(1, (debugfile, 
X			   "\n\n**** Calling parse_arpa_who for from ****\n\n"));
X#else
X	      parse_arpa_who(buffer, current_header->from, FALSE);
X#endif /* MMDF */
X
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	      dprint(1, (debugfile, 
X			   "\n\n**** Calling parse_arpa_date ****\n\n"));
X	      remove_first_word(buffer);
X	      parse_arpa_date(buffer, current_header);
X	    }
X
X	    /** some status things about the message... **/
X
X	    else if ((first_word(buffer, "Priority:") ||
X		     first_word(buffer, "Importance: 2")) &&
X		   !(first_word(buffer, "Priority: normal") ||
X		     first_word(buffer, "Priority: Normal") ||
X		     first_word(buffer, "Priority: Non-urgent") ||
X		     first_word(buffer, "Priority: non-urgent")))
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 = index(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 (index(current_header->mailx_status, 'R') != NULL)
X		current_header->status &= ~(NEW | UNREAD);
X	      else if (index(current_header->mailx_status, 'O') != NULL) {
X		current_header->status &= ~NEW;
X		current_header->status |= UNREAD;
X	      }
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])) && index(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	  if ((ferror(mailfile)) || (fclose(mailfile) == EOF)) {
X	      err = errno;
X	      Write_to_screen("\n\rClose on folder %s failed!!\n\r", 1,
X			      cur_folder);
X	      Write_to_screen("** %s - %s. **\n\r", 2,
X			      error_name(err), error_description(err));
X	      dprint(1, (debugfile, "Can't close on folder %s!!\n",
X			 cur_folder));
X	      rm_temps_exit();
X	  }
X	  if ((ferror(temp)) || (fclose(temp) == EOF)) {
X	      err = errno;
X	      Write_to_screen("\n\rClose on tempfile %s failed!!\n\r", 1,
X			      cur_tempfolder);
X	      Write_to_screen("** %s - %s. **\n\r", 2,
X			      error_name(err), error_description(err));
X	      dprint(1, (debugfile, "Can't close on tempfile %s!!\n",
X			 cur_tempfolder));
X	      rm_temps_exit();
X	  }
X	  /* sanity check on append - is resulting temp file longer??? */
X	  if ( bytes(cur_tempfolder) != mailfile_size) {
X	     Write_to_screen(
X	       "\n\rnewmbox - length of mbox. != spool mailbox length!!\n\r",
X		0);
X	    dprint(0, (debugfile, "newmbox - mbox. != spool mail length"));
X	    rm_temps_exit();
X	  }
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
echo "File src/newmbox.c is complete" &&
# ============= src/opt_utils.c ==============
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 4.1 90/04/28 22:43:37 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	opt_utils.c,v $
X * Revision 4.1  90/04/28  22:43:37  syd
X * checkin of Elm 2.3 as of Release PL0
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 PWDINSYS
X#  include <sys/pwd.h>
X#else
X#  include <pwd.h>
X#endif
X
X#ifdef BSD
X#undef tolower
X#undef toupper
X#endif
X
X#ifndef GETHOSTNAME
X# ifdef DOUNAME
X#  include <sys/types.h>
X#  include <sys/utsname.h>
X# endif
X#endif
X
Xchar *index();
X
X#ifndef GETHOSTNAME
X
Xgethostname(cur_hostname,size) /* get name of current host */
Xchar *cur_hostname;
Xint size;
X{
X	/** Return the name of the current host machine. **/
X
X#if defined(XENIX) & !defined(DOUNAME)
X	char    buf[32];
X	FILE    *fp;
X	char    *p;
X
X	if ((fp = fopen("/etc/systemid", "r")) != 0) {
X	  fgets(buf, sizeof(buf) - 1, fp);
X	  fclose(fp);
X	  if ((p = index(buf, '\n')) != NULL)
X	    *p = '\0';
X	  (void) strncpy(cur_hostname, buf, size - 1);
X	  cur_hostname[size - 1] = '\0';
X	  return 0;
X	}
X
X#else   /* XENIX */
X
X#ifdef DOUNAME
X	/** This routine compliments of Scott McGregor at the HP
X	    Corporate Computing Center **/
X     
X	int uname();
X	struct utsname name;
X
X	(void) uname(&name);
X	(void) strncpy(cur_hostname,name.nodename,size-1);
X#else
X	(void) strncpy(cur_hostname, HOSTNAME, size-1);
X#endif	/* DOUNAME */
X
X	cur_hostname[size - 1] = '\0';
X	return 0;
X
X#endif  /* XENIX */
X}
X
X#endif  /* GETHOSTNAME */
X
X
Xgethostdomain(hostdom, size)    /* get domain of current host */
Xchar *hostdom;
Xint size;
X{
X	char    buf[64];
X	FILE    *fp;
X	char    *p;
X
X	if (size < 2)
X	  return -1;
X
X	if ((fp = fopen(hostdomfile, "r")) != 0) {
X	  fgets(buf, sizeof(buf) - 1, fp);
X	  fclose(fp);
X	  if ((p = index(buf, '\n')) != NULL)
X	    *p = '\0';
X	}
X	else {
X	  strncpy(buf, DEFAULT_DOMAIN, sizeof(buf) - 1);
X	}
X	if (buf[0] != '\0' && buf[0] != '.') {
X	  *hostdom++ = '.';
X	  --size;
X	}
X	(void) strncpy(hostdom, buf, size - 1);
X	hostdom[size - 1] = '\0';
X
X	return 0;
X}
X
X
X#ifdef NEED_CUSERID
X
Xchar *cuserid(uname)
X     char *uname;
X{
X	/** Added for compatibility with Bell systems, this is the last-ditch
X	    attempt to get the users login name, after getlogin() fails.  It
X	    instantiates "uname" to the name of the user...(it also tries
X	    to use "getlogin" again, just for luck)
X	**/
X	/** This wasn't really compatible.  According to our man page, 
X	 ** It was inconsistent.  If the parameter is NULL then you return
X	 ** the name in a static area.  Else the ptr is supposed to be a
X	 ** pointer to l_cuserid bytes of memory [probally 9 bytes]...
X	 ** It's not mention what it should return if you copy the name
X	 ** into the array, so I chose NULL.
X	 ** 					Sept 20, 1988
X	 **					**WJL**
X	 **/
X
X  struct passwd *password_entry;
X#ifndef _POSIX_SOURCE
X  struct passwd *getpwuid();
X#endif
X  char   *name, *getlogin();
X  static char buf[10];
X  register returnonly = 0;
X  
X  if (uname == NULL) ++returnonly;
X  
X  if ((name = getlogin()) != NULL) {
X    if (returnonly) {
X      return(name);
X    } else {
X      strcpy(uname, name);
X      return name;
X    }
X  } 
X  else 
X    if (( password_entry = getpwuid(getuid())) != NULL) 
X      {
X	if (returnonly) 
X	  {
X	    return(password_entry->pw_name);
X	  }
X	else 
X	  {
X	    strcpy(uname, password_entry->pw_name);
X	    return name;
X	  }
X      } 
X    else 
X      {
X	return NULL;
X      }
X}
X
X#endif
X
X#if defined(BSD) && !defined(_POSIX_SOURCE)
X
X/** some supplementary string functions for Berkeley Unix systems **/
X
Xint
Xtolower(ch)
Xchar ch;
X{
X	/** This should be a macro call, but if you use this as a macro
X	    calls to 'tolower' where the argument is a function call will
X	    cause the function to be called TWICE which is obviously the
X	    wrong behaviour.  On the other hand, to just blindly translate
X	    assuming the character is always uppercase can cause BIG
X	    problems, so...
X	**/
X
X	return ( isupper(ch) ? ch - 'A' + 'a' : ch );
X}
X
Xint
Xtoupper(ch)
Xchar ch;
X{
X	/** see comment for above routine - tolower() **/
X
X	return ( islower(ch) ? ch - 'a' + 'A' : ch );
X}
X
Xchar *strtok(source, keys)
Xchar *source, *keys;
X{
X	/** This function returns a pointer to the next word in source
X	    with the string considered broken up at the characters 
X	    contained in 'keys'.  Source should be a character pointer
X	    when this routine is first called, then NULL subsequently.
X	    When strtok has exhausted the source string, it will 
X	    return NULL as the next word. 
X
X	    WARNING: This routine will DESTROY the string pointed to
X	    by 'source' when first invoked.  If you want to keep the
X	    string, make a copy before using this routine!!
X	 **/
X
X	register int  last_ch;
X	static   char *sourceptr;
X		 char *return_value;
X
X	if (source != NULL)
X	  sourceptr = source;
X	
X	if (*sourceptr == '\0') 
X	  return(NULL);		/* we hit end-of-string last time!? */
X
X	sourceptr += strspn(sourceptr, keys);	/* skip leading crap */
X	
X	if (*sourceptr == '\0') 
X	  return(NULL);		/* we've hit end-of-string */
X
X	last_ch = strcspn(sourceptr, keys);	/* end of good stuff */
X
X	return_value = sourceptr;		/* and get the ret   */
X
X	sourceptr += last_ch;			/* ...value 	     */
X
X	if (*sourceptr != '\0')		/* don't forget if we're at END! */
X	  sourceptr++;			   /* and skipping for next time */
X
X	return_value[last_ch] = '\0';		/* ..ending right    */
X	
X	return((char *) return_value);		/* and we're outta here! */
X}
X
Xchar *strpbrk(source, keys)
Xchar *source, *keys;
X{
X	/** Returns a pointer to the first character of source that is any
X	    of the specified keys, or NULL if none of the keys are present
X	    in the source string. 
X	**/
X
X	register int loc = 0, key_index = 0;
X
X	while (source[loc] != '\0') {
X	  key_index = 0;
X	  while (keys[key_index] != '\0')
X	    if (keys[key_index++] == source[loc])
X	      return((char *) (source + loc));
X	  loc++;
X	}
X	
X	return(NULL);
X}
X
X#endif
X
X#ifndef STRSPN
X
Xstrspn(source, keys)
Xchar *source, *keys;
X{
X	/** This function returns the length of the substring of
X	    'source' (starting at zero) that consists ENTIRELY of
X	    characters from 'keys'.  This is used to skip over a
X	    defined set of characters with parsing, usually. 
X	**/
X
X	register int loc = 0, key_index = 0;
X
X	while (source[loc] != '\0') {
X	  key_index = 0;
X	  while (keys[key_index] != source[loc])
X	    if (keys[key_index++] == '\0')
X	      return(loc);
X	  loc++;
X	}
X
X	return(loc);
X}
X
X#endif
X
X#ifndef STRCSPN
X
Xstrcspn(source, keys)
Xchar *source, *keys;
X{
X	/** This function returns the length of the substring of
X	    'source' (starting at zero) that consists entirely of
X	    characters NOT from 'keys'.  This is used to skip to a
X	    defined set of characters with parsing, usually. 
X	    NOTE that this is the opposite of strspn() above
X	**/
X
X	register int loc = 0, key_index = 0;
X
X	while (source[loc] != '\0') {
X	  key_index = 0;
X	  while (keys[key_index] != '\0')
X	    if (keys[key_index++] == source[loc])
X	      return(loc);
X	  loc++;
X	}
X
X	return(loc);
X}
X
X#endif
X
X#ifndef TEMPNAM
X/* and a tempnam for temporary files */
Xstatic int cnt = 0;
X
Xchar *tempnam( dir, pfx)
X char *dir, *pfx;
X{
X	char space[SLEN];
X	char *newspace;
X
X	char	*malloc();
X
X	if (dir == NULL) {
X		dir = "/usr/tmp";
X	} else if (*dir == '\0') {
X		dir = "/usr/tmp";
X	}
X	
X	if (pfx == NULL) {
X		pfx = "";
X	}
X
X	sprintf(space, "%s%s%d.%d", dir, pfx, getpid(), cnt);
X	cnt++;
X	
X	newspace = malloc(strlen(space) + 1);
X	if (newspace != NULL) {
X		strcpy(newspace, space);
X	}
X	return newspace;
X}
X
X#endif
X
X#ifndef GETOPT
X
X/*LINTLIBRARY*/
X#define NULL	0
X#define EOF	(-1)
X#define ERR(s, c)	if(opterr){\
X	extern int strlen(), write();\
X	char errbuf[2];\
X	errbuf[0] = c; errbuf[1] = '\n';\
X	(void) write(2, argv[0], (unsigned)strlen(argv[0]));\
X	(void) write(2, s, (unsigned)strlen(s));\
X	(void) write(2, errbuf, 2);}
X
Xextern int strcmp();
X
Xint	opterr = 1;
Xint	optind = 1;
Xint	optopt;
Xchar	*optarg;
X
Xint
Xgetopt(argc, argv, opts)
Xint	argc;
Xchar	**argv, *opts;
X{
X	static int sp = 1;
X	register int c;
X	register char *cp;
X
X	if(sp == 1)
X		if(optind >= argc ||
X		   argv[optind][0] != '-' || argv[optind][1] == '\0')
X			return(EOF);
X		else if(strcmp(argv[optind], "--") == NULL) {
X			optind++;
X			return(EOF);
X		}
X	optopt = c = argv[optind][sp];
X	if(c == ':' || (cp=index(opts, c)) == NULL) {
X		ERR(": illegal option -- ", c);
X		if(argv[optind][++sp] == '\0') {
X			optind++;
X			sp = 1;
X		}
X		return('?');
X	}
X	if(*++cp == ':') {
X		if(argv[optind][sp+1] != '\0')
X			optarg = &argv[optind++][sp+1];
X		else if(++optind >= argc) {
X			ERR(": option requires an argument -- ", c);
X			sp = 1;
X			return('?');
X		} else
X			optarg = argv[optind++];
X		sp = 1;
X	} else {
X		if(argv[optind][++sp] == '\0') {
X			sp = 1;
X			optind++;
X		}
X		optarg = NULL;
X	}
X	return(c);
X}
X
X#endif
X
X#ifndef RENAME
Xint rename(tmpfname, fname)
Xchar *tmpfname, *fname;
X{
X	int status;
X
X	(void) unlink(fname);
X	if ((status = link(tmpfname, fname)) != 0)
X		return(status);
X	(void) unlink(tmpfname);
X	return(0);
X}
X#endif
SHAR_EOF
# ============= src/options.c ==============
echo "x - extracting src/options.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/options.c &&
X
Xstatic char rcsid[] = "@(#)$Id: options.c,v 4.1 90/04/28 22:43:38 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	options.c,v $
X * Revision 4.1  90/04/28  22:43:38  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** This set of routines allows the alteration of a number of paramaters
X    in the Elm mailer, including the following;
X
X	calendar-file	<where to put calendar entries>
X	display pager	<how to page messages>
X	editor		<name of composition editor>
X	folder-dir	<folder directory>
X	sort-by		<how to sort folders>
X	sent-mail	<file to save outbound message copies to>
X	printmail	<how to print messages>
X	full_username	<your full user name for outgoing mail>
X
X	arrow-cursor	<on or off>
X	menu-display    <on or off>
X
X	user-level	<BEGINNER|INTERMEDIATE|EXPERT>
X        names-only      <on or off>
X	
X    And others as they seem useful.
X
X**/
X
X#include "headers.h"
X
X#ifdef BSD
X#undef tolower
X#endif
X
X#undef onoff
X#define   onoff(n)	(n == 1? "ON ":"OFF")
X
Xchar *one_liner_for(), *level_name();
Xunsigned long sleep();
X
Xoptions()
X{
X	/** change options... **/
X	/* return:
X	 *	> 0	if restort was done - to indicate we might need to
X	 *	 	change the page of our headers as a consequence
X	 *		of the new sort order
X	 *	< 0	if user entered 'x' to quit elm immediately
X	 *	0	otherwise
X	 */
X
X	int	ch,
X	     	resort = 0;
X	char	*strcpy(),
X	     	temp[SLEN];	/* needed when an option is run through
X				 * expand_env(), because that function
X				 * is destructive of the original
X				 */
X
X	display_options();
X
X	clearerr(stdin);
X
X	while(1) {
X	  ClearLine(LINES-4);
X
X	  Centerline(LINES-4,
X "Select first letter of option line, '>' to save, or 'i' to return to index.");
X
X	  PutLine0(LINES-2, 0, "Command: ");
X
X	  ch = ReadCh();
X	  ch = tolower(ch);
X
X	  clear_error();	/* remove possible "sorting" message etc... */ 
X
X	  one_liner(one_liner_for(ch));
X
X	  switch (ch) {
X	    case 'c' : optionally_enter(raw_calendar_file, 2, 23, FALSE, FALSE);
X		       strcpy(temp, raw_calendar_file);
X		       expand_env(calendar_file, temp);
X		       break;
X	    case 'd' : optionally_enter(raw_pager, 3, 23, FALSE, FALSE); 
X		       strcpy(temp, raw_pager);
X		       expand_env(pager, temp);
X		       clear_pages = (equal(pager, "builtin+") || 
X			             equal(pager, "internal+")); 
X		       break;
X	    case 'e' : optionally_enter(raw_editor, 4, 23, FALSE, FALSE);
X		       strcpy(temp, raw_editor);
X		       expand_env(editor, temp);
X	               break;
X	    case 'f' : optionally_enter(raw_folders, 5, 23, FALSE, FALSE);
X		       strcpy(temp, raw_folders);
X		       expand_env(folders, temp);
X		       break;
X	    case 's' : if(change_sort(6,23)) resort++;			break;
X	    case 'o' : optionally_enter(raw_sentmail, 7, 23, FALSE, FALSE);
X		       strcpy(temp, raw_sentmail);
X		       expand_env(sent_mail, temp);
X		       break;
X	    case 'p' : optionally_enter(raw_printout, 8, 23, FALSE, FALSE);
X		       strcpy(temp, raw_printout);
X		       expand_env(printout, temp);
X		       break;
X	    case 'y' : optionally_enter(full_username, 9, 23, FALSE, FALSE);
X		       break;
X	    case 'a' : on_or_off(&arrow_cursor, 12, 23); 		break;
X	    case 'm' : on_or_off(&mini_menu, 13, 23);			
X		       headers_per_page = LINES - (mini_menu ? 13 : 8); break;
X
X	    case 'u' : switch_user_level(&user_level,15, 23);		break;
X	    case 'n' : on_or_off(&names_only, 16, 23);			break;
X	
X	    case '?' : options_help(); 
X	               PutLine0(LINES-2,0,"Command: ");			break;
X	   
X	    case '>' : printf("Save options in .elm/elmrc...");
X		       fflush(stdout);    save_options();		break;
X
X	    case 'x' :	return(-1);	/* exit elm */
X	    case 'q' :	/* pop back up to previous level, in this case == 'i' */
X	    case 'i' :  /* return to index screen */
X			return(resort ? 1 : 0);
X	    case ctrl('L'): display_options();				break;
X	    default: error("Command unknown!");
X	  }
X
X	}
X}
X	
Xdisplay_options()
X{
X	/** Display all the available options.. **/
X	
X	char *sort_name();
X	
X	ClearScreen();
X	Centerline(0,"-- ELM Options Editor --");
X
X#ifdef ENABLE_CALENDAR
X	PutLine1(2, 0, "C)alendar file       : %s", raw_calendar_file);
X#endif
X	PutLine1(3, 0, "D)isplay mail using  : %s", raw_pager);
X	PutLine1(4, 0, "E)ditor              : %s", raw_editor);
X	PutLine1(5, 0, "F)older directory    : %s", raw_folders);
X	PutLine1(6, 0, "S)orting criteria    : %s", sort_name(FULL));
X	PutLine1(7, 0, "O)utbound mail saved : %s", raw_sentmail);
X	PutLine1(8, 0, "P)rint mail using    : %s", raw_printout);
X	PutLine1(9, 0, "Y)our full name      : %s", full_username);
X
X	PutLine1(12,0, "A)rrow cursor        : %s", onoff(arrow_cursor));
X	PutLine1(13,0, "M)enu display        : %s", onoff(mini_menu));
X
X	PutLine1(15,0, "U)ser level          : %s", level_name(user_level));
X	PutLine1(16,0, "N)ames only          : %s", onoff(names_only));
X}
X
Xon_or_off(var, x, y)
Xint *var, x,y;
X{
X	/** 'var' field at x.y toggles between on and off... **/
X
X	char ch;
X
X     	PutLine0(x, y+6, 
X		"(use <space> to toggle, any other key to leave)");
X
X	MoveCursor(x,y+3);	/* at end of value... */
X
X	do {
X	  ch = ReadCh();
X
X	  if (ch == SPACE) {
X	    *var = ! *var;
X	    PutLine0(x,y, onoff(*var));
X	  }
X	} while (ch == SPACE);
X
X	MoveCursor(x,y+4); 	CleartoEOLN();	/* remove help prompt */
X}
X
X
Xswitch_user_level(ulevel, x, y)
Xint *ulevel, x, y;
X{
X	/** step through possible user levels... **/
X
X     	PutLine0(x, y+20, "<space> to change");
X
X	MoveCursor(x,y);	/* at end of value... */
X
X	while (ReadCh() == ' ') {
X	  *ulevel = (*ulevel >= 2? 0 : *ulevel + 1);
X	  PutLine1(x,y, "%s", level_name(*ulevel));
X	}
X
X	MoveCursor(x,y+20); 	CleartoEOLN();	/* remove help prompt */
X}
X	
Xchange_sort(x, y)
Xint x,y;
X{
X	/** change the sorting scheme... **/
X	/** return !0 if new sort order, else 0 **/
X	
X	int last_sortby,	/* so we know if it changes... */
X	    sign = 1;		/* are we reverse sorting??    */
X	int ch;			/* character typed in ...      */
X
X	last_sortby = sortby;	/* remember current ordering   */
X
X	PutLine0(x, COLUMNS-29, "(SPACE for next, or R)everse)");
X	sort_one_liner(sortby);
X	MoveCursor(x, y);
X
X	do {
X	  ch = ReadCh();
X	  ch = tolower(ch);
X	  switch (ch) {
X	    case SPACE : if (sortby < 0) { 
X	    		   sign = -1; 
X	    		   sortby = - sortby; 
X	  		 }
X			 else sign = 1;		/* insurance! */
X	  		 sortby = sign * ((sortby + 1) % (STATUS+2));
X			 if (sortby == 0) sortby = sign;  /* snicker */
X	  		 PutLine0(x, y, sort_name(PAD));
X			 sort_one_liner(sortby);
X	  		 MoveCursor(x, y);
X			 break;
X
X	    case 'r'   : sortby = - sortby;
X	  		 PutLine0(x, y, sort_name(PAD));
X			 sort_one_liner(sortby);
X	  		 MoveCursor(x, y);
X	 }
X        } while (ch == SPACE || ch == 'r');
X
X	MoveCursor(x, COLUMNS-30);	CleartoEOLN();
X
X	if (sortby != last_sortby) {
X	  error("Resorting folder...");
X	  sleep(1);
X	  sort_mailbox(message_count, 0);
X	}
X	ClearLine(LINES-2);		/* clear sort_one_liner()! */
X	return(sortby-last_sortby);
X}
X
Xone_liner(string)
Xchar *string;
X{
X	/** A single-line description of the selected item... **/
X
X	ClearLine(LINES-4);
X	if (string)
X		Centerline(LINES-4, string);
X}
X
Xsort_one_liner(sorting_by)
Xint sorting_by;
X{
X	/** A one line summary of the particular sorting scheme... **/
X
X	ClearLine(LINES-2);
X
X	switch (sorting_by) {
X	  
X	  case -SENT_DATE : Centerline(LINES-2,
X"This sort will order most-recently-sent to least-recently-sent");	break;
X	  case -RECEIVED_DATE : Centerline(LINES-2,
X"This sort will order most-recently-received to least-recently-received");
X		 	    break;
X	  case -MAILBOX_ORDER : Centerline(LINES-2,
X"This sort will order most-recently-added-to-folder to least-recently");
X		 	    break;
X	  case -SENDER : Centerline(LINES-2,
X"This sort will order by sender name, in reverse alphabetical order");	break;
X	  case -SIZE   : Centerline(LINES-2,
X"This sort will order messages by longest to shortest");		break;
X	  case -SUBJECT : Centerline(LINES-2,
X"This sort will order by subject, in reverse alphabetical order");	break;
X	  case -STATUS  : Centerline(LINES-2,
X"This sort will order by reverse status - Deleted through Tagged...");	break;
X
X	  case SENT_DATE : Centerline(LINES-2,
X"This sort will order least-recently-sent to most-recently-sent");	break;
X	  case RECEIVED_DATE : Centerline(LINES-2,
X"This sort will order least-recently-received to most-recently-received");
X	                    break;
X	  case MAILBOX_ORDER : Centerline(LINES-2,
X"This sort will order least-recently-added-to-folder to most-recently");
X		 	    break;
X	  case SENDER : Centerline(LINES-2,
X		        "This sort will order by sender name");	break;
X	  case SIZE   : Centerline(LINES-2,
X		        "This sort will order messages by shortest to longest");
X			break;
X	  case SUBJECT : Centerline(LINES-2,
X            		"This sort will order messages by subject");	break;
X	  case STATUS  : Centerline(LINES-2,
X"This sort will order by status - Tagged through Deleted...");		break;
X	}
X}
X
Xchar *one_liner_for(c)
Xchar c;
X{
X	/** returns the one-line description of the command char... **/
X
X	switch (c) {
X	    case 'c' : return(
X"This is the file where calendar entries from messages are saved.");
X
X	    case 'd' : return(
X"This is the program invoked to display individual messages (try 'builtin')");
X
X	    case 'e' : return(
X"This is the editor that will be used for sending messages, etc.");
X
X	    case 'f' : return(
X"This is the folders directory used when '=' (etc) is used in filenames");
X
X	    case 'm' : return(
X"This determines if you have the mini-menu displayed or not");
X
X	    case 'n' : return(
X"Whether to display the names and addresses on mail, or names only");
X	    case 'o' : return(
X"This is where copies of outbound messages are saved automatically.");
X
X	    case 'p' : return(
X"This is how printouts are generated.  \"%s\" will be replaced by the filename.");
X
X	    case 's' : return(
X"This is used to specify the sorting criteria for the folders");
X
X	    case 'y' : return(
X"When mail is sent out, this is what your full name will be recorded as.");
X
X	    case 'a' : return(
X"This defines whether the ELM cursor is an arrow or a highlight bar.");
X
X	   case 'u' : return(
X"The level of knowledge you have about the ELM mail system.");
X
X	    default : return(NULL);	/* nothing if we don't know! */
X	}
X}
X
Xoptions_help()
X{
X	/** help menu for the options screen... **/
X
X	char c, *ptr;
X
X	Centerline(LINES-3,
X  "Press the key you want help for, '?' for a key list, or '.' to exit help");
X
X	lower_prompt("Key : ");
X
X	while ((c = ReadCh()) != '.') {
X	  c = tolower(c);
X	  if (c == '?') {
X	     display_helpfile(OPTIONS_HELP);
X	     display_options();
X	     return;
X	  }
X	  if ((ptr = one_liner_for(c)) != NULL)
X	    error2("%c = %s.", c, ptr);
X	  else
X	    error1("%c isn't used in this section.", c);
X	  lower_prompt("Key : ");
X	}
X}
X
Xchar *level_name(n)
Xint n;
X{
X	/** return the 'name' of the level... **/
X
X	switch (n) {
X	  case 0 : return("Beginning User   ");
X	  case 1 : return("Intermediate User");
X	  default: return("Expert User      ");
X	}
X}
SHAR_EOF
# ============= src/out_utils.c ==============
echo "x - extracting src/out_utils.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/out_utils.c &&
X
Xstatic char rcsid[] = "@(#)$Id: out_utils.c,v 4.1 90/04/28 22:43:40 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	out_utils.c,v $
X * Revision 4.1  90/04/28  22:43:40  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** This file contains routines used for output in the ELM program.
X
X**/
X
X#include "headers.h"
X
X
Xstatic char err_buffer[SLEN];		/* store last error message */
X
Xstatic char central_message_buffer[SLEN];
X
Xchar *strcpy();
X
Xshow_last_error()
X{
X	/** rewrite last error message! **/
X
X	error(err_buffer);
X}
X
Xclear_error()
X{
X	MoveCursor(LINES,0);
X	CleartoEOLN();
X	err_buffer[0] = '\0';
X}
X
Xset_error(s)
Xchar *s;
X{
X	strcpy(err_buffer, s);
X}
X
Xerror(s)
Xchar *s;
X{
X	/** outputs error 's' to screen at line 22, centered! **/
X
X	if(batch_only)
X	  printf("%s\n\r", s);
X	else {
X	  MoveCursor(LINES,0);
X	  CleartoEOLN();
X	  PutLine0(LINES,(COLUMNS-strlen(s))/2,s);
X	  fflush(stdout);
X	}
X	strcpy(err_buffer, s);	/* save it too! */
X}
X
X/*VARARGS1*/
X
Xerror1(s, a)
Xchar *s, *a;
X{
X	/** same as error, but with a 'printf' argument **/
X	char buffer[SLEN];
X
X	sprintf(buffer,s,a);
X	error(buffer);
X}
X
X/*VARARGS1*/
X
Xerror2(s, a1, a2)
Xchar *s, *a1, *a2;
X{
X	/** same as error, but with two 'printf' arguments **/
X	char buffer[SLEN];
X
X	sprintf(buffer,s, a1, a2);
X	error(buffer);
X}
X
X/*VARARGS1*/
X
Xerror3(s, a1, a2, a3)
Xchar *s, *a1, *a2, *a3;
X{
X	/** same as error, but with three 'printf' arguments **/
X	char buffer[SLEN];
X
X	sprintf(buffer,s, a1, a2, a3);
X	error(buffer);
X}
X
Xlower_prompt(s)
Xchar *s;
X{
X	/** prompt user for input on LINES-1 line, left justified **/
X
X	PutLine0(LINES-1,0,s);
X	CleartoEOLN();
X}
X
Xprompt(s)
Xchar *s;
X{
X	/** prompt user for input on LINES-3 line, left justified **/
X
X	PutLine0(LINES-3,0,s);
X	CleartoEOLN();
X}
X
X
Xset_central_message(string, arg)
Xchar *string, *arg;
X{
X	/** set up the given message to be displayed in the center of
X	    the current window **/ 
X
X	sprintf(central_message_buffer, string, arg);
X}
X
Xdisplay_central_message()
X{
X	/** display the message if set... **/
X
X	if (central_message_buffer[0] != '\0') {
X	  ClearLine(LINES-15);
X	  Centerline(LINES-15, central_message_buffer);
X	  fflush(stdout);
X	}
X}
X
Xclear_central_message()
X{
X	/** clear the central message buffer **/
X
X	central_message_buffer[0] = '\0';
X}
SHAR_EOF
# ============= src/pattern.c ==============
echo "x - extracting src/pattern.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/pattern.c &&
X
Xstatic char rcsid[] = "@(#)$Id: pattern.c,v 4.1 90/04/28 22:43:42 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	pattern.c,v $
X * Revision 4.1  90/04/28  22:43:42  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/**    General pattern matching for the ELM mailer.     
X
X**/
X
X#include <errno.h>
X
X#include "headers.h"
X
Xstatic char pattern[SLEN] = { "" };
Xstatic char alt_pattern[SLEN] = { "" };
X
Xextern int errno;
X
Xchar *error_name(), *shift_lower(), *strcpy();
X
Xint
Xmeta_match(function)
Xint function;
X{
X	char	ch;
X
X	/** Perform specific function based on whether an entered string 
X	    matches either the From or Subject lines.. 
X	    Return TRUE if the current message was matched, else FALSE.
X	**/
X
X	register int i, tagged=0, count=0, curtag = 0;
X	static char     meta_pattern[SLEN];
X
X	PutLine1(LINES-3, strlen("Command: "), 
X	     "%s messages that match pattern...", 
X	     function==TAGGED?"Tag": function==DELETED?"Delete":"Undelete");
X
X	if (function == TAGGED) {	/* are messages already tagged??? */
X	  for (i=0; i < message_count; i++)
X	    if (ison(headers[i]->status,TAGGED))
X	      tagged++;
X
X	  if (tagged) {
X	    if (tagged > 2) 
X	      PutLine0(LINES-2,0, "Some messages are already tagged.");
X	    else
X	      PutLine0(LINES-2,0, "A message is already tagged.");
X	
X	    Write_to_screen("Remove tag%s? y%c", 2, plural(tagged),BACKSPACE);
X
X	    ch = ReadCh();
X	    if (tolower(ch) != 'n') {	/* remove tags... */
X	      Write_to_screen("Yes.", 0);
X	      for (i=0; i < message_count; i++) {
X	        clearit(headers[i]->status,TAGGED);
X		show_new_status(i);
X	      }
X	    } else
X	      Write_to_screen("No.", 0);
X	  }
X	}
X	
X	PutLine0(LINES-2,0, "Enter pattern: "); CleartoEOLN();
X
X	optionally_enter(meta_pattern, LINES-2, strlen("Enter pattern: "),
X	  FALSE, FALSE);
X
X	if (strlen(meta_pattern) == 0) {
X	  ClearLine(LINES-2);
X	  return(curtag);
X	}
X
X	strcpy(meta_pattern, shift_lower(meta_pattern));   /* lowercase it */
X
X	for (i = 0; i < message_count; i++) {
X	  if (from_matches(i, meta_pattern)) {
X	    if ((selected && headers[i]->status & VISIBLE) || ! selected) {
X	      if (function == UNDELETE)
X	        clearit(headers[i]->status, DELETED);
X	      else
X	        setit(headers[i]->status, function);
X	      show_new_status(i);
X	      if(i == current - 1) curtag++;
X	      count++;
X	    }
X	  }
X	  else if (subject_matches(i, meta_pattern)) {
X	    if ((selected && headers[i]->status & VISIBLE) || ! selected) {
X	      if (function == UNDELETE)
X	        clearit(headers[i]->status, DELETED);
X	      else
X	        setit(headers[i]->status, function);
X	      show_new_status(i);
X	      if(i == current - 1) curtag++;
X	      count++;
X	    }
X	  }
X	}
X
X	ClearLine(LINES-2);	/* remove "pattern: " prompt */
X	
X	if (count > 0)
X	  error3("%s %d messsage%s.", 
X	         function==TAGGED? "Tagged" : 
X		   function==DELETED? "Marked for deletion" : "Undeleted",
X		 count, plural(count));
X	else
X	  error1("No matches. No messages %s.",
X		 function==TAGGED? "tagged" : 
X		   function==DELETED? "marked for deletion": "undeleted");
X
X	return(curtag);
X}
X	  
Xint
Xpattern_match()
X{
X	/** Get a pattern from the user and try to match it with the
X	    from/subject lines being displayed.  If matched (ignoring
X	    case), move current message pointer to that message, if
X	    not, error and return ZERO **/
X
X	register int i;
X
X	PutLine0(LINES-3,40,"/ = Match anywhere in messages.");
X	
X	PutLine0(LINES-1,0, "Match pattern:");
X
X	if (pattern_enter(pattern, alt_pattern, LINES-1, 16, 
X	    "Match pattern (in entire folder):"))
X	  if (strlen(alt_pattern) > 0) {
X	    strcpy(alt_pattern, shift_lower(alt_pattern));
X	    return(match_in_message(alt_pattern));
X	  }
X	  else
X	    return(1);
X	  
X	if (strlen(pattern) == 0) 
X	  return(0);
X	else
X	  strcpy(pattern, shift_lower(pattern));
X
X	for (i = current; i < message_count; i++) {
X	  if (from_matches(i, pattern)) {
X	    if (!selected || (selected && headers[i]->status & VISIBLE)) {
X	      current = ++i;
X	      return(1);
X	    }
X	  }
X	  else if (subject_matches(i, pattern)) {
X	    if (!selected || (selected && headers[i]->status & VISIBLE)) {
X	      current = ++i;
X	      return(1);
X	    }
X	  }
X	}
X
X	return(0);
X}
X
Xint
Xfrom_matches(message_number, pat)
Xint message_number;
Xchar *pat;
X{
X	/** Returns true iff the pattern occurs in it's entirety
X	    in the from line of the indicated message **/
X
X	return( in_string(shift_lower(headers[message_number]->from), 
X		pat) );
X}
X
Xint
Xsubject_matches(message_number, pat)
Xint message_number;
Xchar *pat;
X{
X	/** Returns true iff the pattern occurs in it's entirety
X	    in the subject line of the indicated message **/
X
X	return( in_string(shift_lower(headers[message_number]->subject), 
X		pat) );
X}
X
Xmatch_in_message(pat)
Xchar *pat;
X{
X	/** Match a string INSIDE a message...starting at the current 
X	    message read each line and try to find the pattern.  As
X	    soon as we do, set current and leave! 
X	    Returns 1 if found, 0 if not
X	**/
X
X	char buffer[LONG_STRING];
X	int  message_number, lines, line;
X
X	message_number = current-1;
X
X	error("Searching folder for pattern...");
X
X	while (message_number < message_count) {
X
X	  if (fseek(mailfile, headers[message_number]->offset, 0L) == -1) {
X
X	    dprint(1, (debugfile,
X		"Error: seek %ld bytes into file failed. errno %d (%s)\n",
X		headers[message_number]->offset, errno, 
X		"match_in_message"));
X	    error2("ELM [match] failed looking %ld bytes into file (%s).",
X		   headers[message_number]->offset, error_name(errno));
X	    return(1);	/* fake it out to avoid replacing error message */
X	  }
X
X	  line = 0;
X	  lines = headers[message_number]->lines;
X
X	  while (fgets(buffer, LONG_STRING, mailfile) != NULL && line < lines) {
X	
X	    if(buffer[strlen(buffer)-1] == '\n') line++;
X
X	    if (in_string(shift_lower(buffer), pat)) {
X	      current = message_number+1; 
X	      clear_error();
X	      return(1);
X	    }
X	  }
X
X	  /** now we've passed the end of THIS message...increment and 
X	      continue the search with the next message! **/
X
X	  message_number++;
X	}
X
X	return(0);
X}
SHAR_EOF
# ============= src/pmalloc.c ==============
echo "x - extracting src/pmalloc.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/pmalloc.c &&
X
Xstatic char rcsid[] = "@(#)$Id: pmalloc.c,v 4.1 90/04/28 22:43:43 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	pmalloc.c,v $
X * Revision 4.1  90/04/28  22:43:43  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** This routine contains a cheap permanent version of the malloc call to 
X    speed up the initial allocation of the weedout headers and the uuname 
X    data.  
X
X      This routine is originally from Jim Davis of HP Labs, with some 
X    mods by me.
X**/
X
X#include <stdio.h>
X#include "defs.h"
X
X/*VARARGS0*/
X
Xchar *pmalloc(size)
Xint size; 
X{
X	/** return the address of a specified block **/
X
X	static char *our_block = NULL;
X	static int   free_mem  = 0;
X
X	char   *return_value, *malloc();
X
X	/** if bigger than our threshold, just do the real thing! **/
X
X	if (size > PMALLOC_THRESHOLD) 
X	   return(malloc(size));
X
X	/** if bigger than available space, get more, tossing what's left **/
X
X	if (size > free_mem) {
X	  if ((our_block = malloc(PMALLOC_BUFFER_SIZE)) == NULL) {
X	    fprintf(stderr, "\n\r\n\rCouldn't malloc %d bytes!!\n\r\n\r",
X		    PMALLOC_BUFFER_SIZE);
X	    leave();	
X          }
X	  our_block += 4;  /* just for safety, don't give back true address */
X	  free_mem = PMALLOC_BUFFER_SIZE-4;
X	}
X	
X	return_value  = our_block;	/* get the memory */
X	size = ((size+3)/4)*4;		/* Go to quad byte boundary */
X	our_block += size;		/* use it up      */
X	free_mem  -= size;		/*  and decrement */
X
X	return( (char *) return_value);
X}
SHAR_EOF
# ============= src/quit.c ==============
echo "x - extracting src/quit.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/quit.c &&
X
Xstatic char rcsid[] = "@(#)$Id: quit.c,v 4.1 90/04/28 22:43:44 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	quit.c,v $
X * Revision 4.1  90/04/28  22:43:44  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** quit: leave the current folder and quit the program.
X  
X**/
X
X#include "headers.h"
X#include <errno.h>
X
Xextern int errno;		/* system error number on failure */
X
Xlong bytes();
X
Xquit(prompt)
Xint prompt;
X{
X	/* a wonderfully short routine!! */
X
X	if (leave_mbox(FALSE, TRUE, prompt) == -1)
X	  /* new mail - leave not done - can't change to another file yet
X	   * check for change in mailfile_size in main() will do the work
X	   * of calling newmbox to add in the new messages to the current
X	   * file and fix the sorting sequence that leave_mbox may have
X	   * changed for its own purposes */
X	  return;
X
X	leave();
X}
X
Xint
Xresync()
X{
X	/** Resync on the current folder. Leave current and read it back in.
X	    Return indicates whether a redraw of the screen is needed.
X	 **/
X
X	  if(leave_mbox(TRUE, FALSE, TRUE) ==-1)
X	    /* new mail - leave not done - can't change to another file yet
X	     * check for change in mailfile_size in main() will do the work
X	     * of calling newmbox to add in the new messages to the current
X	     * file and fix the sorting sequence that leave_mbox may have
X	     * changed for its own purposes */
X	    return(FALSE);
X
X	  if ((errno = can_access(cur_folder, READ_ACCESS)) != 0) {
X	    dprint(1, (debugfile,
X		  "Error: given file %s as folder - unreadable (%s)!\n", 
X		  cur_folder, error_name(errno)));
X	    fprintf(stderr,"Can't open folder '%s' for reading!\n", cur_folder);
X	    leave();
X	    }
X
X	  newmbox(cur_folder, FALSE);
X	  return(TRUE);
X}
X
Xchange_file()
X{
X	  /* Prompt user for name of folder to change to.
X	   * If all okay with that folder, leave the current folder.
X	   * If leave goes okay (i.e. no new messages in current folder),
X	   * change to the folder that the user specified.
X	   *
X	   * Return value indicates whether a redraw is needed.
X	   */
X
X	  int redraw = FALSE;
X	  char newfile[SLEN];
X	  static char helpmsg[LONG_STRING];
X
X	  char	*nameof();
X
X
X	  /* get new file name */
X
X	  MoveCursor(LINES-3, 30);
X	  CleartoEOS();
X	  PutLine0(LINES-3, 38, "(Use '?' for help/to list your folders.)");
X	  PutLine0(LINES-2,0,"Change to which folder: ");
X	  while(1) {
X	    newfile[0] = '\0';
X	    (void) optionally_enter(newfile, LINES-2, 24, FALSE, FALSE);
X	    clear_error();
X
X	    if(*newfile == '\0') {	/* if user didn't enter a file name */
X	      MoveCursor(LINES-3, 30);	/* abort changing file process */
X	      CleartoEOS();
X	      return(redraw);
X
X	    } else if (strcmp(newfile, "?") == 0) {
X
X	      /* user wants to list folders */
X	      if(!*helpmsg) {	/* format helpmsg if not yet done */
X
X		strcpy(helpmsg,
X		  "\n\r\n\rEnter: <nothing> to not change to a new folder,");
X		strcat(helpmsg,
X		  "\n\r       '!' to change to your incoming mailbox (");
X		strcat(helpmsg, defaultfile);
X		strcat(helpmsg,
X		  ")\n\r       '>' to change to your \"received\" folder (");
X		strcat(helpmsg, nameof(recvd_mail));
X		strcat(helpmsg,
X		  ")\n\r       '<' to change to your \"sent\" folder (");
X		strcat(helpmsg, nameof(sent_mail));
X		strcat(helpmsg, ")\n\r       or a filename");
X		strcat(helpmsg,
X		  " (leading '=' denotes your folder directory ");
X		strcat(helpmsg, folders);
X		strcat(helpmsg, ").\n\r");
X	      }
X	      list_folders(4, helpmsg);
X	      PutLine0(LINES-2,0,"Change to which folder: ");	/* reprompt */
X	      redraw = TRUE;		/* we'll need to clean the screen */
X
X	    } else {
X
X	      /* user entered a file name - expand it */
X	      if (! expand_filename(newfile, TRUE))
X		continue;	/* prompt again */
X
X	      /* don't accept the same file as the current */
X	      if (strcmp(newfile, cur_folder) == 0) {
X		error("Already reading that folder!");
X		continue;	/* prompt again */
X	      }
X
X	      /* Make sure this is a file the user can open, unless it's the
X	       * default mailfile, which is openable even if empty */
X	      if ((errno = can_access(newfile, READ_ACCESS)) != 0 ) {
X		if (strcmp(newfile, defaultfile) != 0 || errno != ENOENT) {
X		  error1("Can't open folder '%s' for reading!", newfile);
X		  continue; 	/* prompt again */
X		}
X	      }
X	      break;	/* exit loop - we got the name of a good file */
X	    }
X	  }
X
X	  /* All's clear with the new file to go ahead and leave the current. */
X	  MoveCursor(LINES-3, 30);
X	  CleartoEOS();
X
X	  if(leave_mbox(FALSE, FALSE, TRUE) ==-1) {
X	    /* new mail - leave not done - can't change to another file yet
X	     * check for change in mailfile_size in main() will do the work
X	     * of calling newmbox to add in the new messages to the current
X	     * file and fix the sorting sequence that leave_mbox may have
X	     * changed for its own purposes */
X	    return(redraw);
X	  }
X
X	  redraw = 1;
X	  newmbox(newfile, FALSE);
X	  return(redraw);
X}
SHAR_EOF
# ============= src/read_rc.c ==============
echo "x - extracting src/read_rc.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/read_rc.c &&
X
Xstatic char rcsid[] = "@(#)$Id: read_rc.c,v 4.1.1.1 90/06/21 23:28:49 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1.1.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	read_rc.c,v $
X * Revision 4.1.1.1  90/06/21  23:28:49  syd
X * Add apollo check for //node
X * From: Russ Johnson
X * 
X * Revision 4.1  90/04/28  22:43:46  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** This file contains programs to allow the user to have a .elm/elmrc file
X    in their home directory containing any of the following: 
X
X	fullname= <username string>
X	maildir = <directory>
X	tmpdir  = <directory>
X	sentmail = <file>
X	editor  = <editor>
X	receviedmail= <file>
X	calendar= <calendar file name>
SHAR_EOF
echo "End of  part 15"
echo "File src/read_rc.c is continued in part 16"
echo "16" > shar3_seq_.tmp
exit 0
Newsgroups: biz.sco.sources