home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume9
/
elm2
/
part08
< prev
next >
Wrap
Text File
|
1987-03-08
|
49KB
|
1,828 lines
Subject: v09i008: ELM Mail System, Part08/19
Newsgroups: mod.sources
Approved: rs@mirror.TMC.COM
Submitted by: Dave Taylor <hplabs!taylor>
Mod.sources: Volume 9, Issue 8
Archive-name: elm2/Part08
#! /bin/sh
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If this archive is complete, you will see the message:
# "End of archive 8 (of 19)."
# Contents: filter/parse.c src/input_utils.c src/mailmsg1.c
# src/options.c utils/answer.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"filter/parse.c\" \(8929 characters\)
if test -f filter/parse.c ; then
echo shar: Will not over-write existing file \"filter/parse.c\"
else
sed "s/^X//" >filter/parse.c <<'END_OF_filter/parse.c'
X/** filter_parse.c **/
X
X/** This is the parser for the filter program. It accepts a wide variety of
X constructs, building the ruleset table as it goes along. Check the
X data structure in filter.h for more information on how the rules are
X stored. The parser is a cunning state-table based program.
X
X (C) Copyright 1986, Dave Taylor
X**/
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "defs.h"
X#include "filter.h"
X
X#define NONE 0
X#define AND 10
X
X#define NEXT_CONDITION 0
X#define GETTING_OP 1
X#define READING_ARGUMENT 2
X#define READING_ACTION 3
X#define ACTION_ARGUMENT 4
X
Xchar *strtok(), *whatname(), *actionname();
X
Xint
Xget_filter_rules()
X{
X /** Given the users home directory, open and parse their rules table,
X building the data structure as we go along.
X returns -1 if we hit an error of any sort...
X **/
X
X FILE *fd; /* the file descriptor */
X char buffer[SLEN], /* fd reading buffer */
X *str, /* ptr to read string */
X *word, /* ptr to 'token' */
X filename[SLEN], /* the name of the ruleset */
X action_argument[SLEN], /* action arg, per rule */
X cond_argument[SLEN]; /* cond arg, per condition */
X int not_condition = FALSE, /* are we in a "not" ?? */
X type=NONE, /* what TYPE of condition? */
X lasttype, /* and the previous TYPE? */
X state = NEXT_CONDITION, /* the current state */
X in_single, in_double, /* for handling spaces. */
X i, /* misc integer for loops */
X relop = NONE, /* relational operator */
X action, /* the current action type */
X line = 0; /* line number we're on */
X
X struct condition_rec *cond, *newcond;
X
X sprintf(filename,"%s/%s", home, filterfile);
X
X if ((fd = fopen(filename,"r")) == NULL) {
X fprintf(stderr,"filter (%s): Couldn't read user filter rules file!\n",
X username);
X return(-1);
X }
X
X cond_argument[0] = action_argument[0] = '\0';
X
X /* Now, for each line... **/
X
X if ((cond = (struct condition_rec *)
X malloc(sizeof(struct condition_rec))) == NULL) {
X fprintf(stderr,"couldn't malloc first condition rec!\n");
X return(-1);
X }
X
X rules[total_rules].condition = cond; /* hooked in! */
X
X while (fgets(buffer, SLEN, fd) != NULL) {
X line++;
X
X if (buffer[0] == '#' || strlen(buffer) < 2)
X continue; /* nothing to look at! */
X
X in_single = in_double = 0;
X
X for (i=0; i < strlen(buffer); i++) {
X if (buffer[i] == '"')
X in_double = ! in_double;
X else if (buffer[i] == '\'')
X in_single = ! in_single;
X if ((in_double || in_single) && buffer[i] == ' ')
X buffer[i] = '_';
X }
X
X lasttype = type;
X type = NONE;
X str = (char *) buffer;
X
X /** Three pieces to this loop - get the `field', the 'relop' (if
X there) then, if needed, get the argument to check against (not
X needed for errors or the AND, of course)
X **/
X
X while ((word = strtok(str, " ()[]:\t\n")) != NULL) {
X
X str = (char *) NULL; /* we can start stomping! */
X
X lowercase(word);
X
X if (strcmp(word, "if") == 0) { /* only ONE 'if' allowed */
X if ((word = strtok(str, " ()[]:\t\n")) == NULL) /* NEXT! */
X continue;
X lowercase(word);
X }
X
X if (state == NEXT_CONDITION) {
X lasttype = type;
X type = NONE;
X
X if (the_same(word, "not") || the_same(word, "!")) {
X not_condition = TRUE;
X if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X continue;
X }
X
X if (the_same(word, "from")) type = FROM;
X else if (the_same(word, "to")) type = TO;
X else if (the_same(word, "subject")) type = SUBJECT;
X else if (the_same(word, "lines")) type = LINES;
X else if (the_same(word, "contains")) type = CONTAINS;
X else if (the_same(word, "and") ||
X the_same(word, "&&")) type = AND;
X
X else if (the_same(word,"?") || the_same(word, "then")) {
X
X /** shove THIS puppy into the structure and let's continue! **/
X
X if (lasttype == AND) {
X fprintf(stderr,
X "%sfilter (%s): Error reading line %d of rules - badly placed \"and\"\n",
X BEEP, username, line);
X return(-1);
X }
X
X cond->matchwhat = lasttype;
X if (relop == NONE) relop = EQ; /* otherwise can't do -relop */
X cond->relation = (not_condition? - (relop) : relop);
X
X for (i=0;i<strlen(cond_argument);i++)
X if (cond_argument[i] == '_') cond_argument[i] = ' ';
X
X strcpy(cond->argument1, cond_argument);
X if ((newcond = (struct condition_rec *)
X malloc(sizeof(struct condition_rec))) == NULL) {
X fprintf(stderr,"Couldn't malloc new cond rec!!\n");
X return(-1);
X }
X cond->next = NULL;
X
X relop = EQ; /* default relational condition */
X
X state = READING_ACTION;
X if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X continue;
X goto get_outta_loop;
X }
X
X if (type == NONE) {
X fprintf(stderr,
X "%sfilter (%s): Error reading line %d of rules - field \"%s\" unknown!\n",
X BEEP, username, line, word);
X return(-1);
X }
X
X if (type == AND) {
X
X /** shove THIS puppy into the structure and let's continue! **/
X
X cond->matchwhat = lasttype;
X cond->relation = (not_condition? - (relop) : relop);
X strcpy(cond->argument1, cond_argument);
X if ((newcond = (struct condition_rec *)
X malloc(sizeof(struct condition_rec))) == NULL) {
X fprintf(stderr,"Couldn't malloc new cond rec!!\n");
X return(-1);
X }
X cond->next = newcond;
X cond = newcond;
X cond->next = NULL;
X
X not_condition = FALSE;
X state = NEXT_CONDITION;
X }
X else {
X state = GETTING_OP;
X }
X }
X
Xget_outta_loop: /* jump out when we change state, if needed */
X
X if (state == GETTING_OP) {
X
X if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X continue;
X
X lowercase(word);
X
X relop = NONE;
X
X if (the_same(word, "=") || the_same(word, "in") ||
X the_same(word, "contains")) {
X state = READING_ARGUMENT;
X relop = EQ;
X }
X else {
X if (the_same(word, "<=")) relop = LE;
X else if (the_same(word, ">=")) relop = GE;
X else if (the_same(word, ">")) relop = GT;
X else if (the_same(word, "<>")||
X the_same(word, "!=")) relop = NE;
X else if (the_same(word, "<")) relop = LT;
X
X /* maybe there isn't a relop at all!! */
X
X state=READING_ARGUMENT;
X
X }
X }
X
X if (state == READING_ARGUMENT) {
X if (relop != NONE) {
X if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X continue;
X }
X for (i=0;i<strlen(word);i++)
X if (word[i] == '_') word[i] = ' ';
X
X strcpy(cond_argument, word);
X state = NEXT_CONDITION;
X }
X
X if (state == READING_ACTION) {
X action = NONE;
X
X not_condition = FALSE;
X
X if (the_same(word, "delete")) action = DELETE;
X else if (the_same(word, "savec")) action = SAVECC;
X else if (the_same(word, "save")) action = SAVE;
X else if (the_same(word, "forward")) action = FORWARD;
X else if (the_same(word, "exec")) action = EXEC;
X else if (the_same(word, "leave")) action = LEAVE;
X else {
X fprintf(stderr,
X "%sfilter (%s): Error on line %d of rules - action \"%s\" unknown\n",
X BEEP, username, line, word);
X }
X
X if (action == DELETE || action == LEAVE) {
X /** add this to the rules section and alloc next... **/
X
X rules[total_rules].action = action;
X rules[total_rules].argument2[0] = '\0'; /* nothing! */
X total_rules++;
X
X if ((cond = (struct condition_rec *)
X malloc(sizeof(struct condition_rec))) == NULL) {
X fprintf(stderr,"couldn't malloc first condition rec!\n");
X return(-1);
X }
X
X rules[total_rules].condition = cond; /* hooked in! */
X state = NEXT_CONDITION;
X }
X else {
X state = ACTION_ARGUMENT;
X }
X
X if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X continue;
X
X }
X
X if (state == ACTION_ARGUMENT) {
X strcpy(action_argument, word);
X
X /** add this to the rules section and alloc next... **/
X
X rules[total_rules].action = action;
X expand_macros(action_argument, rules[total_rules].argument2,line);
X total_rules++;
X
X if ((cond = (struct condition_rec *)
X malloc(sizeof(struct condition_rec))) == NULL) {
X fprintf(stderr,"couldn't malloc first condition rec!\n");
X return(-1);
X }
X
X rules[total_rules].condition = cond; /* hooked in! */
X
X state = NEXT_CONDITION;
X if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X continue;
X }
X }
X }
X
X return(0);
X}
END_OF_filter/parse.c
if test 8929 -ne `wc -c <filter/parse.c`; then
echo shar: \"filter/parse.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/input_utils.c\" \(8598 characters\)
if test -f src/input_utils.c ; then
echo shar: Will not over-write existing file \"src/input_utils.c\"
else
sed "s/^X//" >src/input_utils.c <<'END_OF_src/input_utils.c'
X/** input_utils.c **/
X
X/** Mindless I/O routines for ELM
X
X (C) Copyright 1985 Dave Taylor
X**/
X
X#include "headers.h"
X#include <errno.h>
X
Xextern int errno; /* system error number */
X
Xunsigned alarm();
X
X#define special_char(c) (c == ' ' || c == '\t' || c == '/' || c == ',' \
X || c == '\0')
X
X#define erase_a_char() { Writechar(BACKSPACE); Writechar(' '); \
X Writechar(BACKSPACE); fflush(stdout); }
X
Xint
Xwant_to(question, dflt, echo_answer)
Xchar *question, dflt;
Xint echo_answer;
X{
X /** Ask 'question' at LINES-2, COLUMNS-40, returning the answer in
X lower case. If 'echo_answer', then echo answer. 'dflt' is the
X default answer if <return> is pressed. (Note: 'dflt' is also what
X will be returned if <return> is pressed!)
X **/
X register char ch, cols;
X
X cols = (strlen(question) < 30)? COLUMNS-40 : COLUMNS-50;
X
X PutLine3(LINES-3, cols,"%s%c%c", question, dflt, BACKSPACE);
X fflush(stdout);
X fflush(stdin);
X ch = tolower(ReadCh());
X
X if (echo_answer && ch > (char) ' ') {
X Writechar(ch);
X fflush(stdout);
X }
X
X return(ch == '\n' || ch == '\r' ? dflt : ch);
X}
X
Xint
Xread_number(ch)
Xchar ch;
X{
X /** Read a number, where 'ch' is the leading digit! **/
X
X char buff[SHORT_SLEN];
X int num;
X
X buff[0] = ch;
X buff[1] = '\0';
X
X PutLine0(LINES-3, COLUMNS-40,"Set current message to :");
X if (optionally_enter(buff, LINES-3, COLUMNS-15, TRUE) == -1)
X return(current);
X
X sscanf(buff,"%d", &num);
X return(num);
X}
X
Xint
Xoptionally_enter(string, x, y, append_current)
Xchar *string;
Xint x,y, append_current;
X{
X /** Display the string on the screen and if RETURN is pressed, return
X it. Otherwise, allow standard text input, including backspaces
X and such until RETURN is hit.
X If "append_current" is set, then leave the default string in
X place and edit AFTER it...assume 'x,y' is placing us at the
X beginning of the string...
X This routine returns zero unless INTERRUPT hit, then it returns
X -1 and must be treated accordingly.
X Added ^W and ^R support...
X Also added that if x and y are < 0 don't try any cursor stuff
X **/
X
X char ch;
X register int index = 0, use_cursor_control;
X
X use_cursor_control = ((! mail_only) && x >= 0 && y >= 0);
X
X if (use_cursor_control)
X PutLine1(x,y, "%s", string);
X else
X printf("%s", string);
X
X CleartoEOLN();
X
X if (! append_current)
X if (use_cursor_control)
X MoveCursor(x,y);
X else
X non_destructive_back_up(strlen(string));
X
X if (cursor_control)
X transmit_functions(OFF);
X
X ch = getchar();
X
X if (ch == '\n' || ch == '\r') {
X if (cursor_control)
X transmit_functions(ON);
X return(0); /* we're done. No change needed */
X }
X
X CleartoEOLN();
X
X index = (append_current? strlen(string) : 0);
X
X if (ch == kill_line) {
X if (use_cursor_control)
X MoveCursor(x,y);
X else
X back_up(index);
X CleartoEOLN();
X index = 0;
X }
X else if (ch != backspace) {
X Writechar(ch);
X string[index++] = ch;
X }
X else if (index > 0) {
X index--;
X erase_a_char();
X }
X else {
X Writechar(' ');
X Writechar(BACKSPACE);
X fflush(stdout);
X }
X
X do {
X ch = getchar();
X
X /* the following is converted from a case statement to
X allow the variable characters (backspace, kill_line
X and break) to be processed. Case statements in
X C require constants as labels, so it failed ...
X */
X
X if (ch == backspace) {
X if (index > 0) {
X erase_a_char();
X index--;
X }
X else {
X Writechar(' ');
X Writechar(BACKSPACE);
X fflush(stdout);
X }
X }
X else if (ch == '\n' || ch == '\r') {
X string[index] = '\0';
X if (cursor_control)
X transmit_functions(ON);
X return(0);
X }
X else if (ch == ctrl('W')) { /* back up a word! */
X if (special_char(string[index]) && index > 0) {
X index--;
X erase_a_char();
X }
X while (index > 0 && ! special_char(string[index])) {
X index--;
X erase_a_char();
X }
X }
X else if (ch == ctrl('R')) {
X string[index] = '\0';
X if (use_cursor_control) {
X PutLine1(x,y, "%s", string);
X CleartoEOLN();
X }
X else
X printf("\n%s", string);
X }
X else if (ch == kill_line) {
X if (use_cursor_control)
X MoveCursor(x,y);
X else
X back_up(index+1);
X CleartoEOLN();
X index = 0;
X }
X else if (ch == NULL) {
X if (cursor_control)
X transmit_functions(ON);
X fflush(stdin); /* remove extraneous chars, if any */
X string[0] = '\0'; /* clean up string, and... */
X return(-1);
X }
X else { /* default case */
X
X string[index++] = ch;
X Writechar(ch);
X }
X } while (index < SLEN);
X
X string[index] = '\0';
X
X if (cursor_control)
X transmit_functions(ON);
X return(0);
X}
X
Xint
Xpattern_enter(string, alt_string, x, y, alternate_prompt)
Xchar *string, *alt_string, *alternate_prompt;
Xint x,y;
X{
X /** This function is functionally similar to the routine
X optionally-enter, but if the first character pressed
X is a '/' character, then the alternate prompt and string
X are used rather than the normal one. This routine
X returns 1 if alternate was used, 0 if not
X **/
X
X char ch;
X register index = 0;
X
X PutLine1(x, y, "%s", string);
X CleartoEOLN();
X MoveCursor(x,y);
X
X if (cursor_control)
X transmit_functions(OFF);
X
X ch = getchar();
X
X if (ch == '\n' || ch == '\r') {
X if (cursor_control)
X transmit_functions(ON);
X return(0); /* we're done. No change needed */
X }
X
X if (ch == '/') {
X PutLine1(x, 0, "%s", alternate_prompt);
X CleartoEOLN();
X (void) optionally_enter(alt_string, x, strlen(alternate_prompt)+1,
X FALSE);
X return(1);
X }
X
X CleartoEOLN();
X
X index = 0;
X
X if (ch == kill_line) {
X MoveCursor(x,y);
X CleartoEOLN();
X index = 0;
X }
X else if (ch != backspace) {
X Writechar(ch);
X string[index++] = ch;
X }
X else if (index > 0) {
X index--;
X erase_a_char();
X }
X else {
X Writechar(' ');
X Writechar(BACKSPACE);
X }
X
X do {
X fflush(stdout);
X ch = getchar();
X
X /* the following is converted from a case statement to
X allow the variable characters (backspace, kill_line
X and break) to be processed. Case statements in
X C require constants as labels, so it failed ...
X */
X
X if (ch == backspace) {
X if (index > 0) {
X index--;
X erase_a_char();
X }
X else {
X Writechar(' ');
X Writechar(BACKSPACE);
X }
X }
X else if (ch == '\n' || ch == '\r') {
X string[index] = '\0';
X if (cursor_control)
X transmit_functions(ON);
X return(0);
X }
X else if (ch == ctrl('W')) {
X /* get to rightmost non-alpha */
X if (special_char (string[index]) && index > 0)
X index--;
X while (index > 0 && ! special_char(string[index])) {
X erase_a_char();
X index--;
X }
X }
X else if (ch == ctrl('R')) {
X string[index] = '\0';
X if (!mail_only) {
X PutLine1(x,y, "%s", string);
X CleartoEOLN();
X }
X else
X printf("\n%s", string);
X }
X else if (ch == kill_line) {
X MoveCursor(x,y);
X CleartoEOLN();
X index = 0;
X }
X else if (ch == NULL) {
X if (cursor_control)
X transmit_functions(ON);
X fflush(stdin); /* remove extraneous chars, if any */
X string[0] = '\0'; /* clean up string, and... */
X return(-1);
X }
X else { /* default case */
X
X string[index++] = ch;
X Writechar(ch);
X }
X } while (index < SLEN);
X
X string[index] = '\0';
X
X if (cursor_control)
X transmit_functions(ON);
X return(0);
X}
X
Xback_up(spaces)
Xint spaces;
X{
X /** this routine is to replace the goto x,y call for when sending
X mail without starting the entire "elm" system up... **/
X
X while (spaces--) {
X erase_a_char();
X }
X}
X
Xnon_destructive_back_up(spaces)
Xint spaces;
X{
X /** same as back_up() but doesn't ERASE the characters on the screen **/
X
X while (spaces--)
X Writechar(BACKSPACE);
X fflush(stdout);
X}
X
Xint
XGetPrompt()
X{
X /** This routine does a read/timeout for a single character.
X The way that this is determined is that the routine to
X read a character is called, then the "errno" is checked
X against EINTR (interrupted call). If they match, this
X returns NO_OP_COMMAND otherwise it returns the normal
X command.
X **/
X
X int ch;
X
X if (timeout > 0) {
X alarm((unsigned) timeout);
X errno = 0; /* we actually have to do this. *sigh* */
X ch = ReadCh();
X if (errno == EINTR) ch = NO_OP_COMMAND;
X alarm((unsigned) 0);
X }
X else
X ch = ReadCh();
X
X return(ch);
X}
END_OF_src/input_utils.c
if test 8598 -ne `wc -c <src/input_utils.c`; then
echo shar: \"src/input_utils.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/mailmsg1.c\" \(8412 characters\)
if test -f src/mailmsg1.c ; then
echo shar: Will not over-write existing file \"src/mailmsg1.c\"
else
sed "s/^X//" >src/mailmsg1.c <<'END_OF_src/mailmsg1.c'
X/** mailmsg1.c **/
X
X/** Interface to allow mail to be sent to users. Part of ELM **/
X
X/** (C) Copyright 1986, Dave Taylor **/
X
X#include "headers.h"
X
X/** strings defined for the hdrconfg routines **/
X
Xchar subject[SLEN], action[SLEN], reply_to[SLEN], expires[SLEN], priority[SLEN];
Xchar to[VERY_LONG_STRING], cc[VERY_LONG_STRING], in_reply_to[SLEN];
Xchar user_defined_header[SLEN];
X
Xchar expanded_to[VERY_LONG_STRING], expanded_cc[VERY_LONG_STRING];
X
X#ifdef ALLOW_BCC
Xchar bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING];
X#endif
X
Xchar *format_long(), *strip_commas(), *tail_of_string(), *strcpy();
Xunsigned long sleep();
X
Xint
Xsend(given_to, given_subject, edit_message, form_letter)
Xchar *given_to, *given_subject;
Xint edit_message, form_letter;
X{
X /** Prompt for fields and then call mail() to send the specified
X message. If 'edit_message' is true then don't allow the
X message to be edited. 'form_letter' can be "YES" "NO" or "MAYBE".
X if YES, then add the header. If MAYBE, then add the M)ake form
X option to the last question (see mailsg2.c) etc. etc. **/
X
X int copy_msg = FALSE, is_a_response = FALSE;
X
X /* First: zero all current global message strings */
X
X cc[0] = action[0] = reply_to[0] = expires[0] = priority[0] = '\0';
X#ifdef ALLOW_BCC
X bcc[0] = expanded_bcc[0] = '\0';
X#endif
X in_reply_to[0] = expanded_to[0] = expanded_cc[0] = '\0';
X
X strcpy(subject, given_subject); /* copy given subject */
X strcpy(to, given_to); /* copy given to: */
X
X /******* And now the real stuff! *******/
X
X copy_msg=copy_the_msg(&is_a_response); /* copy msg into edit buffer? */
X
X if (get_to(to, expanded_to) == 0) /* get the To: address and expand */
X return(0);
X
X /** are we by any chance just checking the addresses? **/
X
X if (check_only) {
X printf("Expands to: %s\n", format_long(expanded_to, 12));
X putchar('\r'); /* don't ask... */
X leave();
X }
X
X /** if we're batchmailing, let's send it and GET OUTTA HERE! **/
X
X if (mail_only && strlen(batch_subject) > 0) {
X strcpy(subject, batch_subject); /* get the batch subject */
X return(mail(FALSE, FALSE, TRUE, form_letter));
X }
X
X display_to(expanded_to); /* display the To: field on screen... */
X
X dprint1(3,"\nMailing to %s\n", expanded_to);
X
X if (get_subject(subject) == 0) /* get the Subject: field */
X return(0);
X
X dprint1(4,"Subject is %s\n", subject);
X
X if (get_copies(cc, expanded_to, expanded_cc) == 0)
X return(0);
X
X if (strlen(cc) > 0)
X dprint1(4,"Copies to %s\n", expanded_cc);
X
X if (mail_only) /* indicate next step... */
X printf("\n\r");
X else
X MoveCursor(LINES,0); /* you know you're hit <return> ! */
X
X /** generate the In-Reply-To: header... **/
X
X if (is_a_response)
X generate_reply_to(current-1);
X
X /* and mail that puppy outta here! */
X
X mail(copy_msg, edit_message, FALSE, form_letter);
X
X return(edit_message);
X}
X
Xget_to(to_field, address)
Xchar *to_field, *address;
X{
X /** prompt for the "To:" field, expanding into address if possible.
X This routine returns ZERO if errored, or non-zero if okay **/
X
X if (strlen(to_field) == 0) {
X if (user_level < 2) {
X PutLine0(LINES-2, 0, "Send the message to: ");
X (void) optionally_enter(to_field, LINES-2, 21, FALSE);
X }
X else {
X PutLine0(LINES-2, 0, "To: ");
X (void) optionally_enter(to_field, LINES-2, 4, FALSE);
X }
X if (strlen(to_field) == 0) {
X ClearLine(LINES-2);
X return(0);
X }
X build_address(strip_commas(to_field), address);
X }
X else if (mail_only)
X build_address(strip_commas(to_field), address);
X else
X strcpy(address, to_field);
X
X if (strlen(address) == 0) { /* bad address! Removed!! */
X if (! mail_only)
X ClearLine(LINES-2);
X return(0);
X }
X
X return(1); /* everything is okay... */
X}
X
Xget_subject(subject_field)
Xchar *subject_field;
X{
X /** get the subject and return non-zero if all okay... **/
X int len = 9;
X
X if (mail_only)
X printf("Subject: ");
X else
X if (user_level == 0) {
X PutLine0(LINES-2,0,"Subject of message: ");
X len = 21;
X }
X else
X PutLine0(LINES-2,0,"Subject: ");
X
X CleartoEOLN();
X
X if (optionally_enter(subject_field, LINES-2, len, TRUE) == -1) {
X /** User hit the BREAK key! **/
X MoveCursor(LINES-2,0);
X CleartoEOLN();
X error("mail not sent");
X return(0);
X }
X
X if (strlen(subject_field) == 0) { /* zero length subject?? */
X if (mail_only)
X printf("\n\rNo subject - Continue with message? (y/n) n%c",
X BACKSPACE);
X else
X PutLine1(LINES-2,0,"No subject - Continue with message? (y/n) n%c",
X BACKSPACE);
X
X if (tolower(ReadCh()) != 'y') { /* user says no! */
X if (mail_only) {
X printf("\n\r\n\rMail Cancelled!\n\r");
X return(0);
X }
X ClearLine(LINES-2);
X error("mail not sent");
X return(0);
X }
X else if (! mail_only) {
X PutLine0(LINES-2,0,"Subject: <none>");
X CleartoEOLN();
X }
X }
X
X return(1); /** everything is cruising along okay **/
X}
X
Xget_copies(cc_field, address, addressII)
Xchar *cc_field, *address, *addressII;
X{
X /** Get the list of people that should be cc'd, returning ZERO if
X any problems arise. Address and AddressII are for expanding
X the aliases out after entry!
X If 'bounceback' is nonzero, add a cc to ourselves via the remote
X site, but only if hops to machine are > bounceback threshold.
X **/
X
X if (mail_only)
X printf("\n\rCopies To: ");
X else
X PutLine0(LINES-1,0,"Copies To: ");
X
X fflush(stdout);
X
X if (optionally_enter(cc_field, LINES-1, 11, FALSE) == -1) {
X if (mail_only) {
X printf("\n\r\n\rMail not sent!\n\r");
X return(0);
X }
X ClearLine(LINES-2);
X ClearLine(LINES-1);
X
X error("mail not sent");
X return(0);
X }
X
X build_address(strip_commas(cc_field), addressII);
X
X if (strlen(address) + strlen(addressII) > VERY_LONG_STRING) {
X dprint0(2,
X "String length of \"To:\" + \"Cc\" too long! (get_copies)\n");
X error("Too many people. Copies ignored");
X sleep(2);
X cc_field[0] = '\0';
X }
X
X return(1); /* everything looks okay! */
X}
X
Xint
Xcopy_the_msg(is_a_response)
Xint *is_a_response;
X{
X /** Returns True iff the user wants to copy the message being
X replied to into the edit buffer before invoking the editor!
X Sets "is_a_response" to true if message is a response...
X **/
X
X int answer = FALSE;
X
X if (strlen(to) > 0 && !mail_only) { /* predefined 'to' line! */
X if (auto_copy)
X answer = TRUE;
X else
X answer = (want_to("Copy message? (y/n) ", 'n', TRUE) == 'y');
X *is_a_response = TRUE;
X }
X else
X if (strlen(subject) > 0) /* predefined 'subject' (Forward) */
X answer = TRUE;
X
X return(answer);
X}
X
Xdisplay_to(address)
Xchar *address;
X{
X /** Simple routine to display the "To:" line according to the
X current configuration (etc)
X **/
X register int open_paren;
X
X if (mail_only)
X printf("To: %s\n\r", format_long(address, 3));
X else {
X if (names_only)
X if ((open_paren = chloc(address, '(')) > 0) {
X if (open_paren < chloc(address, ')')) {
X output_abbreviated_to(address);
X return;
X }
X }
X if (strlen(address) > 45)
X PutLine1(LINES-3, COLUMNS-50, "To: (%s)",
X tail_of_string(address, 40));
X else {
X if (strlen(address) > 30)
X PutLine1(LINES-3, COLUMNS-50, "To: %s", address);
X else
X PutLine1(LINES-3, COLUMNS-50, " To: %s", address);
X CleartoEOLN();
X }
X }
X}
X
Xoutput_abbreviated_to(address)
Xchar *address;
X{
X /** Output just the fields in parens, separated by commas if need
X be, and up to COLUMNS-50 characters...This is only used if the
X user is at level BEGINNER.
X **/
X
X char newaddress[LONG_STRING];
X register int index, newindex = 0, in_paren = 0;
X
X index = 0;
X
X while (newindex < 55 && index < strlen(address)) {
X if (address[index] == '(') in_paren++;
X else if (address[index] == ')') {
X in_paren--;
X if (index < strlen(address)-4) {
X newaddress[newindex++] = ',';
X newaddress[newindex++] = ' ';
X }
X }
X
X if (in_paren && address[index] != '(')
X newaddress[newindex++] = address[index];
X
X index++;
X }
X
X newaddress[newindex] = '\0';
X
X if (strlen(newaddress) > 50)
X PutLine1(LINES-3, COLUMNS-50, "To: (%s)",
X tail_of_string(newaddress, 40));
X else {
X if (strlen(newaddress) > 30)
X PutLine1(LINES-3, COLUMNS-50, "To: %s", newaddress);
X else
X PutLine1(LINES-3, COLUMNS-50, " To: %s", newaddress);
X CleartoEOLN();
X }
X
X return;
X}
END_OF_src/mailmsg1.c
if test 8412 -ne `wc -c <src/mailmsg1.c`; then
echo shar: \"src/mailmsg1.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/options.c\" \(9046 characters\)
if test -f src/options.c ; then
echo shar: Will not over-write existing file \"src/options.c\"
else
sed "s/^X//" >src/options.c <<'END_OF_src/options.c'
X/** options.c **/
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 mailboxes>
X savefile <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 (C) Copyright 1986, Dave Taylor
X**/
X
X#include "headers.h"
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
X char ch;
X
X display_options();
X
X do {
X ClearLine(LINES-4);
X
X Centerline(LINES-4,
X "Select first letter of Option line, '>' to Save, or R)eturn");
X
X PutLine0(LINES-2, 0, "Command: ");
X
X ch = tolower(ReadCh());
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(calendar_file, 2, 23, FALSE); break;
X case 'd' : optionally_enter(pager, 3, 23, FALSE); break;
X case 'e' : optionally_enter(editor, 4, 23, FALSE); break;
X case 'f' : optionally_enter(folders, 5, 23, FALSE); break;
X case 's' : change_sort(6,23); break;
X case 'o' : optionally_enter(savefile, 7, 23, FALSE); break;
X case 'p' : optionally_enter(printout, 8, 23, FALSE); break;
X case 'y' : optionally_enter(full_username, 9, 23, FALSE); 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 .elmrc...");
X fflush(stdout); save_options(); break;
X
X case 'x' :
X case 'r' :
X case ctrl('M'):
X case ctrl('J'): return;
X case ctrl('L'): display_options(); break;
X default: error("Command unknown!");
X }
X
X } while (ch != 'r');
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", calendar_file);
X#endif
X PutLine1(3, 0, "D)isplay mail using : %s", pager);
X PutLine1(4, 0, "E)ditor : %s", editor);
X PutLine1(5, 0, "F)older directory : %s", folders);
X PutLine1(6, 0, "S)orting criteria : %s", sort_name(FULL));
X PutLine1(7, 0, "O)utbound mail saved : %s", savefile);
X PutLine1(8, 0, "P)rint mail using : %s", 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
X int last_sortby, /* so we know if it changes... */
X sign = 1; /* are we reverse sorting?? */
X char 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 = tolower(ReadCh());
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+1));
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 mailbox...");
X sleep(1);
X sort_mailbox(message_count, 0);
X }
X ClearLine(LINES-2); /* clear sort_one_liner()! */
X}
X
Xone_liner(string)
Xchar *string;
X{
X /** A single-line description of the selected item... **/
X
X ClearLine(LINES-4);
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 -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 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 mailboxes");
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(""); /* 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 "Enter the key you want help on, '?' for a list, or '.' to exit help");
X
X lower_prompt("Key : ");
X
X while ((c = tolower(ReadCh())) != '.') {
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}
END_OF_src/options.c
if test 9046 -ne `wc -c <src/options.c`; then
echo shar: \"src/options.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"utils/answer.c\" \(8992 characters\)
if test -f utils/answer.c ; then
echo shar: Will not over-write existing file \"utils/answer.c\"
else
sed "s/^X//" >utils/answer.c <<'END_OF_utils/answer.c'
X/** answer.c **/
X
X/** This program is a phone message transcription system, and
X is designed for secretaries and the like, to allow them to
X painlessly generate electronic mail instead of paper forms.
X
X Note: this program ONLY uses the local alias file, and does not
X even read in the system alias file at all.
X
X (C) Copyright 1986, Dave Taylor
X
X**/
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <ctype.h>
X
X#include "defs.h" /* ELM system definitions */
X
X#define ELM "elm" /* where the elm program lives */
X
X#define answer_temp_file "/tmp/answer."
X
Xstatic char ident[] = { WHAT_STRING };
X
Xstruct alias_rec user_hash_table [MAX_UALIASES];
X
Xint user_data; /* fileno of user data file */
X
Xchar *expand_group(), *get_alias_address(), *get_token(), *strip_parens();
X
Xmain()
X{
X FILE *fd;
X char *address, buffer[LONG_STRING], tempfile[SLEN];
X char name[SLEN], user_name[SLEN];
X int msgnum = 0, eof;
X
X read_alias_files();
X
X while (1) {
X if (msgnum > 9999) msgnum = 0;
X
X printf("\n-------------------------------------------------------------------------------\n");
X
Xprompt: printf("\nMessage to: ");
X gets(user_name, SLEN);
X if (user_name == NULL)
X goto prompt;
X
X if ((strcmp(user_name,"quit") == 0) ||
X (strcmp(user_name,"exit") == 0) ||
X (strcmp(user_name,"done") == 0) ||
X (strcmp(user_name,"bye") == 0))
X exit(0);
X
X if (translate(user_name, name) == 0)
X goto prompt;
X
X address = get_alias_address(name, 1, 0);
X
X printf("address '%s'\n", address);
X
X if (address == NULL || strlen(address) == 0) {
X printf("Sorry, could not find '%s' [%s] in list!\n", user_name,
X name);
X goto prompt;
X }
X
X sprintf(tempfile, "%s%d", answer_temp_file, msgnum++);
X
X if ((fd = fopen(tempfile,"w")) == NULL)
X exit(printf("** Fatal Error: could not open %s to write\n",
X tempfile));
X
X
X printf("\nEnter message for %s ending with a blank line.\n\n",
X user_name);
X
X fprintf(fd,"\n\n");
X
X do {
X printf("> ");
X if (! (eof = (gets(buffer, SLEN) == NULL)))
X fprintf(fd, "%s\n", buffer);
X } while (! eof && strlen(buffer) > 0);
X
X fclose(fd);
X
X sprintf(buffer,
X "((%s -s \"While You Were Out\" %s ; %s %s) & ) < %s > /dev/null",
X ELM, strip_parens(address), remove, tempfile, tempfile);
X
X system(buffer);
X }
X}
X
Xint
Xtranslate(fullname, name)
Xchar *fullname, *name;
X{
X /** translate fullname into name..
X 'first last' translated to first_initial - underline - last
X 'initial last' translated to initial - underline - last
X Return 0 if error.
X **/
X register int i, lastname = 0;
X
X for (i=0; i < strlen(fullname); i++) {
X
X if (isupper(fullname[i]))
X fullname[i] = tolower(fullname[i]);
X
X if (fullname[i] == ' ')
X if (lastname) {
X printf(
X "** Can't have more than 'FirstName LastName' as address!\n");
X return(0);
X }
X else
X lastname = i+1;
X
X }
X
X if (lastname)
X sprintf(name, "%c_%s", fullname[0], (char *) fullname + lastname);
X else
X strcpy(name, fullname);
X
X return(1);
X}
X
X
Xread_alias_files()
X{
X /** read the user alias file **/
X
X char fname[SLEN];
X int hash;
X
X sprintf(fname, "%s/.alias_hash", getenv("HOME"));
X
X if ((hash = open(fname, O_RDONLY)) == -1)
X exit(printf("** Fatal Error: Could not open %s!\n", fname));
X
X read(hash, user_hash_table, sizeof user_hash_table);
X close(hash);
X
X sprintf(fname, "%s/.alias_data", getenv("HOME"));
X
X if ((user_data = open(fname, O_RDONLY)) == -1)
X return;
X}
X
Xchar *get_alias_address(name, mailing, depth)
Xchar *name;
Xint mailing, depth;
X{
X /** return the line from either datafile that corresponds
X to the specified name. If 'mailing' specified, then
X fully expand group names. Returns NULL if not found.
X Depth is the nesting depth, and varies according to the
X nesting level of the routine. **/
X
X static char buffer[VERY_LONG_STRING];
X int loc;
X
X if ((loc = find(name, user_hash_table, MAX_UALIASES)) >= 0) {
X lseek(user_data, user_hash_table[loc].byte, 0L);
X get_line(user_data, buffer, LONG_STRING);
X if (buffer[0] == '!' && mailing)
X return( (char *) expand_group(buffer, depth));
X else
X return( (char *) buffer);
X }
X
X return( (char *) NULL);
X}
X
Xchar *expand_group(members, depth)
Xchar *members;
Xint depth;
X{
X /** given a group of names separated by commas, this routine
X will return a string that is the full addresses of each
X member separated by spaces. Depth is the current recursion
X depth of the expansion (for the 'get_token' routine) **/
X
X char buffer[VERY_LONG_STRING];
X char buf[LONG_STRING], *word, *address, *bufptr;
X
X strcpy(buf, members); /* parameter safety! */
X buffer[0] = '\0'; /* nothing in yet! */
X bufptr = (char *) buf; /* grab the address */
X depth++; /* one more deeply into stack */
X
X while ((word = (char *) get_token(bufptr, "!, ", depth)) != NULL) {
X if ((address = (char *) get_alias_address(word, 1, depth)) == NULL) {
X fprintf(stderr, "Alias %s not found for group expansion!", word);
X return( (char *) NULL);
X }
X else if (strcmp(buffer,address) != 0) {
X sprintf(buffer,"%s %s", buffer, address);
X }
X
X bufptr = NULL;
X }
X
X return( (char *) buffer);
X}
X
Xint
Xfind(word, table, size)
Xchar *word;
Xstruct alias_rec table[];
Xint size;
X{
X /** find word and return loc, or -1 **/
X register int loc;
X
X if (strlen(word) > 20)
X exit(printf("Bad alias name: %s. Too long.\n", word));
X
X loc = hash_it(word, size);
X
X while (strcmp(word, table[loc].name) != 0) {
X if (table[loc].name[0] == '\0')
X return(-1);
X loc = (loc + 1) % size;
X }
X
X return(loc);
X}
X
Xint
Xhash_it(string, table_size)
Xchar *string;
Xint table_size;
X{
X /** compute the hash function of the string, returning
X it (mod table_size) **/
X
X register int i, sum = 0;
X
X for (i=0; string[i] != '\0'; i++)
X sum += (int) string[i];
X
X return(sum % table_size);
X}
X
Xget_line(fd, buffer)
Xint fd;
Xchar *buffer;
X{
X /* read from file fd. End read upon reading either
X EOF or '\n' character (this is where it differs
X from a straight 'read' command!) */
X
X register int i= 0;
X char ch;
X
X while (read(fd, &ch, 1) > 0)
X if (ch == '\n' || ch == '\r') {
X buffer[i] = 0;
X return;
X }
X else
X buffer[i++] = ch;
X}
X
Xprint_long(buffer, init_len)
Xchar *buffer;
Xint init_len;
X{
X /** print buffer out, 80 characters (or less) per line, for
X as many lines as needed. If 'init_len' is specified,
X it is the length that the first line can be.
X **/
X
X register int i, loc=0, space, length;
X
X /* In general, go to 80 characters beyond current character
X being processed, and then work backwards until space found! */
X
X length = init_len;
X
X do {
X if (strlen(buffer) > loc + length) {
X space = loc + length;
X while (buffer[space] != ' ' && space > loc + 50) space--;
X for (i=loc;i <= space;i++)
X putchar(buffer[i]);
X putchar('\n');
X loc = space;
X }
X else {
X for (i=loc;i < strlen(buffer);i++)
X putchar(buffer[i]);
X putchar('\n');
X loc = strlen(buffer);
X }
X length = 80;
X } while (loc < strlen(buffer));
X}
X
X/****
X The following is a newly chopped version of the 'strtok' routine
X that can work in a recursive way (up to 20 levels of recursion) by
X changing the character buffer to an array of character buffers....
X****/
X
X#define MAX_RECURSION 20 /* up to 20 deep recursion */
X
X#undef NULL
X#define NULL (char *) 0 /* for this routine only */
X
Xextern int strspn();
Xextern char *strpbrk();
X
Xchar *get_token(string, sepset, depth)
Xchar *string, *sepset;
Xint depth;
X{
X
X /** string is the string pointer to break up, sepstr are the
X list of characters that can break the line up and depth
X is the current nesting/recursion depth of the call **/
X
X register char *p, *q, *r;
X static char *savept[MAX_RECURSION];
X
X /** is there space on the recursion stack? **/
X
X if (depth >= MAX_RECURSION) {
X fprintf(stderr,"Error: Get_token calls nested greated than %d deep!\n",
X MAX_RECURSION);
X exit(1);
X }
X
X /* set up the pointer for the first or subsequent call */
X p = (string == NULL)? savept[depth]: string;
X
X if(p == 0) /* return if no tokens remaining */
X return(NULL);
X
X q = p + strspn(p, sepset); /* skip leading separators */
X
X if (*q == '\0') /* return if no tokens remaining */
X return(NULL);
X
X if ((r = strpbrk(q, sepset)) == NULL) /* move past token */
X savept[depth] = 0; /* indicate this is last token */
X else {
X *r = '\0';
X savept[depth] = ++r;
X }
X return(q);
X}
X
Xchar *strip_parens(string)
Xchar *string;
X{
X /** Return string with all parenthesized information removed.
X This is a non-destructive algorithm... **/
X
X static char buffer[LONG_STRING];
X register int i, depth = 0, buffer_index = 0;
X
X for (i=0; i < strlen(string); i++) {
X if (string[i] == '(')
X depth++;
X else if (string[i] == ')')
X depth--;
X else if (depth == 0)
X buffer[buffer_index++] = string[i];
X }
X
X buffer[buffer_index] = '\0';
X
X return( (char *) buffer);
X}
END_OF_utils/answer.c
if test 8992 -ne `wc -c <utils/answer.c`; then
echo shar: \"utils/answer.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: End of archive 8 \(of 19\).
cp /dev/null ark8isdone
DONE=true
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
if test ! -f ark${I}isdone ; then
echo shar: You still need to run archive ${I}.
DONE=false
fi
done
if test "$DONE" = "true" ; then
echo You have unpacked all 19 archives.
echo "See the Instructions file"
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
fi
## End of shell archive.
exit 0