home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume9 / elm2 / part08 < prev    next >
Text File  |  1987-03-08  |  49KB  |  1,828 lines

  1. Subject:  v09i008:  ELM Mail System, Part08/19
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.TMC.COM
  4.  
  5. Submitted by: Dave Taylor <hplabs!taylor>
  6. Mod.sources: Volume 9, Issue 8
  7. Archive-name: elm2/Part08
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line,
  11. # then unpack it by saving it in a file and typing "sh file".
  12. # If this archive is complete, you will see the message:
  13. #        "End of archive 8 (of 19)."
  14. # Contents:  filter/parse.c src/input_utils.c src/mailmsg1.c
  15. #   src/options.c utils/answer.c
  16. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  17. echo shar: Extracting \"filter/parse.c\" \(8929 characters\)
  18. if test -f filter/parse.c ; then 
  19.   echo shar: Will not over-write existing file \"filter/parse.c\"
  20. else
  21. sed "s/^X//" >filter/parse.c <<'END_OF_filter/parse.c'
  22. X/**            filter_parse.c            **/
  23. X
  24. X/** This is the parser for the filter program.  It accepts a wide variety of
  25. X    constructs, building the ruleset table as it goes along.  Check the 
  26. X    data structure in filter.h for more information on how the rules are
  27. X    stored.  The parser is a cunning state-table based program.
  28. X
  29. X   (C) Copyright 1986, Dave Taylor
  30. X**/
  31. X
  32. X#include <stdio.h>
  33. X#include <ctype.h>
  34. X
  35. X#include "defs.h"
  36. X#include "filter.h"
  37. X
  38. X#define NONE            0
  39. X#define AND            10
  40. X
  41. X#define NEXT_CONDITION        0
  42. X#define GETTING_OP        1
  43. X#define READING_ARGUMENT    2
  44. X#define READING_ACTION        3
  45. X#define ACTION_ARGUMENT        4
  46. X
  47. Xchar *strtok(), *whatname(), *actionname();
  48. X
  49. Xint
  50. Xget_filter_rules()
  51. X{
  52. X    /** Given the users home directory, open and parse their rules table,
  53. X        building the data structure as we go along.
  54. X        returns -1 if we hit an error of any sort...
  55. X    **/
  56. X
  57. X    FILE *fd;                /* the file descriptor     */
  58. X    char  buffer[SLEN],             /* fd reading buffer       */
  59. X          *str,                 /* ptr to read string      */
  60. X          *word,                /* ptr to 'token'          */
  61. X          filename[SLEN],             /* the name of the ruleset */
  62. X          action_argument[SLEN],         /* action arg, per rule    */
  63. X          cond_argument[SLEN];        /* cond arg, per condition */
  64. X    int   not_condition = FALSE,         /* are we in a "not" ??    */
  65. X          type=NONE,             /* what TYPE of condition? */
  66. X          lasttype,             /* and the previous TYPE?  */
  67. X          state = NEXT_CONDITION,        /* the current state       */
  68. X          in_single, in_double,         /* for handling spaces.    */
  69. X          i,                 /* misc integer for loops  */
  70. X          relop = NONE,            /* relational operator     */
  71. X          action,                 /* the current action type */
  72. X          line = 0;                /* line number we're on    */
  73. X
  74. X    struct condition_rec    *cond, *newcond;
  75. X
  76. X    sprintf(filename,"%s/%s", home, filterfile);
  77. X
  78. X    if ((fd = fopen(filename,"r")) == NULL) {
  79. X      fprintf(stderr,"filter (%s): Couldn't read user filter rules file!\n",
  80. X          username);
  81. X      return(-1);
  82. X    }
  83. X
  84. X    cond_argument[0] = action_argument[0] = '\0';
  85. X
  86. X    /* Now, for each line... **/
  87. X
  88. X    if ((cond = (struct condition_rec *) 
  89. X             malloc(sizeof(struct condition_rec))) == NULL) {
  90. X      fprintf(stderr,"couldn't malloc first condition rec!\n");
  91. X      return(-1);
  92. X    }
  93. X    
  94. X    rules[total_rules].condition = cond;    /* hooked in! */
  95. X
  96. X    while (fgets(buffer, SLEN, fd) != NULL) {
  97. X      line++;
  98. X
  99. X      if (buffer[0] == '#' || strlen(buffer) < 2)
  100. X        continue;        /* nothing to look at! */
  101. X
  102. X      in_single = in_double = 0;
  103. X
  104. X      for (i=0; i < strlen(buffer); i++) {
  105. X        if (buffer[i] == '"') 
  106. X          in_double = ! in_double;
  107. X        else if (buffer[i] == '\'')
  108. X          in_single = ! in_single;
  109. X        if ((in_double || in_single) && buffer[i] == ' ')
  110. X          buffer[i] = '_';
  111. X      }
  112. X
  113. X      lasttype = type;
  114. X      type = NONE;
  115. X      str = (char *) buffer;
  116. X
  117. X      /** Three pieces to this loop - get the `field', the 'relop' (if
  118. X          there) then, if needed, get the argument to check against (not 
  119. X          needed for errors or the AND, of course)
  120. X      **/
  121. X
  122. X      while ((word = strtok(str, " ()[]:\t\n")) != NULL) {
  123. X
  124. X        str = (char *) NULL;        /* we can start stomping! */
  125. X      
  126. X        lowercase(word);
  127. X
  128. X        if (strcmp(word, "if") == 0) {    /* only ONE 'if' allowed */
  129. X          if ((word = strtok(str, " ()[]:\t\n")) == NULL)    /* NEXT! */
  130. X            continue;
  131. X          lowercase(word);
  132. X        }
  133. X    
  134. X        if (state == NEXT_CONDITION) {
  135. X          lasttype = type;
  136. X          type = NONE;
  137. X
  138. X          if (the_same(word, "not") || the_same(word, "!")) {
  139. X            not_condition = TRUE;
  140. X            if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
  141. X              continue;
  142. X          }
  143. X
  144. X               if (the_same(word, "from"))         type = FROM;
  145. X          else if (the_same(word, "to"))         type = TO;
  146. X          else if (the_same(word, "subject"))   type = SUBJECT;
  147. X          else if (the_same(word, "lines"))     type = LINES;
  148. X          else if (the_same(word, "contains"))  type = CONTAINS;
  149. X          else if (the_same(word, "and") || 
  150. X                   the_same(word, "&&"))         type = AND;
  151. X
  152. X          else if (the_same(word,"?") || the_same(word, "then")) {
  153. X
  154. X        /** shove THIS puppy into the structure and let's continue! **/
  155. X
  156. X            if (lasttype == AND) {
  157. X              fprintf(stderr,
  158. X       "%sfilter (%s): Error reading line %d of rules - badly placed \"and\"\n",
  159. X          BEEP, username, line);
  160. X          return(-1);
  161. X            }
  162. X
  163. X        cond->matchwhat = lasttype;
  164. X            if (relop == NONE) relop = EQ;    /* otherwise can't do -relop */
  165. X            cond->relation  = (not_condition? - (relop) : relop);
  166. X
  167. X        for (i=0;i<strlen(cond_argument);i++)
  168. X              if (cond_argument[i] == '_') cond_argument[i] = ' ';
  169. X
  170. X        strcpy(cond->argument1, cond_argument);
  171. X            if ((newcond = (struct condition_rec *)
  172. X             malloc(sizeof(struct condition_rec))) == NULL) {
  173. X              fprintf(stderr,"Couldn't malloc new cond rec!!\n");
  174. X          return(-1);
  175. X            }
  176. X            cond->next = NULL;
  177. X
  178. X            relop = EQ;    /* default relational condition */
  179. X
  180. X            state = READING_ACTION;
  181. X            if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
  182. X              continue;
  183. X            goto get_outta_loop;
  184. X          }
  185. X
  186. X          if (type == NONE) {
  187. X            fprintf(stderr,
  188. X      "%sfilter (%s): Error reading line %d of rules - field \"%s\" unknown!\n",
  189. X             BEEP, username, line, word);
  190. X        return(-1);
  191. X          }
  192. X
  193. X          if (type == AND) {
  194. X
  195. X        /** shove THIS puppy into the structure and let's continue! **/
  196. X
  197. X        cond->matchwhat = lasttype;
  198. X            cond->relation  = (not_condition? - (relop) : relop);
  199. X        strcpy(cond->argument1, cond_argument);
  200. X            if ((newcond = (struct condition_rec *)
  201. X                 malloc(sizeof(struct condition_rec))) == NULL) {
  202. X              fprintf(stderr,"Couldn't malloc new cond rec!!\n");
  203. X          return(-1);
  204. X            }
  205. X            cond->next = newcond;
  206. X        cond = newcond;
  207. X        cond->next = NULL;
  208. X
  209. X            not_condition = FALSE;
  210. X            state = NEXT_CONDITION;
  211. X          }
  212. X          else {
  213. X            state = GETTING_OP;
  214. X          }
  215. X        }
  216. X
  217. Xget_outta_loop:     /* jump out when we change state, if needed */
  218. X
  219. X        if (state == GETTING_OP) {
  220. X
  221. X           if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
  222. X             continue;
  223. X
  224. X           lowercase(word);
  225. X
  226. X           relop = NONE;
  227. X
  228. X           if (the_same(word, "=") || the_same(word, "in") || 
  229. X                   the_same(word, "contains")) {
  230. X                 state = READING_ARGUMENT;
  231. X             relop = EQ;
  232. X           }
  233. X           else {
  234. X             if (the_same(word, "<="))     relop = LE;
  235. X             else if (the_same(word, ">="))    relop = GE;
  236. X             else if (the_same(word, ">"))    relop = GT;
  237. X             else if (the_same(word, "<>")||
  238. X                  the_same(word, "!="))    relop = NE;
  239. X             else if (the_same(word, "<"))    relop = LT;
  240. X
  241. X         /* maybe there isn't a relop at all!! */
  242. X
  243. X         state=READING_ARGUMENT;
  244. X
  245. X           }
  246. X        }
  247. X         
  248. X        if (state == READING_ARGUMENT) {
  249. X          if (relop != NONE) {
  250. X            if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
  251. X              continue;
  252. X          }
  253. X          for (i=0;i<strlen(word);i++)
  254. X            if (word[i] == '_') word[i] = ' ';
  255. X
  256. X          strcpy(cond_argument, word);
  257. X          state = NEXT_CONDITION;
  258. X        }
  259. X
  260. X        if (state == READING_ACTION) {
  261. X          action = NONE;
  262. X
  263. X          not_condition = FALSE;
  264. X
  265. X          if (the_same(word, "delete"))       action = DELETE;
  266. X          else if (the_same(word, "savec"))   action = SAVECC;
  267. X          else if (the_same(word, "save"))    action = SAVE;
  268. X          else if (the_same(word, "forward")) action = FORWARD;
  269. X          else if (the_same(word, "exec"))    action = EXEC;
  270. X          else if (the_same(word, "leave"))   action = LEAVE;
  271. X          else {
  272. X            fprintf(stderr,
  273. X    "%sfilter (%s): Error on line %d of rules - action \"%s\" unknown\n",
  274. X            BEEP, username, line, word);
  275. X          }
  276. X
  277. X          if (action == DELETE || action == LEAVE) {
  278. X            /** add this to the rules section and alloc next... **/
  279. X
  280. X            rules[total_rules].action = action;
  281. X        rules[total_rules].argument2[0] = '\0';    /* nothing! */
  282. X            total_rules++;
  283. X             
  284. X            if ((cond = (struct condition_rec *)
  285. X             malloc(sizeof(struct condition_rec))) == NULL) {
  286. X              fprintf(stderr,"couldn't malloc first condition rec!\n");
  287. X              return(-1);
  288. X            }
  289. X    
  290. X            rules[total_rules].condition = cond;    /* hooked in! */
  291. X            state = NEXT_CONDITION;    
  292. X          }
  293. X          else {
  294. X            state = ACTION_ARGUMENT;
  295. X          }
  296. X
  297. X          if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
  298. X            continue;
  299. X
  300. X        }
  301. X    
  302. X        if (state == ACTION_ARGUMENT) {
  303. X          strcpy(action_argument, word);
  304. X
  305. X          /** add this to the rules section and alloc next... **/
  306. X
  307. X          rules[total_rules].action = action;
  308. X          expand_macros(action_argument, rules[total_rules].argument2,line);
  309. X          total_rules++;
  310. X             
  311. X          if ((cond = (struct condition_rec *)
  312. X             malloc(sizeof(struct condition_rec))) == NULL) {
  313. X            fprintf(stderr,"couldn't malloc first condition rec!\n");
  314. X            return(-1);
  315. X          }
  316. X    
  317. X          rules[total_rules].condition = cond;    /* hooked in! */
  318. X
  319. X          state = NEXT_CONDITION;
  320. X          if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
  321. X            continue;
  322. X        }
  323. X      }
  324. X    }
  325. X
  326. X    return(0);
  327. X}
  328. END_OF_filter/parse.c
  329. if test 8929 -ne `wc -c <filter/parse.c`; then
  330.     echo shar: \"filter/parse.c\" unpacked with wrong size!?
  331. fi
  332. # end of overwriting check
  333. fi
  334. echo shar: Extracting \"src/input_utils.c\" \(8598 characters\)
  335. if test -f src/input_utils.c ; then 
  336.   echo shar: Will not over-write existing file \"src/input_utils.c\"
  337. else
  338. sed "s/^X//" >src/input_utils.c <<'END_OF_src/input_utils.c'
  339. X/**            input_utils.c            **/
  340. X
  341. X/** Mindless I/O routines for ELM 
  342. X    
  343. X    (C) Copyright 1985 Dave Taylor
  344. X**/
  345. X
  346. X#include "headers.h"
  347. X#include <errno.h>
  348. X
  349. Xextern int errno;        /* system error number */
  350. X
  351. Xunsigned alarm();
  352. X
  353. X#define special_char(c)        (c == ' ' || c == '\t' || c == '/' || c == ',' \
  354. X                 || c == '\0')
  355. X
  356. X#define erase_a_char()        { Writechar(BACKSPACE); Writechar(' '); \
  357. X                      Writechar(BACKSPACE); fflush(stdout); }
  358. X
  359. Xint
  360. Xwant_to(question, dflt, echo_answer)
  361. Xchar *question, dflt;
  362. Xint  echo_answer;
  363. X{
  364. X    /** Ask 'question' at LINES-2, COLUMNS-40, returning the answer in 
  365. X        lower case.  If 'echo_answer', then echo answer.  'dflt' is the 
  366. X        default answer if <return> is pressed. (Note: 'dflt' is also what 
  367. X        will be returned if <return> is pressed!)
  368. X    **/
  369. X    register char ch, cols;
  370. X
  371. X    cols = (strlen(question) < 30)? COLUMNS-40 : COLUMNS-50;
  372. X
  373. X    PutLine3(LINES-3, cols,"%s%c%c", question, dflt, BACKSPACE);
  374. X    fflush(stdout);
  375. X    fflush(stdin);
  376. X    ch = tolower(ReadCh());
  377. X
  378. X    if (echo_answer && ch > (char) ' ') {
  379. X      Writechar(ch);
  380. X      fflush(stdout);
  381. X    }
  382. X
  383. X    return(ch == '\n' || ch == '\r' ? dflt : ch);
  384. X}
  385. X
  386. Xint
  387. Xread_number(ch)
  388. Xchar ch;
  389. X{
  390. X    /** Read a number, where 'ch' is the leading digit! **/
  391. X    
  392. X    char buff[SHORT_SLEN];
  393. X    int  num;
  394. X
  395. X    buff[0] = ch;
  396. X    buff[1] = '\0';
  397. X
  398. X    PutLine0(LINES-3, COLUMNS-40,"Set current message to :");
  399. X    if (optionally_enter(buff, LINES-3, COLUMNS-15, TRUE) == -1)
  400. X      return(current);
  401. X
  402. X    sscanf(buff,"%d", &num);
  403. X    return(num);
  404. X}
  405. X
  406. Xint
  407. Xoptionally_enter(string, x, y, append_current)
  408. Xchar *string;
  409. Xint  x,y, append_current;
  410. X{
  411. X    /** Display the string on the screen and if RETURN is pressed, return 
  412. X        it.  Otherwise, allow standard text input, including backspaces 
  413. X        and such until RETURN is hit.  
  414. X        If "append_current" is set, then leave the default string in 
  415. X        place and edit AFTER it...assume 'x,y' is placing us at the
  416. X        beginning of the string...
  417. X        This routine returns zero unless INTERRUPT hit, then it returns
  418. X        -1 and must be treated accordingly.
  419. X        Added ^W and ^R support...
  420. X        Also added that if x and y are < 0 don't try any cursor stuff
  421. X    **/
  422. X
  423. X    char ch;
  424. X    register int index = 0, use_cursor_control;
  425. X
  426. X    use_cursor_control = ((! mail_only) && x >= 0 && y >= 0);
  427. X    
  428. X    if (use_cursor_control)
  429. X      PutLine1(x,y, "%s", string);    
  430. X    else
  431. X      printf("%s", string);    
  432. X
  433. X    CleartoEOLN();
  434. X
  435. X    if (! append_current) 
  436. X      if (use_cursor_control)
  437. X        MoveCursor(x,y);
  438. X      else
  439. X        non_destructive_back_up(strlen(string));
  440. X
  441. X    if (cursor_control)
  442. X      transmit_functions(OFF);
  443. X
  444. X    ch = getchar();
  445. X
  446. X    if (ch == '\n' || ch == '\r') {
  447. X      if (cursor_control)
  448. X        transmit_functions(ON);
  449. X      return(0);    /* we're done.  No change needed */
  450. X    }
  451. X    
  452. X    CleartoEOLN();
  453. X
  454. X    index = (append_current? strlen(string) : 0);
  455. X
  456. X    if (ch == kill_line) {
  457. X      if (use_cursor_control)
  458. X        MoveCursor(x,y);
  459. X      else
  460. X        back_up(index);
  461. X          CleartoEOLN();
  462. X      index = 0;
  463. X    }
  464. X    else if (ch != backspace) {
  465. X      Writechar(ch);
  466. X      string[index++] = ch;
  467. X    }
  468. X    else if (index > 0) {
  469. X      index--;
  470. X      erase_a_char();
  471. X    }
  472. X    else {
  473. X      Writechar(' ');
  474. X      Writechar(BACKSPACE);
  475. X      fflush(stdout);
  476. X    }
  477. X
  478. X    do {
  479. X      ch = getchar();
  480. X
  481. X      /* the following is converted from a case statement to
  482. X         allow the variable characters (backspace, kill_line
  483. X         and break) to be processed.  Case statements in
  484. X         C require constants as labels, so it failed ...
  485. X      */
  486. X
  487. X        if (ch == backspace) {
  488. X              if (index > 0) {
  489. X            erase_a_char();
  490. X        index--;
  491. X          }
  492. X          else {
  493. X        Writechar(' ');
  494. X        Writechar(BACKSPACE);
  495. X            fflush(stdout);
  496. X          }
  497. X        }
  498. X        else if (ch == '\n' || ch == '\r') {
  499. X          string[index] = '\0';
  500. X          if (cursor_control)
  501. X            transmit_functions(ON);
  502. X          return(0);
  503. X        }
  504. X        else if (ch == ctrl('W')) {        /* back up a word! */
  505. X          if (special_char(string[index]) && index > 0) {
  506. X        index--;
  507. X            erase_a_char();
  508. X          }
  509. X          while (index > 0 && ! special_char(string[index])) {
  510. X        index--;
  511. X            erase_a_char();
  512. X          }
  513. X        }
  514. X        else if (ch == ctrl('R')) {
  515. X          string[index] = '\0';
  516. X          if (use_cursor_control) {
  517. X            PutLine1(x,y, "%s", string);    
  518. X            CleartoEOLN();
  519. X          }
  520. X          else
  521. X            printf("\n%s", string);    
  522. X        }
  523. X        else if (ch == kill_line) {
  524. X          if (use_cursor_control)
  525. X            MoveCursor(x,y);
  526. X          else
  527. X            back_up(index+1);
  528. X              CleartoEOLN();
  529. X          index = 0;
  530. X        }
  531. X        else if (ch == NULL) {
  532. X          if (cursor_control)
  533. X            transmit_functions(ON);
  534. X          fflush(stdin);     /* remove extraneous chars, if any */
  535. X          string[0] = '\0'; /* clean up string, and... */
  536. X          return(-1);
  537. X        }
  538. X        else {  /* default case */
  539. X                
  540. X          string[index++] = ch;
  541. X          Writechar(ch);
  542. X       }
  543. X    } while (index < SLEN);
  544. X
  545. X    string[index] = '\0';
  546. X
  547. X    if (cursor_control)
  548. X      transmit_functions(ON);
  549. X    return(0);
  550. X}
  551. X
  552. Xint
  553. Xpattern_enter(string, alt_string, x, y, alternate_prompt)
  554. Xchar *string, *alt_string, *alternate_prompt;
  555. Xint  x,y;
  556. X{
  557. X    /** This function is functionally similar to the routine
  558. X        optionally-enter, but if the first character pressed
  559. X        is a '/' character, then the alternate prompt and string
  560. X        are used rather than the normal one.  This routine 
  561. X        returns 1 if alternate was used, 0 if not
  562. X    **/
  563. X
  564. X    char ch;
  565. X    register index = 0;
  566. X
  567. X    PutLine1(x, y, "%s", string);    
  568. X    CleartoEOLN();
  569. X    MoveCursor(x,y);
  570. X
  571. X    if (cursor_control)
  572. X      transmit_functions(OFF);
  573. X
  574. X    ch = getchar();
  575. X
  576. X    if (ch == '\n' || ch == '\r') {
  577. X      if (cursor_control)
  578. X        transmit_functions(ON);
  579. X      return(0);    /* we're done.  No change needed */
  580. X    }
  581. X    
  582. X    if (ch == '/') {
  583. X      PutLine1(x, 0, "%s", alternate_prompt);
  584. X      CleartoEOLN();
  585. X      (void) optionally_enter(alt_string, x, strlen(alternate_prompt)+1,
  586. X         FALSE);
  587. X      return(1);
  588. X    }
  589. X
  590. X    CleartoEOLN();
  591. X
  592. X    index = 0;
  593. X
  594. X    if (ch == kill_line) {
  595. X      MoveCursor(x,y);
  596. X          CleartoEOLN();
  597. X      index = 0;
  598. X    }
  599. X    else if (ch != backspace) {
  600. X      Writechar(ch);
  601. X      string[index++] = ch;
  602. X    }
  603. X    else if (index > 0) {
  604. X      index--;
  605. X      erase_a_char();
  606. X    }
  607. X    else {
  608. X      Writechar(' ');
  609. X      Writechar(BACKSPACE);
  610. X    }
  611. X
  612. X    do {
  613. X      fflush(stdout);
  614. X      ch = getchar();
  615. X
  616. X      /* the following is converted from a case statement to
  617. X         allow the variable characters (backspace, kill_line
  618. X         and break) to be processed.  Case statements in
  619. X         C require constants as labels, so it failed ...
  620. X      */
  621. X
  622. X        if (ch == backspace) {
  623. X              if (index > 0) {
  624. X        index--;
  625. X        erase_a_char();
  626. X          }
  627. X          else {
  628. X        Writechar(' ');
  629. X        Writechar(BACKSPACE);
  630. X          }
  631. X        }
  632. X        else if (ch == '\n' || ch == '\r') {
  633. X          string[index] = '\0';
  634. X          if (cursor_control)
  635. X            transmit_functions(ON);
  636. X          return(0);
  637. X        }
  638. X        else if (ch == ctrl('W')) {
  639. X          /* get to rightmost non-alpha */
  640. X          if (special_char (string[index]) && index > 0)
  641. X        index--;
  642. X          while (index > 0 && ! special_char(string[index])) {
  643. X        erase_a_char();
  644. X        index--;
  645. X          }
  646. X        }
  647. X        else if (ch == ctrl('R')) {
  648. X          string[index] = '\0';
  649. X          if (!mail_only) {
  650. X            PutLine1(x,y, "%s", string);    
  651. X            CleartoEOLN();
  652. X          }
  653. X          else
  654. X            printf("\n%s", string);    
  655. X        }
  656. X        else if (ch == kill_line) {
  657. X          MoveCursor(x,y);
  658. X              CleartoEOLN();
  659. X          index = 0;
  660. X        }
  661. X        else if (ch == NULL) {
  662. X          if (cursor_control)
  663. X            transmit_functions(ON);
  664. X          fflush(stdin);     /* remove extraneous chars, if any */
  665. X          string[0] = '\0'; /* clean up string, and... */
  666. X          return(-1);
  667. X        }
  668. X        else {  /* default case */
  669. X                
  670. X          string[index++] = ch;
  671. X          Writechar(ch);
  672. X       }
  673. X    } while (index < SLEN);
  674. X
  675. X    string[index] = '\0';
  676. X
  677. X    if (cursor_control)
  678. X      transmit_functions(ON);
  679. X    return(0);
  680. X}
  681. X
  682. Xback_up(spaces)
  683. Xint spaces;
  684. X{
  685. X    /** this routine is to replace the goto x,y call for when sending
  686. X        mail without starting the entire "elm" system up... **/
  687. X    
  688. X    while (spaces--) {
  689. X      erase_a_char();
  690. X    }
  691. X}
  692. X
  693. Xnon_destructive_back_up(spaces)
  694. Xint spaces;
  695. X{
  696. X    /** same as back_up() but doesn't ERASE the characters on the screen **/
  697. X
  698. X    while (spaces--)
  699. X      Writechar(BACKSPACE); 
  700. X        fflush(stdout); 
  701. X}
  702. X
  703. Xint
  704. XGetPrompt()
  705. X{
  706. X    /** This routine does a read/timeout for a single character.
  707. X        The way that this is determined is that the routine to
  708. X        read a character is called, then the "errno" is checked
  709. X        against EINTR (interrupted call).  If they match, this
  710. X        returns NO_OP_COMMAND otherwise it returns the normal
  711. X        command.    
  712. X    **/
  713. X
  714. X    int ch;
  715. X
  716. X    if (timeout > 0) {
  717. X      alarm((unsigned) timeout);
  718. X      errno = 0;    /* we actually have to do this.  *sigh*  */
  719. X      ch = ReadCh();
  720. X      if (errno == EINTR) ch = NO_OP_COMMAND;
  721. X      alarm((unsigned) 0);
  722. X    }
  723. X    else
  724. X      ch = ReadCh();
  725. X
  726. X    return(ch);
  727. X}
  728. END_OF_src/input_utils.c
  729. if test 8598 -ne `wc -c <src/input_utils.c`; then
  730.     echo shar: \"src/input_utils.c\" unpacked with wrong size!?
  731. fi
  732. # end of overwriting check
  733. fi
  734. echo shar: Extracting \"src/mailmsg1.c\" \(8412 characters\)
  735. if test -f src/mailmsg1.c ; then 
  736.   echo shar: Will not over-write existing file \"src/mailmsg1.c\"
  737. else
  738. sed "s/^X//" >src/mailmsg1.c <<'END_OF_src/mailmsg1.c'
  739. X/**             mailmsg1.c            **/
  740. X
  741. X/** Interface to allow mail to be sent to users.  Part of ELM  **/
  742. X
  743. X/** (C) Copyright 1986, Dave Taylor                    **/
  744. X
  745. X#include "headers.h"
  746. X
  747. X/** strings defined for the hdrconfg routines **/
  748. X
  749. Xchar subject[SLEN], action[SLEN], reply_to[SLEN], expires[SLEN], priority[SLEN];
  750. Xchar to[VERY_LONG_STRING], cc[VERY_LONG_STRING], in_reply_to[SLEN];
  751. Xchar user_defined_header[SLEN];
  752. X
  753. Xchar expanded_to[VERY_LONG_STRING], expanded_cc[VERY_LONG_STRING];
  754. X
  755. X#ifdef ALLOW_BCC
  756. Xchar bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING];
  757. X#endif
  758. X
  759. Xchar *format_long(), *strip_commas(), *tail_of_string(), *strcpy();
  760. Xunsigned long sleep();
  761. X
  762. Xint
  763. Xsend(given_to, given_subject, edit_message, form_letter)
  764. Xchar *given_to, *given_subject;
  765. Xint   edit_message, form_letter;
  766. X{
  767. X    /** Prompt for fields and then call mail() to send the specified
  768. X        message.  If 'edit_message' is true then don't allow the
  769. X            message to be edited. 'form_letter' can be "YES" "NO" or "MAYBE".
  770. X        if YES, then add the header.  If MAYBE, then add the M)ake form
  771. X        option to the last question (see mailsg2.c) etc. etc. **/
  772. X
  773. X    int  copy_msg = FALSE, is_a_response = FALSE;
  774. X
  775. X    /* First: zero all current global message strings */
  776. X
  777. X    cc[0] = action[0] = reply_to[0] = expires[0] = priority[0] = '\0';
  778. X#ifdef ALLOW_BCC
  779. X    bcc[0] = expanded_bcc[0] = '\0';
  780. X#endif
  781. X    in_reply_to[0] = expanded_to[0] = expanded_cc[0] = '\0';
  782. X
  783. X    strcpy(subject, given_subject);        /* copy given subject */
  784. X    strcpy(to, given_to);            /* copy given to:     */
  785. X
  786. X    /******* And now the real stuff! *******/
  787. X
  788. X    copy_msg=copy_the_msg(&is_a_response); /* copy msg into edit buffer? */
  789. X
  790. X    if (get_to(to, expanded_to) == 0)   /* get the To: address and expand */
  791. X      return(0);
  792. X
  793. X    /** are we by any chance just checking the addresses? **/
  794. X
  795. X    if (check_only) {
  796. X      printf("Expands to: %s\n", format_long(expanded_to, 12));
  797. X      putchar('\r');    /* don't ask... */
  798. X      leave();
  799. X    }
  800. X
  801. X    /** if we're batchmailing, let's send it and GET OUTTA HERE! **/
  802. X
  803. X    if (mail_only && strlen(batch_subject) > 0) { 
  804. X      strcpy(subject, batch_subject);    /* get the batch subject */
  805. X      return(mail(FALSE, FALSE, TRUE, form_letter));
  806. X    }
  807. X
  808. X    display_to(expanded_to);    /* display the To: field on screen... */
  809. X
  810. X    dprint1(3,"\nMailing to %s\n", expanded_to);
  811. X  
  812. X    if (get_subject(subject) == 0)        /* get the Subject: field */
  813. X      return(0);
  814. X
  815. X    dprint1(4,"Subject is %s\n", subject);
  816. X
  817. X    if (get_copies(cc, expanded_to, expanded_cc) == 0)
  818. X      return(0);
  819. X
  820. X    if (strlen(cc) > 0)
  821. X      dprint1(4,"Copies to %s\n", expanded_cc);
  822. X
  823. X    if (mail_only)                 /* indicate next step... */
  824. X      printf("\n\r");
  825. X    else
  826. X      MoveCursor(LINES,0);    /* you know you're hit <return> ! */
  827. X
  828. X    /** generate the In-Reply-To: header... **/
  829. X
  830. X    if (is_a_response)
  831. X      generate_reply_to(current-1);
  832. X
  833. X    /* and mail that puppy outta here! */
  834. X
  835. X    mail(copy_msg, edit_message, FALSE, form_letter);
  836. X    
  837. X    return(edit_message);
  838. X}
  839. X
  840. Xget_to(to_field, address)
  841. Xchar *to_field, *address;
  842. X{
  843. X    /** prompt for the "To:" field, expanding into address if possible.
  844. X        This routine returns ZERO if errored, or non-zero if okay **/
  845. X
  846. X    if (strlen(to_field) == 0) {
  847. X      if (user_level < 2) {
  848. X        PutLine0(LINES-2, 0, "Send the message to: ");
  849. X        (void) optionally_enter(to_field, LINES-2, 21, FALSE); 
  850. X      }
  851. X      else {
  852. X        PutLine0(LINES-2, 0, "To: ");
  853. X        (void) optionally_enter(to_field, LINES-2, 4, FALSE); 
  854. X      }
  855. X      if (strlen(to_field) == 0) {
  856. X        ClearLine(LINES-2);    
  857. X        return(0);
  858. X      }
  859. X      build_address(strip_commas(to_field), address); 
  860. X    }
  861. X    else if (mail_only) 
  862. X      build_address(strip_commas(to_field), address); 
  863. X    else 
  864. X      strcpy(address, to_field);
  865. X    
  866. X    if (strlen(address) == 0) {    /* bad address!  Removed!! */
  867. X      if (! mail_only)
  868. X        ClearLine(LINES-2);
  869. X      return(0);
  870. X    }
  871. X
  872. X    return(1);        /* everything is okay... */
  873. X}
  874. X
  875. Xget_subject(subject_field)
  876. Xchar *subject_field;
  877. X{
  878. X    /** get the subject and return non-zero if all okay... **/
  879. X    int len = 9;
  880. X
  881. X    if (mail_only)
  882. X      printf("Subject: ");
  883. X    else 
  884. X      if (user_level == 0) {
  885. X        PutLine0(LINES-2,0,"Subject of message: ");
  886. X        len = 21;
  887. X      }
  888. X      else
  889. X        PutLine0(LINES-2,0,"Subject: ");
  890. X
  891. X    CleartoEOLN();
  892. X
  893. X    if (optionally_enter(subject_field, LINES-2, len, TRUE) == -1) {
  894. X      /** User hit the BREAK key! **/
  895. X      MoveCursor(LINES-2,0);     
  896. X      CleartoEOLN();
  897. X      error("mail not sent");
  898. X      return(0);
  899. X    }
  900. X
  901. X    if (strlen(subject_field) == 0) {    /* zero length subject?? */
  902. X      if (mail_only) 
  903. X        printf("\n\rNo subject - Continue with message? (y/n) n%c",
  904. X          BACKSPACE);
  905. X      else
  906. X        PutLine1(LINES-2,0,"No subject - Continue with message? (y/n) n%c",
  907. X          BACKSPACE);
  908. X
  909. X      if (tolower(ReadCh()) != 'y') {    /* user says no! */
  910. X        if (mail_only) {
  911. X          printf("\n\r\n\rMail Cancelled!\n\r");
  912. X          return(0);
  913. X        }
  914. X        ClearLine(LINES-2);
  915. X        error("mail not sent");
  916. X        return(0);
  917. X      }
  918. X      else if (! mail_only) {
  919. X        PutLine0(LINES-2,0,"Subject: <none>");
  920. X        CleartoEOLN();
  921. X      }
  922. X    }
  923. X
  924. X    return(1);        /** everything is cruising along okay **/
  925. X}
  926. X
  927. Xget_copies(cc_field, address, addressII)
  928. Xchar *cc_field, *address, *addressII;
  929. X{
  930. X    /** Get the list of people that should be cc'd, returning ZERO if
  931. X        any problems arise.  Address and AddressII are for expanding
  932. X        the aliases out after entry! 
  933. X        If 'bounceback' is nonzero, add a cc to ourselves via the remote
  934. X        site, but only if hops to machine are > bounceback threshold.
  935. X    **/
  936. X
  937. X    if (mail_only)
  938. X      printf("\n\rCopies To: ");
  939. X    else
  940. X      PutLine0(LINES-1,0,"Copies To: ");
  941. X
  942. X    fflush(stdout);
  943. X
  944. X    if (optionally_enter(cc_field, LINES-1, 11, FALSE) == -1) {
  945. X      if (mail_only) {
  946. X        printf("\n\r\n\rMail not sent!\n\r");
  947. X        return(0);
  948. X      }
  949. X      ClearLine(LINES-2);
  950. X      ClearLine(LINES-1);
  951. X      
  952. X      error("mail not sent");
  953. X      return(0);
  954. X    }
  955. X    
  956. X    build_address(strip_commas(cc_field), addressII);
  957. X
  958. X    if (strlen(address) + strlen(addressII) > VERY_LONG_STRING) {
  959. X      dprint0(2,
  960. X        "String length of \"To:\" + \"Cc\" too long! (get_copies)\n");
  961. X      error("Too many people.  Copies ignored");
  962. X      sleep(2);
  963. X      cc_field[0] = '\0';
  964. X    }
  965. X
  966. X    return(1);        /* everything looks okay! */
  967. X}
  968. X    
  969. Xint
  970. Xcopy_the_msg(is_a_response)
  971. Xint *is_a_response;
  972. X{
  973. X    /** Returns True iff the user wants to copy the message being
  974. X        replied to into the edit buffer before invoking the editor! 
  975. X        Sets "is_a_response" to true if message is a response...
  976. X    **/
  977. X
  978. X    int answer = FALSE;
  979. X
  980. X    if (strlen(to) > 0 && !mail_only) {    /* predefined 'to' line! */
  981. X      if (auto_copy) 
  982. X        answer = TRUE;
  983. X      else 
  984. X        answer = (want_to("Copy message? (y/n) ", 'n', TRUE) == 'y');
  985. X      *is_a_response = TRUE;
  986. X    }
  987. X    else
  988. X      if (strlen(subject) > 0)      /* predefined 'subject' (Forward) */
  989. X        answer = TRUE;
  990. X
  991. X    return(answer);
  992. X}
  993. X
  994. Xdisplay_to(address)
  995. Xchar *address;
  996. X{
  997. X    /** Simple routine to display the "To:" line according to the
  998. X        current configuration (etc)                   
  999. X     **/
  1000. X    register int open_paren;
  1001. X
  1002. X    if (mail_only)
  1003. X      printf("To: %s\n\r", format_long(address, 3));
  1004. X    else {
  1005. X      if (names_only)
  1006. X        if ((open_paren = chloc(address, '(')) > 0) {
  1007. X          if (open_paren < chloc(address, ')')) {
  1008. X            output_abbreviated_to(address);
  1009. X            return;
  1010. X          } 
  1011. X         }
  1012. X      if (strlen(address) > 45) 
  1013. X        PutLine1(LINES-3, COLUMNS-50, "To: (%s)", 
  1014. X        tail_of_string(address, 40));
  1015. X      else {
  1016. X        if (strlen(address) > 30) 
  1017. X          PutLine1(LINES-3, COLUMNS-50, "To: %s", address);
  1018. X        else
  1019. X          PutLine1(LINES-3, COLUMNS-50, "          To: %s", address);
  1020. X        CleartoEOLN();
  1021. X      }
  1022. X    }
  1023. X}
  1024. X
  1025. Xoutput_abbreviated_to(address)
  1026. Xchar *address;
  1027. X{
  1028. X    /** Output just the fields in parens, separated by commas if need
  1029. X        be, and up to COLUMNS-50 characters...This is only used if the
  1030. X        user is at level BEGINNER.
  1031. X    **/
  1032. X
  1033. X    char newaddress[LONG_STRING];
  1034. X    register int index, newindex = 0, in_paren = 0;
  1035. X
  1036. X    index = 0;
  1037. X
  1038. X    while (newindex < 55 && index < strlen(address)) {
  1039. X      if (address[index] == '(') in_paren++;
  1040. X      else if (address[index] == ')') { 
  1041. X        in_paren--;
  1042. X        if (index < strlen(address)-4) {
  1043. X          newaddress[newindex++] = ',';
  1044. X          newaddress[newindex++] = ' ';
  1045. X        }
  1046. X      }
  1047. X      
  1048. X      if (in_paren && address[index] != '(')
  1049. X          newaddress[newindex++] = address[index];
  1050. X         
  1051. X      index++;
  1052. X    }
  1053. X
  1054. X    newaddress[newindex] = '\0';
  1055. X
  1056. X    if (strlen(newaddress) > 50) 
  1057. X       PutLine1(LINES-3, COLUMNS-50, "To: (%s)", 
  1058. X           tail_of_string(newaddress, 40));
  1059. X     else {
  1060. X       if (strlen(newaddress) > 30)
  1061. X         PutLine1(LINES-3, COLUMNS-50, "To: %s", newaddress);
  1062. X       else
  1063. X         PutLine1(LINES-3, COLUMNS-50, "          To: %s", newaddress);
  1064. X       CleartoEOLN();
  1065. X     }
  1066. X
  1067. X    return;
  1068. X}
  1069. END_OF_src/mailmsg1.c
  1070. if test 8412 -ne `wc -c <src/mailmsg1.c`; then
  1071.     echo shar: \"src/mailmsg1.c\" unpacked with wrong size!?
  1072. fi
  1073. # end of overwriting check
  1074. fi
  1075. echo shar: Extracting \"src/options.c\" \(9046 characters\)
  1076. if test -f src/options.c ; then 
  1077.   echo shar: Will not over-write existing file \"src/options.c\"
  1078. else
  1079. sed "s/^X//" >src/options.c <<'END_OF_src/options.c'
  1080. X/**            options.c            **/
  1081. X
  1082. X/** This set of routines allows the alteration of a number of paramaters
  1083. X    in the Elm mailer, including the following;
  1084. X
  1085. X    calendar-file    <where to put calendar entries>
  1086. X    display pager    <how to page messages>
  1087. X    editor        <name of composition editor>
  1088. X    folder-dir    <folder directory>
  1089. X    sort-by        <how to sort mailboxes>
  1090. X    savefile    <file to save outbound message copies to>
  1091. X    printmail    <how to print messages>
  1092. X    full_username    <your full user name for outgoing mail>
  1093. X
  1094. X    arrow-cursor    <on or off>
  1095. X    menu-display    <on or off>
  1096. X
  1097. X    user-level    <BEGINNER|INTERMEDIATE|EXPERT>
  1098. X        names-only      <on or off>
  1099. X    
  1100. X    And others as they seem useful.
  1101. X
  1102. X    (C) Copyright 1986, Dave Taylor
  1103. X**/
  1104. X
  1105. X#include "headers.h"
  1106. X
  1107. X#undef onoff
  1108. X#define   onoff(n)    (n == 1? "ON ":"OFF")
  1109. X
  1110. Xchar *one_liner_for(), *level_name();
  1111. Xunsigned long sleep();
  1112. X
  1113. Xoptions()
  1114. X{
  1115. X    /** change options... **/
  1116. X
  1117. X    char ch;
  1118. X
  1119. X    display_options();
  1120. X
  1121. X    do {
  1122. X      ClearLine(LINES-4);
  1123. X
  1124. X      Centerline(LINES-4,
  1125. X       "Select first letter of Option line, '>' to Save, or R)eturn");
  1126. X
  1127. X      PutLine0(LINES-2, 0, "Command: ");
  1128. X
  1129. X      ch = tolower(ReadCh());
  1130. X
  1131. X      clear_error();    /* remove possible "sorting" message etc... */ 
  1132. X
  1133. X      one_liner(one_liner_for(ch));
  1134. X
  1135. X      switch (ch) {
  1136. X        case 'c' : optionally_enter(calendar_file, 2, 23, FALSE);    break;
  1137. X        case 'd' : optionally_enter(pager, 3, 23, FALSE);         break;
  1138. X        case 'e' : optionally_enter(editor, 4, 23, FALSE);        break;
  1139. X        case 'f' : optionally_enter(folders, 5, 23, FALSE);        break;
  1140. X        case 's' : change_sort(6,23);                break;
  1141. X        case 'o' : optionally_enter(savefile, 7, 23, FALSE);    break;
  1142. X        case 'p' : optionally_enter(printout, 8, 23, FALSE);    break;
  1143. X        case 'y' : optionally_enter(full_username, 9, 23, FALSE);    break;
  1144. X        case 'a' : on_or_off(&arrow_cursor, 12, 23);         break;
  1145. X        case 'm' : on_or_off(&mini_menu, 13, 23);            
  1146. X               headers_per_page = LINES - (mini_menu ? 13 : 8); break;
  1147. X
  1148. X        case 'u' : switch_user_level(&user_level,15, 23);        break;
  1149. X        case 'n' : on_or_off(&names_only, 16, 23);            break;
  1150. X    
  1151. X        case '?' : options_help(); 
  1152. X                   PutLine0(LINES-2,0,"Command: ");            break;
  1153. X       
  1154. X        case '>' : printf("Save options in .elmrc...");
  1155. X               fflush(stdout);    save_options();        break;
  1156. X
  1157. X        case 'x' :
  1158. X        case 'r' :
  1159. X        case ctrl('M'):
  1160. X        case ctrl('J'): return;
  1161. X        case ctrl('L'): display_options();                break;
  1162. X        default: error("Command unknown!");
  1163. X      }
  1164. X
  1165. X    } while (ch != 'r');
  1166. X}
  1167. X    
  1168. Xdisplay_options()
  1169. X{
  1170. X    /** Display all the available options.. **/
  1171. X    
  1172. X    char *sort_name();
  1173. X    
  1174. X    ClearScreen();
  1175. X    Centerline(0,"-- Elm Options Editor --");
  1176. X
  1177. X#ifdef ENABLE_CALENDAR
  1178. X    PutLine1(2, 0, "C)alendar file       : %s", calendar_file);
  1179. X#endif
  1180. X    PutLine1(3, 0, "D)isplay mail using  : %s", pager);
  1181. X    PutLine1(4, 0, "E)ditor              : %s", editor);
  1182. X    PutLine1(5, 0, "F)older directory    : %s", folders);
  1183. X    PutLine1(6, 0, "S)orting criteria    : %s", sort_name(FULL));
  1184. X    PutLine1(7, 0, "O)utbound mail saved : %s", savefile);
  1185. X    PutLine1(8, 0, "P)rint mail using    : %s", printout);
  1186. X    PutLine1(9, 0, "Y)our full name      : %s", full_username);
  1187. X
  1188. X    PutLine1(12,0, "A)rrow cursor        : %s", onoff(arrow_cursor));
  1189. X    PutLine1(13,0, "M)enu display        : %s", onoff(mini_menu));
  1190. X
  1191. X    PutLine1(15,0, "U)ser level          : %s", level_name(user_level));
  1192. X    PutLine1(16,0, "N)ames only          : %s", onoff(names_only));
  1193. X}
  1194. X
  1195. Xon_or_off(var, x, y)
  1196. Xint *var, x,y;
  1197. X{
  1198. X    /** 'var' field at x.y toggles between on and off... **/
  1199. X
  1200. X    char ch;
  1201. X
  1202. X         PutLine0(x, y+6, 
  1203. X        "(use <space> to toggle, any other key to leave)");
  1204. X
  1205. X    MoveCursor(x,y+3);    /* at end of value... */
  1206. X
  1207. X    do {
  1208. X      ch = ReadCh();
  1209. X
  1210. X      if (ch == SPACE) {
  1211. X        *var = ! *var;
  1212. X        PutLine0(x,y, onoff(*var));
  1213. X      }
  1214. X    } while (ch == SPACE);
  1215. X
  1216. X    MoveCursor(x,y+4);     CleartoEOLN();    /* remove help prompt */
  1217. X}
  1218. X
  1219. X
  1220. Xswitch_user_level(ulevel, x, y)
  1221. Xint *ulevel, x, y;
  1222. X{
  1223. X    /** step through possible user levels... **/
  1224. X
  1225. X         PutLine0(x, y+20, "<space> to change");
  1226. X
  1227. X    MoveCursor(x,y);    /* at end of value... */
  1228. X
  1229. X    while (ReadCh() == ' ') {
  1230. X      *ulevel = (*ulevel == 2? 0 : *ulevel + 1);
  1231. X      PutLine1(x,y, "%s", level_name(*ulevel));
  1232. X    }
  1233. X
  1234. X    MoveCursor(x,y+20);     CleartoEOLN();    /* remove help prompt */
  1235. X}
  1236. X    
  1237. Xchange_sort(x, y)
  1238. Xint x,y;
  1239. X{
  1240. X    /** change the sorting scheme... **/
  1241. X    
  1242. X    int last_sortby,    /* so we know if it changes... */
  1243. X        sign = 1;        /* are we reverse sorting??    */
  1244. X    char ch;        /* character typed in ...      */
  1245. X
  1246. X    last_sortby = sortby;    /* remember current ordering   */
  1247. X
  1248. X    PutLine0(x, COLUMNS-29, "(SPACE for next, or R)everse)");
  1249. X    sort_one_liner(sortby);
  1250. X    MoveCursor(x, y);
  1251. X
  1252. X    do {
  1253. X      ch = tolower(ReadCh());
  1254. X      switch (ch) {
  1255. X        case SPACE : if (sortby < 0) { 
  1256. X                   sign = -1; 
  1257. X                   sortby = - sortby; 
  1258. X               }
  1259. X             else sign = 1;        /* insurance! */
  1260. X               sortby = sign * ((sortby + 1) % (STATUS+1));
  1261. X             if (sortby == 0) sortby = sign;  /* snicker */
  1262. X               PutLine0(x, y, sort_name(PAD));
  1263. X             sort_one_liner(sortby);
  1264. X               MoveCursor(x, y);
  1265. X             break;
  1266. X
  1267. X        case 'r'   : sortby = - sortby;
  1268. X               PutLine0(x, y, sort_name(PAD));
  1269. X             sort_one_liner(sortby);
  1270. X               MoveCursor(x, y);
  1271. X     }
  1272. X        } while (ch == SPACE || ch == 'r');
  1273. X
  1274. X    MoveCursor(x, COLUMNS-30);    CleartoEOLN();
  1275. X
  1276. X    if (sortby != last_sortby) {
  1277. X      error("resorting mailbox...");
  1278. X      sleep(1);
  1279. X      sort_mailbox(message_count, 0);
  1280. X    }
  1281. X    ClearLine(LINES-2);        /* clear sort_one_liner()! */
  1282. X}
  1283. X
  1284. Xone_liner(string)
  1285. Xchar *string;
  1286. X{
  1287. X    /** A single-line description of the selected item... **/
  1288. X
  1289. X    ClearLine(LINES-4);
  1290. X    Centerline(LINES-4, string);
  1291. X}
  1292. X
  1293. Xsort_one_liner(sorting_by)
  1294. Xint sorting_by;
  1295. X{
  1296. X    /** A one line summary of the particular sorting scheme... **/
  1297. X
  1298. X    ClearLine(LINES-2);
  1299. X
  1300. X    switch (sorting_by) {
  1301. X      
  1302. X      case -SENT_DATE : Centerline(LINES-2,
  1303. X"This sort will order most-recently-sent to least-recently-sent");    break;
  1304. X      case -RECEIVED_DATE : Centerline(LINES-2,
  1305. X"This sort will order most-recently-received to least-recently-received");
  1306. X                 break;
  1307. X      case -SENDER : Centerline(LINES-2,
  1308. X"This sort will order by sender name, in reverse alphabetical order");    break;
  1309. X      case -SIZE   : Centerline(LINES-2,
  1310. X"This sort will order messages by longest to shortest");        break;
  1311. X      case -SUBJECT : Centerline(LINES-2,
  1312. X"This sort will order by subject, in reverse alphabetical order");    break;
  1313. X      case -STATUS  : Centerline(LINES-2,
  1314. X"This sort will order by reverse status - Deleted through Tagged...");    break;
  1315. X
  1316. X      case SENT_DATE : Centerline(LINES-2,
  1317. X"This sort will order least-recently-sent to most-recently-sent");    break;
  1318. X      case RECEIVED_DATE : Centerline(LINES-2,
  1319. X"This sort will order least-recently-received to most-recently-received");
  1320. X                        break;
  1321. X      case SENDER : Centerline(LINES-2,
  1322. X                "This sort will order by sender name");    break;
  1323. X      case SIZE   : Centerline(LINES-2,
  1324. X                "This sort will order messages by shortest to longest");
  1325. X            break;
  1326. X      case SUBJECT : Centerline(LINES-2,
  1327. X                    "This sort will order messages by subject");    break;
  1328. X      case STATUS  : Centerline(LINES-2,
  1329. X"This sort will order by status - Tagged through Deleted...");        break;
  1330. X    }
  1331. X}
  1332. X
  1333. Xchar *one_liner_for(c)
  1334. Xchar c;
  1335. X{
  1336. X    /** returns the one-line description of the command char... **/
  1337. X
  1338. X    switch (c) {
  1339. X        case 'c' : return(
  1340. X"This is the file where calendar entries from messages are saved.");
  1341. X
  1342. X        case 'd' : return(
  1343. X"This is the program invoked to display individual messages (try 'builtin')");
  1344. X
  1345. X        case 'e' : return(
  1346. X"This is the editor that will be used for sending messages, etc.");
  1347. X
  1348. X        case 'f' : return(
  1349. X"This is the folders directory used when '=' (etc) is used in filenames");
  1350. X
  1351. X        case 'm' : return(
  1352. X"This determines if you have the mini-menu displayed or not");
  1353. X
  1354. X        case 'n' : return(
  1355. X"Whether to display the names and addresses on mail, or names only");
  1356. X        case 'o' : return(
  1357. X"This is where copies of outbound messages are saved automatically.");
  1358. X
  1359. X        case 'p' : return(
  1360. X"This is how printouts are generated.  \"%s\" will be replaced by the filename.");
  1361. X
  1362. X        case 's' : return(
  1363. X"This is used to specify the sorting criteria for the mailboxes");
  1364. X
  1365. X        case 'y' : return(
  1366. X"When mail is sent out, this is what your full name will be recorded as.");
  1367. X
  1368. X        case 'a' : return(
  1369. X"This defines whether the ELM cursor is an arrow or a highlight bar.");
  1370. X
  1371. X       case 'u' : return(
  1372. X"The level of knowledge you have about the Elm mail system.");
  1373. X
  1374. X        default : return("");    /* nothing if we don't know! */
  1375. X    }
  1376. X}
  1377. X
  1378. Xoptions_help()
  1379. X{
  1380. X    /** help menu for the options screen... **/
  1381. X
  1382. X    char c, *ptr;
  1383. X
  1384. X    Centerline(LINES-3,
  1385. X     "Enter the key you want help on, '?' for a list, or '.' to exit help");
  1386. X
  1387. X    lower_prompt("Key : ");
  1388. X
  1389. X    while ((c = tolower(ReadCh())) != '.') {
  1390. X      if (c == '?') {
  1391. X         display_helpfile(OPTIONS_HELP);
  1392. X         display_options();
  1393. X         return;
  1394. X      }
  1395. X      if ((ptr = one_liner_for(c)) != NULL)
  1396. X        error2("%c = %s", c, ptr);
  1397. X      else
  1398. X        error1("%c isn't used in this section", c);
  1399. X      lower_prompt("Key : ");
  1400. X    }
  1401. X}
  1402. X
  1403. Xchar *level_name(n)
  1404. Xint n;
  1405. X{
  1406. X    /** return the 'name' of the level... **/
  1407. X
  1408. X    switch (n) {
  1409. X      case 0 : return("Beginning User   ");
  1410. X      case 1 : return("Intermediate User");
  1411. X      default: return("Expert User      ");
  1412. X    }
  1413. X}
  1414. END_OF_src/options.c
  1415. if test 9046 -ne `wc -c <src/options.c`; then
  1416.     echo shar: \"src/options.c\" unpacked with wrong size!?
  1417. fi
  1418. # end of overwriting check
  1419. fi
  1420. echo shar: Extracting \"utils/answer.c\" \(8992 characters\)
  1421. if test -f utils/answer.c ; then 
  1422.   echo shar: Will not over-write existing file \"utils/answer.c\"
  1423. else
  1424. sed "s/^X//" >utils/answer.c <<'END_OF_utils/answer.c'
  1425. X/**            answer.c            **/
  1426. X
  1427. X/** This program is a phone message transcription system, and
  1428. X    is designed for secretaries and the like, to allow them to
  1429. X    painlessly generate electronic mail instead of paper forms.
  1430. X
  1431. X    Note: this program ONLY uses the local alias file, and does not
  1432. X      even read in the system alias file at all.
  1433. X
  1434. X    (C) Copyright 1986, Dave Taylor
  1435. X
  1436. X**/
  1437. X
  1438. X#include <stdio.h>
  1439. X#include <fcntl.h>
  1440. X#include <ctype.h>
  1441. X
  1442. X#include "defs.h"            /* ELM system definitions      */
  1443. X
  1444. X#define  ELM        "elm"        /* where the elm program lives */
  1445. X
  1446. X#define  answer_temp_file    "/tmp/answer."
  1447. X
  1448. Xstatic char ident[] = { WHAT_STRING };
  1449. X
  1450. Xstruct alias_rec user_hash_table  [MAX_UALIASES];
  1451. X
  1452. Xint user_data;        /* fileno of user data file   */
  1453. X
  1454. Xchar *expand_group(), *get_alias_address(), *get_token(), *strip_parens();
  1455. X
  1456. Xmain()
  1457. X{
  1458. X    FILE *fd;
  1459. X    char *address, buffer[LONG_STRING], tempfile[SLEN];
  1460. X    char  name[SLEN], user_name[SLEN];
  1461. X    int   msgnum = 0, eof;
  1462. X    
  1463. X    read_alias_files();
  1464. X
  1465. X    while (1) {
  1466. X      if (msgnum > 9999) msgnum = 0;
  1467. X    
  1468. X      printf("\n-------------------------------------------------------------------------------\n");
  1469. X
  1470. Xprompt:   printf("\nMessage to: ");
  1471. X      gets(user_name, SLEN);
  1472. X      if (user_name == NULL)
  1473. X        goto prompt;
  1474. X      
  1475. X      if ((strcmp(user_name,"quit") == 0) ||
  1476. X          (strcmp(user_name,"exit") == 0) ||
  1477. X          (strcmp(user_name,"done") == 0) ||
  1478. X          (strcmp(user_name,"bye")  == 0))
  1479. X         exit(0);
  1480. X
  1481. X      if (translate(user_name, name) == 0)
  1482. X        goto prompt;
  1483. X
  1484. X      address = get_alias_address(name, 1, 0);
  1485. X
  1486. X      printf("address '%s'\n", address);
  1487. X
  1488. X      if (address == NULL || strlen(address) == 0) {
  1489. X        printf("Sorry, could not find '%s' [%s] in list!\n", user_name, 
  1490. X           name);
  1491. X        goto prompt;
  1492. X      }
  1493. X
  1494. X      sprintf(tempfile, "%s%d", answer_temp_file, msgnum++);
  1495. X
  1496. X      if ((fd = fopen(tempfile,"w")) == NULL)
  1497. X        exit(printf("** Fatal Error: could not open %s to write\n",
  1498. X         tempfile));
  1499. X
  1500. X
  1501. X      printf("\nEnter message for %s ending with a blank line.\n\n", 
  1502. X         user_name);
  1503. X
  1504. X      fprintf(fd,"\n\n");
  1505. X
  1506. X      do {
  1507. X       printf("> ");
  1508. X       if (! (eof = (gets(buffer, SLEN) == NULL))) 
  1509. X         fprintf(fd, "%s\n", buffer);
  1510. X      } while (! eof && strlen(buffer) > 0);
  1511. X    
  1512. X      fclose(fd);
  1513. X      sprintf(buffer, 
  1514. X         "((%s -s \"While You Were Out\" %s ; %s %s) & ) < %s > /dev/null",
  1515. X         ELM, strip_parens(address), remove, tempfile, tempfile);
  1516. X
  1517. X      system(buffer);
  1518. X    }
  1519. X}
  1520. X
  1521. Xint
  1522. Xtranslate(fullname, name)
  1523. Xchar *fullname, *name;
  1524. X{
  1525. X    /** translate fullname into name..
  1526. X           'first last'  translated to first_initial - underline - last
  1527. X           'initial last' translated to initial - underline - last
  1528. X        Return 0 if error.
  1529. X    **/
  1530. X    register int i, lastname = 0;
  1531. X
  1532. X    for (i=0; i < strlen(fullname); i++) {
  1533. X
  1534. X      if (isupper(fullname[i]))
  1535. X         fullname[i] = tolower(fullname[i]);
  1536. X
  1537. X      if (fullname[i] == ' ') 
  1538. X        if (lastname) {
  1539. X          printf(
  1540. X          "** Can't have more than 'FirstName LastName' as address!\n");
  1541. X          return(0);
  1542. X        }
  1543. X        else
  1544. X          lastname = i+1;
  1545. X    
  1546. X    }
  1547. X
  1548. X    if (lastname) 
  1549. X      sprintf(name, "%c_%s", fullname[0], (char *) fullname + lastname);
  1550. X    else
  1551. X      strcpy(name, fullname);
  1552. X
  1553. X    return(1);
  1554. X}
  1555. X
  1556. X        
  1557. Xread_alias_files()
  1558. X{
  1559. X    /** read the user alias file **/
  1560. X
  1561. X    char fname[SLEN];
  1562. X    int  hash;
  1563. X
  1564. X    sprintf(fname,  "%s/.alias_hash", getenv("HOME")); 
  1565. X
  1566. X    if ((hash = open(fname, O_RDONLY)) == -1) 
  1567. X      exit(printf("** Fatal Error: Could not open %s!\n", fname));
  1568. X
  1569. X    read(hash, user_hash_table, sizeof user_hash_table);
  1570. X    close(hash);
  1571. X
  1572. X    sprintf(fname,  "%s/.alias_data", getenv("HOME")); 
  1573. X
  1574. X    if ((user_data = open(fname, O_RDONLY)) == -1) 
  1575. X      return;
  1576. X}
  1577. X
  1578. Xchar *get_alias_address(name, mailing, depth)
  1579. Xchar *name;
  1580. Xint   mailing, depth;
  1581. X{
  1582. X    /** return the line from either datafile that corresponds 
  1583. X        to the specified name.  If 'mailing' specified, then
  1584. X        fully expand group names.  Returns NULL if not found.
  1585. X        Depth is the nesting depth, and varies according to the
  1586. X        nesting level of the routine.  **/
  1587. X
  1588. X    static char buffer[VERY_LONG_STRING];
  1589. X    int    loc;
  1590. X
  1591. X    if ((loc = find(name, user_hash_table, MAX_UALIASES)) >= 0) {
  1592. X      lseek(user_data, user_hash_table[loc].byte, 0L);
  1593. X      get_line(user_data, buffer, LONG_STRING);
  1594. X      if (buffer[0] == '!' && mailing)
  1595. X        return( (char *) expand_group(buffer, depth));
  1596. X      else
  1597. X        return( (char *) buffer);
  1598. X    }
  1599. X    
  1600. X    return( (char *) NULL);
  1601. X}
  1602. X
  1603. Xchar *expand_group(members, depth)
  1604. Xchar *members;
  1605. Xint   depth;
  1606. X{
  1607. X    /** given a group of names separated by commas, this routine
  1608. X        will return a string that is the full addresses of each
  1609. X        member separated by spaces.  Depth is the current recursion
  1610. X        depth of the expansion (for the 'get_token' routine) **/
  1611. X
  1612. X    char   buffer[VERY_LONG_STRING];
  1613. X    char   buf[LONG_STRING], *word, *address, *bufptr;
  1614. X
  1615. X    strcpy(buf, members);     /* parameter safety! */
  1616. X    buffer[0] = '\0';    /* nothing in yet!   */
  1617. X    bufptr = (char *) buf;    /* grab the address  */
  1618. X    depth++;        /* one more deeply into stack */
  1619. X
  1620. X    while ((word = (char *) get_token(bufptr, "!, ", depth)) != NULL) {
  1621. X      if ((address = (char *) get_alias_address(word, 1, depth)) == NULL) {
  1622. X        fprintf(stderr, "Alias %s not found for group expansion!", word);
  1623. X        return( (char *) NULL);
  1624. X      }
  1625. X      else if (strcmp(buffer,address) != 0) {
  1626. X        sprintf(buffer,"%s %s", buffer, address);
  1627. X      }
  1628. X
  1629. X      bufptr = NULL;
  1630. X    }
  1631. X
  1632. X    return( (char *) buffer);
  1633. X}
  1634. X
  1635. Xint
  1636. Xfind(word, table, size)
  1637. Xchar *word;
  1638. Xstruct alias_rec table[];
  1639. Xint size;
  1640. X{
  1641. X    /** find word and return loc, or -1 **/
  1642. X    register int loc;
  1643. X    
  1644. X    if (strlen(word) > 20)
  1645. X      exit(printf("Bad alias name: %s.  Too long.\n", word));
  1646. X
  1647. X    loc = hash_it(word, size);
  1648. X
  1649. X    while (strcmp(word, table[loc].name) != 0) {
  1650. X      if (table[loc].name[0] == '\0') 
  1651. X        return(-1);
  1652. X      loc = (loc + 1) % size; 
  1653. X    }
  1654. X
  1655. X    return(loc);
  1656. X}
  1657. X
  1658. Xint
  1659. Xhash_it(string, table_size)
  1660. Xchar *string;
  1661. Xint   table_size;
  1662. X{
  1663. X    /** compute the hash function of the string, returning
  1664. X        it (mod table_size) **/
  1665. X
  1666. X    register int i, sum = 0;
  1667. X    
  1668. X    for (i=0; string[i] != '\0'; i++)
  1669. X      sum += (int) string[i];
  1670. X
  1671. X    return(sum % table_size);
  1672. X}
  1673. X
  1674. Xget_line(fd, buffer)
  1675. Xint fd;
  1676. Xchar *buffer;
  1677. X{
  1678. X    /* read from file fd.  End read upon reading either 
  1679. X       EOF or '\n' character (this is where it differs 
  1680. X       from a straight 'read' command!) */
  1681. X
  1682. X    register int i= 0;
  1683. X    char     ch;
  1684. X
  1685. X    while (read(fd, &ch, 1) > 0)
  1686. X      if (ch == '\n' || ch == '\r') {
  1687. X        buffer[i] = 0;
  1688. X        return;
  1689. X      }
  1690. X      else
  1691. X        buffer[i++] = ch;
  1692. X}
  1693. X
  1694. Xprint_long(buffer, init_len)
  1695. Xchar *buffer;
  1696. Xint   init_len;
  1697. X{
  1698. X    /** print buffer out, 80 characters (or less) per line, for
  1699. X        as many lines as needed.  If 'init_len' is specified, 
  1700. X        it is the length that the first line can be.
  1701. X    **/
  1702. X
  1703. X    register int i, loc=0, space, length; 
  1704. X
  1705. X    /* In general, go to 80 characters beyond current character
  1706. X       being processed, and then work backwards until space found! */
  1707. X
  1708. X    length = init_len;
  1709. X
  1710. X    do {
  1711. X      if (strlen(buffer) > loc + length) {
  1712. X        space = loc + length;
  1713. X        while (buffer[space] != ' ' && space > loc + 50) space--;
  1714. X        for (i=loc;i <= space;i++)
  1715. X          putchar(buffer[i]);
  1716. X        putchar('\n');
  1717. X        loc = space;
  1718. X      }
  1719. X      else {
  1720. X        for (i=loc;i < strlen(buffer);i++)
  1721. X          putchar(buffer[i]);
  1722. X        putchar('\n');
  1723. X        loc = strlen(buffer);
  1724. X      }
  1725. X      length = 80;
  1726. X    } while (loc < strlen(buffer));
  1727. X}
  1728. X
  1729. X/****
  1730. X     The following is a newly chopped version of the 'strtok' routine
  1731. X  that can work in a recursive way (up to 20 levels of recursion) by
  1732. X  changing the character buffer to an array of character buffers....
  1733. X****/
  1734. X
  1735. X#define MAX_RECURSION        20        /* up to 20 deep recursion */
  1736. X
  1737. X#undef  NULL
  1738. X#define NULL            (char *) 0    /* for this routine only   */
  1739. X
  1740. Xextern int strspn();
  1741. Xextern char *strpbrk();
  1742. X
  1743. Xchar *get_token(string, sepset, depth)
  1744. Xchar *string, *sepset;
  1745. Xint  depth;
  1746. X{
  1747. X
  1748. X    /** string is the string pointer to break up, sepstr are the
  1749. X        list of characters that can break the line up and depth
  1750. X        is the current nesting/recursion depth of the call **/
  1751. X
  1752. X    register char    *p, *q, *r;
  1753. X    static char    *savept[MAX_RECURSION];
  1754. X
  1755. X    /** is there space on the recursion stack? **/
  1756. X
  1757. X    if (depth >= MAX_RECURSION) {
  1758. X     fprintf(stderr,"Error: Get_token calls nested greated than %d deep!\n",
  1759. X            MAX_RECURSION);
  1760. X     exit(1);
  1761. X    }
  1762. X
  1763. X    /* set up the pointer for the first or subsequent call */
  1764. X    p = (string == NULL)? savept[depth]: string;
  1765. X
  1766. X    if(p == 0)        /* return if no tokens remaining */
  1767. X        return(NULL);
  1768. X
  1769. X    q = p + strspn(p, sepset);    /* skip leading separators */
  1770. X
  1771. X    if (*q == '\0')        /* return if no tokens remaining */
  1772. X        return(NULL);
  1773. X
  1774. X    if ((r = strpbrk(q, sepset)) == NULL)    /* move past token */
  1775. X        savept[depth] = 0;    /* indicate this is last token */
  1776. X    else {
  1777. X        *r = '\0';
  1778. X        savept[depth] = ++r;
  1779. X    }
  1780. X    return(q);
  1781. X}
  1782. X
  1783. Xchar *strip_parens(string)
  1784. Xchar *string;
  1785. X{
  1786. X    /** Return string with all parenthesized information removed.
  1787. X        This is a non-destructive algorithm... **/
  1788. X
  1789. X    static char  buffer[LONG_STRING];
  1790. X    register int i, depth = 0, buffer_index = 0;
  1791. X
  1792. X    for (i=0; i < strlen(string); i++) {
  1793. X      if (string[i] == '(')
  1794. X        depth++;
  1795. X      else if (string[i] == ')') 
  1796. X        depth--;
  1797. X      else if (depth == 0)
  1798. X        buffer[buffer_index++] = string[i];
  1799. X    }
  1800. X    
  1801. X    buffer[buffer_index] = '\0';
  1802. X
  1803. X    return( (char *) buffer);
  1804. X}
  1805. END_OF_utils/answer.c
  1806. if test 8992 -ne `wc -c <utils/answer.c`; then
  1807.     echo shar: \"utils/answer.c\" unpacked with wrong size!?
  1808. fi
  1809. # end of overwriting check
  1810. fi
  1811. echo shar: End of archive 8 \(of 19\).
  1812. cp /dev/null ark8isdone
  1813. DONE=true
  1814. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  1815.     if test ! -f ark${I}isdone ; then
  1816.     echo shar: You still need to run archive ${I}.
  1817.     DONE=false
  1818.     fi
  1819. done
  1820. if test "$DONE" = "true" ; then
  1821.     echo You have unpacked all 19 archives.
  1822.     echo "See the Instructions file"
  1823.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1824. fi
  1825. ##  End of shell archive.
  1826. exit 0
  1827.