home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume22 / elm2.3 / part25 < prev    next >
Text File  |  1990-06-07  |  51KB  |  1,872 lines

  1. Subject:  v22i083:  ELM mail syste, release 2.3, Part25/26
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: dcc30b68 9a84483e f0b5046f 25130268
  5.  
  6. Submitted-by: Syd Weinstein <syd@dsinc.dsi.com>
  7. Posting-number: Volume 22, Issue 83
  8. Archive-name: elm2.3/part25
  9.  
  10. ---- Cut Here and unpack ----
  11. #!/bin/sh
  12. # this is part 25 of a multipart archive
  13. # do not concatenate these parts, unpack them in order with /bin/sh
  14. # file utils/from.c continued
  15. #
  16. CurArch=25
  17. if test ! -r s2_seq_.tmp
  18. then echo "Please unpack part 1 first!"
  19.      exit 1; fi
  20. ( read Scheck
  21.   if test "$Scheck" != $CurArch
  22.   then echo "Please unpack part $Scheck next!"
  23.        exit 1;
  24.   else exit 0; fi
  25. ) < s2_seq_.tmp || exit 1
  26. echo "x - Continuing file utils/from.c"
  27. sed 's/^X//' << 'SHAR_EOF' >> utils/from.c
  28. X        temp[i] = '\0';
  29. X        temp++;
  30. X      }
  31. X
  32. X      /* if anything is left, let's change 'from' value! */
  33. X
  34. X      if (strlen(temp) > 0)
  35. X        strcpy(newfrom, temp);
  36. X    }
  37. X}
  38. X
  39. Xreverse(string)
  40. Xchar *string;
  41. X{
  42. X    /** reverse string... pretty trivial routine, actually! **/
  43. X
  44. X    char buffer[SLEN];
  45. X    register int i, j = 0;
  46. X
  47. X    for (i = strlen(string)-1; i >= 0; i--)
  48. X      buffer[j++] = string[i];
  49. X
  50. X    buffer[j] = '\0';
  51. X
  52. X    strcpy(string, buffer);
  53. X}
  54. SHAR_EOF
  55. echo "File utils/from.c is complete"
  56. chmod 0444 utils/from.c || echo "restore of utils/from.c fails"
  57. echo "x - extracting utils/listalias.c (Text)"
  58. sed 's/^X//' << 'SHAR_EOF' > utils/listalias.c &&
  59. X
  60. Xstatic char rcsid[] = "@(#)$Id: listalias.c,v 4.1 90/04/28 22:44:42 syd Exp $";
  61. X
  62. X/*******************************************************************************
  63. X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
  64. X *
  65. X *             Copyright (c) 1986, 1987 Dave Taylor
  66. X *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  67. X *******************************************************************************
  68. X * Bug reports, patches, comments, suggestions should be sent to:
  69. X *
  70. X *    Syd Weinstein, Elm Coordinator
  71. X *    elm@DSI.COM            dsinc!elm
  72. X *
  73. X *******************************************************************************
  74. X * $Log:    listalias.c,v $
  75. X * Revision 4.1  90/04/28  22:44:42  syd
  76. X * checkin of Elm 2.3 as of Release PL0
  77. X * 
  78. X *
  79. X ******************************************************************************/
  80. X
  81. X/** Program that lists all the available aliases.  This one uses the pipe 
  82. X    command, feeding the stuff to egrep then sort, or just sort.
  83. X
  84. X**/
  85. X
  86. X#include <stdio.h>
  87. X#include <fcntl.h>
  88. X
  89. X#include "defs.h"
  90. X#include "sysdefs.h"
  91. X
  92. X#ifdef BSD
  93. X  FILE *popen();
  94. X#endif
  95. X
  96. Xchar *getenv();
  97. X
  98. Xmain(argc, argv)
  99. Xint argc;
  100. Xchar *argv[];
  101. X{
  102. X    FILE *datafile, *fd_pipe;
  103. X    struct alias_rec hash_record;
  104. X    int hashfile, count = 0;
  105. X    char buffer[SLEN], fd_hash[SLEN], 
  106. X         fd_data[SLEN], *home;
  107. X
  108. X    if (argc > 2) {
  109. X      printf("Usage: listalias <optional-regular-expression>\n");
  110. X      exit(1);
  111. X    }
  112. X
  113. X    home = getenv("HOME");
  114. X
  115. X    sprintf(fd_hash, "%s/%s", home, ALIAS_HASH);
  116. X    sprintf(fd_data, "%s/%s", home, ALIAS_DATA);
  117. X
  118. X    if (argc > 1)
  119. X      sprintf(buffer, "egrep \"%s\" | sort", argv[1]);
  120. X    else
  121. X      sprintf(buffer, "sort");
  122. X
  123. X    if ((fd_pipe = popen(buffer, "w")) == NULL) {
  124. X      if (argc > 1) 
  125. X        printf("cannot open pipe to egrep program for expressions!\n");
  126. X      fd_pipe = stdout;
  127. X    }
  128. X
  129. X    do {
  130. X
  131. X      if ((hashfile = open(fd_hash, O_RDONLY)) > 0) {
  132. X        if ((datafile = fopen(fd_data, "r")) == NULL) {
  133. X          printf("Opened %s hash file, but couldn't open data file!\n",
  134. X               count? "system" : "user");
  135. X          goto next_file;
  136. X        }
  137. X    
  138. X        /** Otherwise let us continue... **/
  139. X
  140. X        while (read(hashfile, &hash_record, sizeof (hash_record)) != 0) {
  141. X          if (strlen(hash_record.name) > 0) {
  142. X            fseek(datafile, ntohl(hash_record.byte), 0L);
  143. X            fgets(buffer, SLEN, datafile);
  144. X            fprintf(fd_pipe, "%-15s  %s", hash_record.name, buffer);
  145. X          }
  146. X        }
  147. X      }
  148. X
  149. Xnext_file: strcpy(fd_hash, system_hash_file);
  150. X       strcpy(fd_data, system_data_file);
  151. X
  152. X    } while (++count < 2);
  153. X
  154. X    pclose(fd_pipe);
  155. X
  156. X    exit(0);
  157. X}
  158. SHAR_EOF
  159. chmod 0444 utils/listalias.c || echo "restore of utils/listalias.c fails"
  160. echo "x - extracting utils/mailrc.awk (Text)"
  161. sed 's/^X//' << 'SHAR_EOF' > utils/mailrc.awk &&
  162. X#
  163. X# @(#)$Id: mailrc.awk,v 4.1 90/04/28 22:44:44 syd Exp $
  164. X#    Copyright (c) 1986, 1987 Dave Taylor
  165. X#    Copyright (c) 1988, 1989, 1990 USENET Community Trust
  166. X# Bug reports, patches, comments, suggestions should be sent to:
  167. X#
  168. X#    Syd Weinstein, Elm Coordinator - elm@DSI.COM
  169. X#                     dsinc!elm
  170. X#
  171. X# $Log:    mailrc.awk,v $
  172. X# Revision 4.1  90/04/28  22:44:44  syd
  173. X# checkin of Elm 2.3 as of Release PL0
  174. X# 
  175. X#
  176. X
  177. X
  178. XBEGIN { 
  179. X    print "# MSG alias_text file, from a .mailrc file..." 
  180. X    print ""
  181. X      }
  182. X
  183. Xnext_line == 1 { 
  184. X
  185. X    next_line = 0;
  186. X        group = ""
  187. X    for (i = 1; i <= NF; i++) {
  188. X      if (i == NF && $i == "\\") sep = ""
  189. X      else                       sep = ", "
  190. X    
  191. X      if ($i == "\\") {
  192. X        group = sprintf("%s,", group)
  193. X        next_line = 1;
  194. X      }
  195. X      else if (length(group) > 0)
  196. X        group = sprintf("%s%s%s", group, sep, $i);
  197. X      else
  198. X        group = $i;
  199. X      }
  200. X      print "\t" group
  201. X
  202. X    }
  203. X
  204. X$1 ~ /[Aa]lias|[Gg]roup/ { 
  205. X
  206. X    if ( NF == 3)
  207. X      print $2 " = user alias = " $3;
  208. X    else {
  209. X      group = ""
  210. X      for (i = 3; i <= NF; i++) {
  211. X        if (i == NF && $i == "\\") sep = ""
  212. X        else        sep = ", "
  213. X    
  214. X        if ($i == "\\") {
  215. X           group = sprintf("%s,", group)
  216. X           next_line = 1;
  217. X        }
  218. X        else if (length(group) > 0) 
  219. X           group = sprintf("%s%s%s", group, sep, $i);
  220. X        else
  221. X           group = $i;
  222. X        }
  223. X        print $2 " = group alias = " group;
  224. X      }
  225. X     }
  226. SHAR_EOF
  227. chmod 0444 utils/mailrc.awk || echo "restore of utils/mailrc.awk fails"
  228. echo "x - extracting utils/messages (Text)"
  229. sed 's/^X//' << 'SHAR_EOF' > utils/messages &&
  230. X: Use /bin/sh
  231. X# messages: part of the Elm mail system
  232. X# @(#)$Id: messages,v 4.1 90/04/28 22:44:45 syd Exp $
  233. Xif [ "$2" != "" ]; then
  234. X  echo Usage: messages \{folder-name\} 1>&2
  235. X  exit 1
  236. Xfi
  237. Xif [ "$1" = "" ]; then
  238. X  fname=$MAIL
  239. X  optional="in your mailbox"
  240. Xelse
  241. X  fname=$1
  242. X  optional="in folder $1"
  243. Xfi
  244. Xif [ -f "$fname" ]; then
  245. X  mcount=`egrep -c "^From " $fname`
  246. Xelse
  247. X  exit 0
  248. Xfi
  249. Xif [ "$mcount" -eq 1 ]; then
  250. X  echo There is $mcount message $optional.
  251. Xelse
  252. X  echo There are $mcount messages $optional.
  253. Xfi
  254. Xexit $mcount
  255. SHAR_EOF
  256. chmod 0444 utils/messages || echo "restore of utils/messages fails"
  257. echo "x - extracting utils/newalias.c (Text)"
  258. sed 's/^X//' << 'SHAR_EOF' > utils/newalias.c &&
  259. X
  260. Xstatic char rcsid[] = "@(#)$Id: newalias.c,v 4.1 90/04/28 22:44:46 syd Exp $";
  261. X
  262. X/*******************************************************************************
  263. X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
  264. X *
  265. X *             Copyright (c) 1986, 1987 Dave Taylor
  266. X *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  267. X *******************************************************************************
  268. X * Bug reports, patches, comments, suggestions should be sent to:
  269. X *
  270. X *    Syd Weinstein, Elm Coordinator
  271. X *    elm@DSI.COM            dsinc!elm
  272. X *
  273. X *******************************************************************************
  274. X * $Log:    newalias.c,v $
  275. X * Revision 4.1  90/04/28  22:44:46  syd
  276. X * checkin of Elm 2.3 as of Release PL0
  277. X * 
  278. X *
  279. X ******************************************************************************/
  280. X
  281. X/** Install a new set of aliases for the 'Elm' mailer. 
  282. X
  283. X    If invoked with a specific filename, it assumes that
  284. X  it is working with an individual users alias tables, and
  285. X  generates the .alias.hash and .alias.data files in their
  286. X  home directory.
  287. X    If, however, it is invoked with no arguments, then
  288. X  it assumes that the user is updating the system alias
  289. X  file and uses the defaults for everything.
  290. X
  291. X  The format for the input file is;
  292. X    alias1, alias2, ... = username = address
  293. Xor  alias1, alias2, ... = groupname= member, member, member, ...
  294. X                                     member, member, member, ...
  295. X
  296. X**/
  297. X
  298. X#include <stdio.h>
  299. X#include "defs.h"
  300. X#include "sysdefs.h"        /* ELM system definitions */
  301. X#include <ctype.h>
  302. X
  303. X#ifdef BSD
  304. X#  include <sys/file.h>
  305. X#  undef tolower
  306. X#  undef toupper
  307. X#else
  308. X#  include <fcntl.h>
  309. X#endif
  310. X
  311. Xstatic char ident[] = { WHAT_STRING };
  312. X
  313. X#define group(string)        (strpbrk(string,", ") != NULL)
  314. X
  315. Xstruct alias_rec
  316. Xshash_table[MAX_SALIASES];    /* the actual hash table     */
  317. X
  318. Xstruct alias_rec
  319. Xuhash_table[MAX_UALIASES];    /* the actual hash table     */
  320. X
  321. Xint  hash_table_loaded=0;    /* is system table actually loaded? */
  322. X
  323. Xint  buff_loaded;        /* for file input overlap... */
  324. Xint  error= 0;            /* if errors, don't save!    */
  325. Xint  is_system=0;        /* system file updating?     */
  326. Xint  count=0;            /* how many aliases so far?  */
  327. Xlong offset = 0L;        /* data file line offset!    */
  328. Xchar home[SLEN];        /* the users home directory  */
  329. X
  330. Xmain(argc, argv)
  331. Xint argc;
  332. Xchar *argv[];
  333. X{
  334. X    FILE *in, *data;
  335. X    char inputname[SLEN], hashname[SLEN], dataname[SLEN];
  336. X    char buffer[LONG_STRING];
  337. X    int  a, hash, count = 0, owner;
  338. X
  339. X    for (a = 1; a < argc; ++a) {
  340. X      if (strcmp(argv[a], "-g") == 0)
  341. X        is_system = 1;
  342. X      else {
  343. X        printf("Usage: %s [-g]\n", argv[0]);
  344. X        exit(1);
  345. X      }
  346. X    }
  347. X
  348. X    if (is_system) {   /* update system aliases */
  349. X      printf("Updating the system alias file...\n");
  350. X
  351. X      strcpy(inputname, system_text_file);
  352. X      strcpy(hashname,  system_hash_file);
  353. X      strcpy(dataname,  system_data_file);
  354. X      init_table(shash_table, MAX_SALIASES);
  355. X    }
  356. X    else
  357. X      printf("Updating your personal alias file...\n");
  358. X    
  359. X    if (! is_system) {
  360. X      if (strcpy(home, getenv("HOME")) == NULL) {
  361. X        printf("I'm confused - no HOME variable in environment!\n");
  362. X        exit(1);
  363. X      }
  364. X
  365. X      sprintf(inputname, "%s/%s", home, ALIAS_TEXT);
  366. X      sprintf(hashname,  "%s/%s", home, ALIAS_HASH); 
  367. X      sprintf(dataname,  "%s/%s", home, ALIAS_DATA); 
  368. X
  369. X      init_table(uhash_table, MAX_UALIASES); 
  370. X
  371. X      read_in_system(shash_table, sizeof shash_table);
  372. X    }
  373. X
  374. X    if ((in = fopen(inputname,"r")) == NULL) {
  375. X      /** let's see if they have the files in the old place... **/
  376. X      sprintf(buffer, "%s/.alias_text", home);
  377. X      if (access(buffer, ACCESS_EXISTS) != -1) {
  378. X        update_alias_file_locations();
  379. X        in = fopen(inputname, "r");
  380. X      }
  381. X      else {
  382. X        printf("Couldn't open %s for input!\n", inputname);
  383. X        exit(1);
  384. X      }
  385. X    }
  386. X
  387. X    if ((hash = open(hashname, O_WRONLY | O_CREAT, 0644)) == -1) {
  388. X      printf("Couldn't open %s for output!\n", hashname);
  389. X      exit(1);
  390. X    }
  391. X
  392. X    if ((data = fopen(dataname,"w")) == NULL) {
  393. X      printf("Couldn't open %s for output!\n", dataname);
  394. X      exit(1);
  395. X    }
  396. X
  397. X    buff_loaded = 0;     /* file buffer empty right now! */
  398. X
  399. X    while (get_alias(in, buffer) != -1) {
  400. X      if (is_system)
  401. X        put_alias(data, buffer, shash_table, MAX_SALIASES);    
  402. X      else
  403. X        put_alias(data, buffer, uhash_table, MAX_UALIASES);    
  404. X      count++;
  405. X    }
  406. X
  407. X    if (error) {
  408. X      printf("\n** Not saving tables!  Please fix and re-run %s!\n",
  409. X         argv[0]);
  410. X      exit(1);
  411. X    }
  412. X    else {
  413. X      if (is_system)
  414. X        write(hash, shash_table, sizeof shash_table);
  415. X      else
  416. X        write(hash, uhash_table, sizeof uhash_table);
  417. X
  418. X      close(hash);
  419. X      fclose(data);
  420. X      fclose(in);
  421. X    
  422. X      printf("Processed %d aliases\n", count);
  423. X      exit(0);
  424. X    }
  425. X}
  426. X
  427. Xint
  428. Xget_alias(file, buffer)
  429. XFILE *file;
  430. Xchar *buffer;
  431. X{
  432. X    /* load buffer with the next complete alias from the file.
  433. X       (this can include reading in multiple lines and appending
  434. X       them all together!)  Returns EOF after last entry in file.
  435. X    
  436. X    Lines that start with '#' are assumed to be comments and are
  437. X     ignored.  White space as the first field of a line is taken
  438. X    to indicate that this line is a continuation of the previous. */
  439. X
  440. X    static char mybuffer[SLEN];
  441. X    int    done = 0, first_read = 1;
  442. X
  443. X    /** get the first line of the entry... **/
  444. X
  445. X    buffer[0] = '\0';            /* zero out line */
  446. X
  447. X    do {
  448. X      if (get_line(file, mybuffer, first_read) == -1) 
  449. X        return(-1);
  450. X      first_read = 0;
  451. X      if (mybuffer[0] != '#')
  452. X        strcpy(buffer, mybuffer);
  453. X    } while (strlen(buffer) == 0);    
  454. X
  455. X    /** now read in the rest (if there is any!) **/
  456. X
  457. X    do {
  458. X      if (get_line(file, mybuffer, first_read) == -1) {
  459. X        buff_loaded = 0;    /* force a read next pass! */
  460. X        return(0);    /* okay. let's just hand 'buffer' back! */
  461. X      }
  462. X      done = (! whitespace(mybuffer[0]));
  463. X      if (! done)
  464. X        strcat(buffer, mybuffer);
  465. X      done = (done && mybuffer[0] != '#');
  466. X    } while (! done);
  467. X    
  468. X    return(0);    /* no sweat! */
  469. X}
  470. X
  471. Xput_alias(data, buffer, table, size)
  472. XFILE *data;
  473. Xchar *buffer;
  474. Xstruct alias_rec table[];
  475. Xint  size;
  476. X{
  477. X    /** break buffer down into three pieces: aliases, comment, and address.
  478. X        Make the appropriate entries in the table (size) 
  479. X    **/
  480. X
  481. X    char aliases[LONG_STRING], address[LONG_STRING];
  482. X    char comment[LONG_STRING], c;
  483. X    int  first, last, i = 0, j = 0;
  484. X
  485. X    remove_all(' ', TAB, buffer);
  486. X
  487. X    for (i=0; buffer[i] != '=' && i < LONG_STRING; i++)
  488. X      aliases[i] = buffer[i];
  489. X    aliases[i] = '\0';
  490. X
  491. X    for (i=strlen(buffer)-1; buffer[i] != '=' && i > 0; i--)
  492. X      address[j++] = buffer[i];
  493. X    address[j] = '\0';
  494. X
  495. X    comment[0] = '\0';    /* default to nothing at all... */
  496. X
  497. X    if ((first=strlen(aliases)+1) < (last=(strlen(buffer) - j))) {
  498. X      extract_comment(comment, buffer, first, last); 
  499. X    }
  500. X
  501. X    reverse(address);
  502. X
  503. X    add_to_table(data, aliases, comment, address, table, size);
  504. X}
  505. X
  506. Xint
  507. Xget_line(file, buffer, first_line)
  508. XFILE *file;
  509. Xchar *buffer;
  510. Xint  first_line;
  511. X{
  512. X    /** read line from file.  If first_line and buff_loaded, 
  513. X        then just return! **/
  514. X
  515. X    int stat, len;
  516. X
  517. X    if (first_line && buff_loaded) {
  518. X      buff_loaded = 1;
  519. X      return(0);
  520. X    }
  521. X
  522. X    buff_loaded = 1;    /* we're going to get SOMETHING in the buffer */
  523. X
  524. X    stat = fgets(buffer, SLEN, file) == NULL ? -1 : 0;
  525. X
  526. X    if (stat != -1) {
  527. X      len = strlen(buffer);
  528. X      if (len > 0) {
  529. X        if (buffer[len - 1] != '\n') {
  530. X          printf("Line too long, split using continuation line format (starting line\nwith whitespace):\n%s\n\n", buffer);
  531. X          exit(1);
  532. X        }
  533. X      }
  534. X      no_ret(buffer);
  535. X    }
  536. X
  537. X    return(stat);
  538. X}
  539. X
  540. Xreverse(string)
  541. Xchar *string;
  542. X{
  543. X    /** reverse the order of the characters in string... 
  544. X        uses a bubble-sort type of algorithm!                 **/
  545. X    
  546. X    register int f, l;
  547. X    char     c;
  548. X    
  549. X    f = 0;
  550. X    l = strlen(string) - 1;
  551. X    
  552. X    while (f < l) {
  553. X      c = string[f];
  554. X       string[f] = string[l];
  555. X      string[l] = c;
  556. X      f++;
  557. X      l--;
  558. X    }
  559. X}
  560. X
  561. Xadd_to_table(data, aliases, comment, address, table, size)
  562. XFILE *data;
  563. Xchar *aliases, *comment, *address;
  564. Xstruct alias_rec table[];
  565. Xint  size;
  566. X{
  567. X    /** add address + comment to datafile, incrementing offset count 
  568. X        (bytes), then for each alias in the aliases string, add to the
  569. X        hash table, with the associated pointer value! **/
  570. X
  571. X    static char buf[SLEN], *word, *s;
  572. X    long additive = 1L;
  573. X
  574. X    word = buf;    /* use the allocated space! */
  575. X
  576. X    for ( s = aliases ; *s != '\0' && (ok_alias_char(*s)||*s==',') ; ++s ) ;
  577. X    if ( *s != '\0' ) {
  578. X      printf("Error - character '%c' in alias '%s' is not supported.\n",
  579. X        *s, aliases);
  580. X      error++;
  581. X      return;
  582. X    }
  583. X
  584. X    if (group(address)) {
  585. X      check_group(address, aliases);
  586. X      if (error) return;    /* don't do work if we aren't to save it! */
  587. X      fprintf(data, "!%s\n", address);
  588. X      additive = 2L;
  589. X    }
  590. X    else {
  591. X      if (error) return;    /* don't do work if we aren't to save it! */
  592. X      if (strlen(comment) > 0) {
  593. X        fprintf(data, "%s (%s)\n", address, comment);
  594. X        additive = (long) (strlen(comment) + 4);
  595. X      }
  596. X      else
  597. X        fprintf(data, "%s\n", address, comment);
  598. X    }
  599. X
  600. X    while ((word = (char *) strtok(aliases,", ")) != NULL) {
  601. X      add_to_hash_table(word, offset, table, size);
  602. X      aliases = NULL;    /* let's get ALL entries via 'strtok' */
  603. X      count++;
  604. X    }
  605. X
  606. X    if ( is_system ? count > MAX_SALIASES-35 : count > MAX_UALIASES-21) {
  607. X      printf("** Too many aliases in file! **\n");
  608. X      error++;
  609. X    }
  610. X
  611. X    offset = (offset + (long) strlen(address) + additive);
  612. X}    
  613. X
  614. Xremove_all(c1, c2, string)
  615. Xchar c1, c2, *string;
  616. X{
  617. X    /* Remove all occurances of character 'c1' or 'c2' from the string.
  618. X       Hacked (literally) to NOT remove ANY characters from within the
  619. X       equals fields.  This will only be used if the line contains TWO
  620. X       equalss (and comments with equalss in them are the kiss of death!)
  621. X     */
  622. X
  623. X    char buffer[LONG_STRING];
  624. X    register int i = 0, j = 0, first_equals = -1, last_equals = -1;
  625. X    
  626. X    for (i = 0; string[i] != '\0' && i < LONG_STRING; i++) {
  627. X      if (string[i] != c1 && string[i] != c2)
  628. X        buffer[j++] = string[i];
  629. X
  630. X      if (first_equals == -1 && string[i] == '=') {
  631. X        first_equals = i;
  632. X        for (last_equals=strlen(string);string[last_equals] != '='; 
  633. X        last_equals--) ;
  634. X      }
  635. X      else if (i > first_equals && i < last_equals)
  636. X       if (string[i] == c1 || string[i] == c2)
  637. X         buffer[j++] = string[i];
  638. X    }
  639. X    
  640. X    buffer[j] = '\0';
  641. X    strcpy(string, buffer);
  642. X}
  643. X
  644. Xadd_to_hash_table(word, offset, table, size)
  645. Xchar *word;
  646. Xlong  offset;
  647. Xstruct alias_rec table[];
  648. Xint   size;
  649. X{
  650. X    /** add word and offset to current hash table. **/
  651. X    register int loc;
  652. X    
  653. X    if (strlen(word) > 20) {
  654. X      printf("Bad alias name: %s.  Too long.\n", word);
  655. X      exit(1);
  656. X    }
  657. X
  658. X    loc = hash_it(word, size);
  659. X
  660. X    while (table[loc].name[0] != '\0' && stricmp(table[loc].name,word) != 0)
  661. X      loc = (loc + 1) % size; 
  662. X
  663. X    if (table[loc].name[0] == '\0') {
  664. X      strcpy(table[loc].name, word);
  665. X      table[loc].byte = htonl(offset);
  666. X    }
  667. X    else 
  668. X      printf("** Duplicate alias '%s' in file.  Multiples ignored.\n",
  669. X             word);
  670. X}
  671. X
  672. Xint
  673. Xstricmp(s1,s2)
  674. Xregister char *s1, *s2;
  675. X{
  676. X    /* case insensitive comparison */
  677. X    register int d;
  678. X    for (;;) {
  679. X      d = ( isupper(*s1) ? tolower(*s1) : *s1 )
  680. X          - ( isupper(*s2) ? tolower(*s2) : *s2 ) ;
  681. X      if ( d != 0 || *s1 == '\0' || *s2 == '\0' )
  682. X        return d;
  683. X      ++s1;
  684. X      ++s2;
  685. X    }
  686. X    /*NOTREACHED*/
  687. X}
  688. X
  689. Xint
  690. Xhash_it(string, table_size)
  691. Xregister char *string;
  692. Xint   table_size;
  693. X{
  694. X    /** compute the hash function of the string, returning
  695. X        it (mod table_size) **/
  696. X
  697. X    register int sum = 0;
  698. X    for ( ; *string != '\0' ; ++string )
  699. X      sum += (int) ( isupper(*string) ? tolower(*string) : *string );
  700. X    return(sum % table_size);
  701. X}
  702. X
  703. X
  704. Xinit_table(table, size)
  705. Xstruct alias_rec table[];
  706. Xint size;
  707. X{
  708. X    /** initialize hash table! **/
  709. X
  710. X    register int i;
  711. X
  712. X    for (i=0; i < size; i++)
  713. X      table[i].name[0] = '\0';
  714. X}
  715. X
  716. Xread_in_system(table, size)
  717. Xstruct alias_rec table[];
  718. Xint size;
  719. X{
  720. X    /** read in the system hash table...to check for group aliases
  721. X        from the user alias file (to ensure that there are no names
  722. X        in the user group files that are not purely contained within
  723. X        either alias table) **/
  724. X    
  725. X    int  fd;
  726. X    char fname[SLEN];
  727. X
  728. X    sprintf(fname, "%s/%s", mailhome, ALIAS_HASH);
  729. X
  730. X    if ((fd = open(fname, O_RDONLY)) == -1)
  731. X      return;    /* no sweat: flag 'hash_table_loaded' not set! */
  732. X
  733. X    (void) read(fd, table, size);
  734. X    close(fd);
  735. X    hash_table_loaded++;
  736. X}
  737. X    
  738. Xcheck_group(names, groupname)
  739. Xchar *names, *groupname;
  740. X{
  741. X    /** one by one make sure each name in the group is defined
  742. X        in either the system alias file or the user alias file.
  743. X        This search is linearly dependent, so all group aliases
  744. X        in the source file should appear LAST, after all the user
  745. X        aliases! **/
  746. X
  747. X    char *word, *bufptr, buffer[LONG_STRING];
  748. X    int aliased;
  749. X
  750. X    strcpy(buffer, names);
  751. X    bufptr = (char *) buffer;
  752. X    names[0] = '\0';
  753. X
  754. X    while ((word = (char *) strtok(bufptr,", ")) != NULL) {
  755. X      if (! (aliased = can_find(word))) 
  756. X        if (! valid_name(word)) {
  757. X          error++;
  758. X          printf("** Alias %s in group %s is bad!\n", word, groupname);
  759. X        }
  760. X      bufptr = NULL;
  761. X      if (names[0])
  762. X        strcat(names, ", ");
  763. X      strcat(names, word);
  764. X    }
  765. X}
  766. X
  767. Xint
  768. Xcan_find(name)
  769. Xchar *name;
  770. X{    
  771. X    /** find name in either hash table...use 'is_system' variable to
  772. X        determine if we should look in both or just system....    **/
  773. X
  774. X    register int loc;
  775. X    
  776. X    if (strlen(name) > 20) {
  777. X      error++;
  778. X      printf("** Bad alias name: %s.  Too long.\n", name);
  779. X      return(1);    /* fake out: don't want 2 error messages! */
  780. X    }
  781. X
  782. X    /** system alias table... **/
  783. X    if (hash_table_loaded || is_system) {
  784. X      loc = hash_it(name, MAX_SALIASES);
  785. X
  786. X      while (stricmp(name, shash_table[loc].name) != 0 && 
  787. X                 shash_table[loc].name[0] != '\0')
  788. X        loc = (loc + 1) % MAX_SALIASES; 
  789. X  
  790. X      if (stricmp(name, shash_table[loc].name) == 0)
  791. X        return(1);    /* found it! */
  792. X    }
  793. X
  794. X    if (! is_system) {    /* okay! Let's check the user alias file! */
  795. X      loc = hash_it(name, MAX_UALIASES);
  796. X
  797. X      while (stricmp(name, uhash_table[loc].name) != 0 && 
  798. X                 uhash_table[loc].name[0] != '\0')
  799. X        loc = (loc + 1) % MAX_UALIASES; 
  800. X
  801. X      if (stricmp(name, uhash_table[loc].name) == 0)
  802. X        return(1);    /* found it! */
  803. X    }
  804. X
  805. X    return(0);
  806. X}
  807. X
  808. Xextract_comment(comment, buffer, first, last)
  809. Xchar *comment, *buffer;
  810. Xint first, last;
  811. X{
  812. X    /** Buffer contains a comment, located between the first and last
  813. X        values.  Copy that into 'comment', but remove leading and
  814. X        trailing white space.  Note also that it doesn't copy past
  815. X        a comma, so `unpublishable' comments can be of the form;
  816. X        dave: Dave Taylor, HP Labs : taylor@hplabs
  817. X        and the output will be "taylor@hplabs (Dave Taylor)".
  818. X    **/
  819. X
  820. X    register int loc = 0; 
  821. X
  822. X    /** first off, skip the LEADING white space... **/
  823. X
  824. X    while (whitespace(buffer[first])) first++;
  825. X    
  826. X    /** now let's backup the 'last' value until we hit a non-whitespace **/
  827. X
  828. X    last -= 2;    /* starts at ch AFTER equals.. */
  829. X    while (whitespace(buffer[last])) last--;
  830. X
  831. X    /** now a final check to make sure we're still talking about a 
  832. X        reasonable string (rather than a "joe :: joe@dec" type string) **/
  833. X
  834. X    if (first < last) {
  835. X      /* one more check - let's find the comma, if present... */
  836. X      for (loc=first; loc < last; loc++)
  837. X        if (buffer[loc] == ',') {
  838. X          last = loc-1;
  839. X          break;
  840. X      }
  841. X      loc = 0;
  842. X      while (first <= last)
  843. X        comment[loc++] = buffer[first++];
  844. X      comment[loc] = '\0';
  845. X    }
  846. X}
  847. X
  848. Xupdate_alias_file_locations()
  849. X{
  850. X    /** a short-term routine to ensure that the data files are
  851. X        moved into the correct directory... **/
  852. X
  853. X    char source[SLEN], dest[SLEN];
  854. X
  855. X    /** first let's create the directory if it ain't there... **/
  856. X
  857. X    sprintf(source, "%s/.elm", home);
  858. X
  859. X    /** Some systems don't have a mkdir call - how inconvienient! **/
  860. X#ifdef MKDIR
  861. X    (void) mkdir(source, 0700);
  862. X#else
  863. X    system("mkdir $HOME/.elm");
  864. X    system("chmod 700 $HOME/.elm");
  865. X#endif /* MKDIR */
  866. X
  867. X    /** now *link* the files... **/
  868. X
  869. X    sprintf(source, "%s/.alias_text", home);
  870. X    sprintf(dest,   "%s/%s",          home, ALIAS_TEXT);
  871. X    link(source, dest);
  872. X
  873. X    sprintf(source, "%s/.alias_hash", home);
  874. X    sprintf(dest,   "%s/%s",          home, ALIAS_HASH);
  875. X    link(source, dest);
  876. X
  877. X    sprintf(source, "%s/.alias_data", home);
  878. X    sprintf(dest,   "%s/%s",          home, ALIAS_DATA);
  879. X    link(source, dest);
  880. X
  881. X    printf("\n*** Moved all data files into %s/.elm directory ***\n\n",
  882. X        home);
  883. X}
  884. SHAR_EOF
  885. chmod 0444 utils/newalias.c || echo "restore of utils/newalias.c fails"
  886. echo "x - extracting utils/newmail.c (Text)"
  887. sed 's/^X//' << 'SHAR_EOF' > utils/newmail.c &&
  888. X
  889. Xstatic char rcsid[] = "@(#)$Id: newmail.c,v 4.1 90/04/28 22:44:48 syd Exp $";
  890. X
  891. X/*******************************************************************************
  892. X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
  893. X *
  894. X *             Copyright (c) 1986, 1987 Dave Taylor
  895. X *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  896. X *******************************************************************************
  897. X * Bug reports, patches, comments, suggestions should be sent to:
  898. X *
  899. X *    Syd Weinstein, Elm Coordinator
  900. X *    elm@DSI.COM            dsinc!elm
  901. X *
  902. X *******************************************************************************
  903. X * $Log:    newmail.c,v $
  904. X * Revision 4.1  90/04/28  22:44:48  syd
  905. X * checkin of Elm 2.3 as of Release PL0
  906. X * 
  907. X *
  908. X ******************************************************************************/
  909. X
  910. X/** This is actually two programs folded into one - 'newmail()' and
  911. X    'wnewmail()'.  They perform essentially the same function, to
  912. X    monitor the mail arriving in a set of/a mailbox or folder, but
  913. X    newmail is designed to run in background on a terminal, and
  914. X    wnewmail is designed to have a window of its own to run in.
  915. X
  916. X    The main difference is that wnewmail checks for mail more often.
  917. X
  918. X    The usage parameters are:
  919. X
  920. X    -i <interval>          how often to check for mail
  921. X                (default: 60 secs if newmail,
  922. X                      10 secs if wnewmail)
  923. X
  924. X    <filename>        name of a folder to monitor
  925. X                (can prefix with '+'/'=', or can
  926. X                 default to the incoming mailbox)
  927. X
  928. X    <filename>=prefix    file to monitor, output with specified
  929. X                prefix when mail arrives.
  930. X
  931. X    If we're monitoring more than one mailbox the program will prefix
  932. X    each line output (if 'newmail') or each cluster of mail (if 'wnewmail')
  933. X    with the basename of the folder the mail has arrived in.  In the 
  934. X    interest of exhaustive functionality, you can also use the "=prefix"
  935. X    suffix (eh?) to specify your own strings to prefix messages with.
  936. X
  937. X    The output format is either:
  938. X
  939. X      newmail:
  940. X         >> New mail from <user> - <subject>
  941. X         >> Priority mail from <user> - <subject>
  942. X
  943. X         >> <folder>: from <user> - <subject>
  944. X         >> <folder>: Priority from <user> - <subject>
  945. X
  946. X      wnewmail:
  947. X         <user> - <subject>
  948. X         Priority: <user> - <subject>
  949. X
  950. X         <folder>: <user> - <subject>
  951. X         <folder>: Priority: <user> - <subject>\fR
  952. X
  953. X**/
  954. X
  955. X#include <stdio.h>
  956. X#include <errno.h>
  957. X#include <sys/types.h>
  958. X#include <sys/stat.h>
  959. X
  960. X#include "defs.h"
  961. X
  962. X#include <signal.h>    /* background jobs ignore some signals... */
  963. X
  964. Xstatic char ident[] = { WHAT_STRING };
  965. X
  966. X#define LINEFEED        (char) 10
  967. X#define BEGINNING        0            /* seek fseek(3S) */
  968. X#define DEFAULT_INTERVAL    60
  969. X
  970. X#define MAX_FOLDERS        25        /* max we can keep track of */
  971. X
  972. X#define NO_SUBJECT    "(No Subject Specified)"
  973. X
  974. X#define metachar(c)    (c == '+' || c == '=' || c == '%')
  975. X
  976. Xchar  *getusername();
  977. Xlong  bytes();
  978. X
  979. Xstruct folder_struct {
  980. X      char        foldername[SLEN];
  981. X      char        prefix[NLEN];
  982. X      FILE         *fd;
  983. X      long        filesize;
  984. X       } folders[MAX_FOLDERS];
  985. X
  986. Xint  interval_time,        /* how long to sleep between checks */
  987. X     debug = 0,            /* include verbose debug output?    */
  988. X     in_window = 0,        /* are we running as 'wnewmail'?    */
  989. X     total_folders = 0,        /* # of folders we're monitoring    */
  990. X     current_folder = 0;    /* struct pointer for looping       */
  991. X
  992. X#ifdef PIDCHECK
  993. Xint  parent_pid;        /* See if sucide should be attempt  */
  994. X#endif /* PIDCHECK */
  995. X
  996. Xchar hostname[SLEN],            /* name of machine we're on         */
  997. X     hostdomain[SLEN];          /* name of domain we're in          */
  998. X
  999. X#ifdef BSD
  1000. Xextern int errno;
  1001. X#endif
  1002. X
  1003. Xmain(argc, argv)
  1004. Xint argc;
  1005. Xchar *argv[];
  1006. X{
  1007. X    extern char *optarg;
  1008. X    extern int   optind, opterr;
  1009. X    char *ptr;
  1010. X    int c, i, done;
  1011. X    long lastsize,
  1012. X         newsize;            /* file size for comparison..      */
  1013. X
  1014. X#ifdef HOSTCOMPILED
  1015. X    strncpy(hostname, HOSTNAME, sizeof(hostname));
  1016. X#else
  1017. X    gethostname(hostname, sizeof(hostname));
  1018. X#endif
  1019. X    gethostdomain(hostdomain, sizeof(hostdomain));
  1020. X
  1021. X#ifdef PIDCHECK                /* This will get the pid that         */
  1022. X    parent_pid = getppid();        /* started the program, ie: /bin/sh   */
  1023. X                    /* If it dies for some reason (logout)*/
  1024. X#endif /* PIDCHECK */            /* Then exit the program if PIDCHECK  */
  1025. X
  1026. X    interval_time = DEFAULT_INTERVAL;
  1027. X    opterr = 0;
  1028. X
  1029. X    /** let's see if the first character of the basename of the
  1030. X        command invoked is a 'w' (e.g. have we been called as
  1031. X        'wnewmail' rather than just 'newmail'?)
  1032. X    **/
  1033. X
  1034. X    for (i=0, ptr=(argv[0] + strlen(argv[0])-1); !i && ptr > argv[0]; ptr--)
  1035. X      if (*ptr == '/') {
  1036. X        in_window = (*(ptr+1) == 'w');
  1037. X        i++;
  1038. X      }
  1039. X
  1040. X    if (ptr == argv[0] && i == 0 && argv[0][0] == 'w')
  1041. X      in_window = 1;
  1042. X
  1043. X    while ((c = getopt(argc, argv, "di:w")) != EOF) {
  1044. X      switch (c) {
  1045. X        case 'd' : debug++;                    break;
  1046. X        case 'i' : interval_time = atoi(optarg);        break;
  1047. X        case 'w' : in_window = 1;                break;
  1048. X        default  : usage(argv[0]);                exit(1);
  1049. X     }
  1050. X    }
  1051. X
  1052. X    if (interval_time < 10)
  1053. X      fprintf(stderr,
  1054. X"Warning: interval set to %d second%s.  I hope you know what you're doing!\n",
  1055. X      interval_time, interval_time == 1 ? "" : "s");
  1056. X
  1057. X    /* now let's parse the foldernames, if any are given */
  1058. X
  1059. X    if (optind >= argc) /* get default */
  1060. X      add_default_folder();
  1061. X    else {
  1062. X      while (optind < argc)
  1063. X        add_folder(argv[optind++]);
  1064. X      pad_prefixes();            /* for nice output...*/
  1065. X    }
  1066. X
  1067. X#ifdef AUTO_BACKGROUND
  1068. X    if (! in_window) {
  1069. X      if (fork())        /* automatically puts this task in background! */
  1070. X        exit(0);
  1071. X
  1072. X      (void) signal(SIGINT, SIG_IGN);
  1073. X      (void) signal(SIGQUIT, SIG_IGN);
  1074. X    }
  1075. X#endif
  1076. X    (void) signal(SIGHUP, SIG_DFL);
  1077. X
  1078. X    if (in_window && ! debug)
  1079. X      printf("Incoming mail:\n");
  1080. X
  1081. X    while (1) {
  1082. X
  1083. X#ifdef PIDCHECK
  1084. X    if ( kill(parent_pid,0))
  1085. X        exit(0);
  1086. X#else
  1087. X#ifndef AUTO_BACKGROUND        /* won't work if we're nested this deep! */
  1088. X      if (getppid() == 1)     /* we've lost our shell! */
  1089. X        exit();
  1090. X#endif /* AUTO_BACKGROUND */
  1091. X#endif /* PIDCHECK */
  1092. X
  1093. X      if (! isatty(1))    /* we're not sending output to a tty any more */
  1094. X         exit();
  1095. X
  1096. X      if (debug) printf("\n----\n");
  1097. X
  1098. X      for (i = 0; i < total_folders; i++) {
  1099. X
  1100. X        if (debug)
  1101. X          printf("[checking folder #%d: %s]\n", i, folders[i].foldername);
  1102. X
  1103. X        if (folders[i].fd == (FILE *) NULL) {
  1104. X
  1105. X          if ((folders[i].fd = fopen(folders[i].foldername,"r")) == NULL)
  1106. X            if (errno == EACCES) {
  1107. X           fprintf(stderr, "\nPermission to monitor %s denied!\n\n",
  1108. X             folders[i].foldername);
  1109. X               sleep(5);
  1110. X               exit(1);
  1111. X            }
  1112. X        }
  1113. X
  1114. X        if ((newsize = bytes(folders[i].foldername)) > 
  1115. X            folders[i].filesize) {    /* new mail has arrived! */
  1116. X
  1117. X          if (debug)
  1118. X            printf(
  1119. X           "\tnew mail has arrived!  old size = %ld, new size=%ld\n",
  1120. X           folders[i].filesize, newsize);
  1121. X
  1122. X          /* skip what we've read already... */
  1123. X
  1124. X          if (fseek(folders[i].fd, folders[i].filesize, 
  1125. X            BEGINNING) != 0)
  1126. X            perror("fseek()");
  1127. X
  1128. X          folders[i].filesize = newsize;
  1129. X
  1130. X          read_headers(i);        /* read and display new mail! */
  1131. X
  1132. X          if (! in_window)
  1133. X            printf("\n\r");
  1134. X        }
  1135. X        else if (newsize != folders[i].filesize) {    /* file SHRUNK! */
  1136. X
  1137. X          folders[i].filesize = bytes(folders[i].foldername);
  1138. X          (void) fclose(folders[i].fd);    /* close it and ...         */
  1139. X          folders[i].fd = (FILE *) NULL;    /* let's reopen the file    */
  1140. X
  1141. X          lastsize = folders[i].filesize;
  1142. X          done     = 0;
  1143. X
  1144. X          while (! done) {
  1145. X            sleep(0);    /* basically gives up our CPU slice */
  1146. X            newsize = bytes(folders[i].foldername);
  1147. X            if (newsize != lastsize)
  1148. X              lastsize = newsize;
  1149. X        else
  1150. X              done++;
  1151. X          } 
  1152. X            
  1153. X          folders[i].filesize = newsize;
  1154. X        }
  1155. X      }
  1156. X
  1157. X      sleep(interval_time);
  1158. X    }
  1159. X}
  1160. X
  1161. Xint
  1162. Xread_headers(current_folder)
  1163. Xint current_folder;
  1164. X{
  1165. X    /** read the headers, output as found given current_folder,
  1166. X        the prefix of that folder, and whether we're in a window
  1167. X        or not.
  1168. X    **/
  1169. X
  1170. X    char buffer[SLEN], from_whom[SLEN], subject[SLEN];
  1171. X    register int subj = 0, in_header = 0, count = 0, priority=0;
  1172. X#ifdef MMDF
  1173. X    int newheader = 0;
  1174. X#endif /* MMDF */
  1175. X
  1176. X    from_whom[0] = '\0';
  1177. X
  1178. X    while (fgets(buffer, SLEN, folders[current_folder].fd) != NULL) {
  1179. X#ifdef MMDF
  1180. X          if (strcmp(buffer, MSG_SEPERATOR) == 0) {
  1181. X            newheader = !newheader;
  1182. X            if (newheader) {
  1183. X#else
  1184. X      if (first_word(buffer,"From ")) {
  1185. X        if (real_from(buffer, from_whom)) {
  1186. X#endif /* MMDF */
  1187. X          subj = 0;
  1188. X          priority = 0;
  1189. X          in_header = 1;
  1190. X          subject[0] ='\0';
  1191. X          if (in_window)
  1192. X            putchar((char) 007);        /* BEEP!*/
  1193. X          else
  1194. X            printf("\n\r");    /* blank lines surrounding message */
  1195. X
  1196. X        }
  1197. X      }
  1198. X      else if (in_header) {
  1199. X#ifdef MMDF
  1200. X            if (first_word(buffer,"From "))
  1201. X              real_from(buffer, from_whom);
  1202. X#endif /* MMDF */
  1203. X        if (first_word(buffer,">From")) 
  1204. X          forwarded(buffer, from_whom); /* return address */
  1205. X        else if (first_word(buffer,"Subject:") ||
  1206. X             first_word(buffer,"Re:")) {
  1207. X          if (! subj++) {
  1208. X            remove_first_word(buffer);
  1209. X        strcpy(subject, buffer);
  1210. X          }
  1211. X        }
  1212. X        else if (first_word(buffer,"Priority:")) 
  1213. X          priority++;
  1214. X        else if (first_word(buffer,"From:")) 
  1215. X          parse_arpa_from(buffer, from_whom);
  1216. X        else if (buffer[0] == LINEFEED) {
  1217. X          in_header = 0;    /* in body of message! */
  1218. X#ifdef MMDF
  1219. X              if (*from_whom == '\0')
  1220. X                strcpy(from_whom,getusername());
  1221. X#endif /* MMDF */
  1222. X          show_header(priority, from_whom, subject, current_folder);
  1223. X          count++;
  1224. X          from_whom[0] = '\0';
  1225. X        }
  1226. X      }
  1227. X    }
  1228. X    return(count);
  1229. X}
  1230. X
  1231. Xadd_folder(name)
  1232. Xchar *name;
  1233. X{
  1234. X    /* add the specified folder to the list of folders...ignore any
  1235. X       problems we may having finding it (user could be monitoring
  1236. X       a mailbox that doesn't currently exist, for example)
  1237. X    */
  1238. X
  1239. X    char *cp, buf[SLEN];
  1240. X
  1241. X    if (current_folder > MAX_FOLDERS) {
  1242. X      fprintf(stderr,
  1243. X              "Sorry, but I can only keep track of %d folders.\n", MAX_FOLDERS);
  1244. X      exit(1);
  1245. X    }
  1246. X
  1247. X    /* now let's rip off the suffix "=<string>" if it's there... */
  1248. X
  1249. X    for (cp = name + strlen(name); cp > name+1 && *cp != '=' ; cp--)
  1250. X      /* just keep stepping backwards */ ;
  1251. X
  1252. X    /* if *cp isn't pointing to the first character we'e got something! */
  1253. X
  1254. X    if (cp > name+1) {
  1255. X
  1256. X      *cp++ = '\0';        /* null terminate the filename & get prefix */
  1257. X
  1258. X      if (metachar(*cp)) cp++;
  1259. X
  1260. X      strcpy(folders[current_folder].prefix, cp);
  1261. X    }
  1262. X    else {            /* nope, let's get the basename of the file */
  1263. X      for (cp = name + strlen(name); cp > name && *cp != '/'; cp--)
  1264. X        /* backing up a bit... */ ;
  1265. X
  1266. X      if (metachar(*cp)) cp++;
  1267. X      if (*cp == '/') cp++;
  1268. X
  1269. X      strcpy(folders[current_folder].prefix, cp);
  1270. X    }
  1271. X
  1272. X    /* and next let's see what kind of weird prefix chars this user
  1273. X       might be testing us with.  We can have '+'|'='|'%' to expand
  1274. X       or a file located in the incoming mail dir...
  1275. X    */
  1276. X
  1277. X    if (metachar(name[0]))
  1278. X      expand_filename(name, folders[current_folder].foldername);
  1279. X    else if (access(name, 00) == -1) {
  1280. X      /* let's try it in the mail home directory */
  1281. X      sprintf(buf, "%s%s", mailhome, name);
  1282. X      if (access(buf, 00) != -1)         /* aha! */
  1283. X        strcpy(folders[current_folder].foldername, buf);
  1284. X      else
  1285. X        strcpy(folders[current_folder].foldername, name);
  1286. X    }
  1287. X    else
  1288. X      strcpy(folders[current_folder].foldername, name);
  1289. X
  1290. X    /* now let's try to actually open the file descriptor and grab
  1291. X       a size... */
  1292. X
  1293. X    if ((folders[current_folder].fd = 
  1294. X          fopen(folders[current_folder].foldername, "r")) == NULL)
  1295. X          if (errno == EACCES) {
  1296. X        fprintf(stderr, "\nPermission to monitor \"%s\" denied!\n\n",
  1297. X             folders[current_folder].foldername);
  1298. X        exit(1);
  1299. X      }
  1300. X
  1301. X    folders[current_folder].filesize = 
  1302. X          bytes(folders[current_folder].foldername);
  1303. X
  1304. X    /* and finally let's output what we did */
  1305. X
  1306. X    if (debug)
  1307. X      printf("folder %d: \"%s\" <%s> %s, size = %ld\n",
  1308. X          current_folder,
  1309. X          folders[current_folder].foldername,
  1310. X          folders[current_folder].prefix,
  1311. X          folders[current_folder].fd == NULL? "not found" : "opened",
  1312. X          folders[current_folder].filesize);
  1313. X
  1314. X    /* and increment current-folder please! */
  1315. X
  1316. X    current_folder++;
  1317. X    total_folders++;
  1318. X}
  1319. X
  1320. Xadd_default_folder()
  1321. X{
  1322. X    char    *cp;
  1323. X
  1324. X    /* this routine will add the users home mailbox as the folder
  1325. X     * to monitor.  Since there'll only be one folder we'll never
  1326. X     * prefix it either...
  1327. X     *    determine mail file from environment variable if found,
  1328. X     *    else use password entry
  1329. X     */
  1330. X    if ((cp = getenv("MAIL")) == NULL)
  1331. X      sprintf(folders[0].foldername, "%s%s", mailhome, getusername());
  1332. X    else
  1333. X      strcpy(folders[0].foldername, cp);
  1334. X    
  1335. X    folders[0].fd       = fopen(folders[0].foldername, "r");
  1336. X    folders[0].filesize = bytes(folders[0].foldername);
  1337. X
  1338. X    if (debug)
  1339. X      printf("default folder: \"%s\" <%s> %s, size = %ld\n",
  1340. X          folders[0].foldername,
  1341. X          folders[0].prefix,
  1342. X          folders[0].fd == NULL? "not found" : "opened",
  1343. X          folders[0].filesize);
  1344. X
  1345. X    total_folders = 1;
  1346. X}
  1347. X
  1348. Xint
  1349. Xreal_from(buffer, who)
  1350. Xchar *buffer, *who;
  1351. X{
  1352. X    /***** returns true iff 's' has the seven 'from' fields,
  1353. X           initializing the who to the sender *****/
  1354. X
  1355. X    char junk[SLEN], who_tmp[SLEN];
  1356. X
  1357. X    junk[0] = '\0';
  1358. X    who_tmp[0] = '\0';
  1359. X
  1360. X    sscanf(buffer, "%*s %s %*s %*s %*s %*s %s",
  1361. X                who, junk);
  1362. X
  1363. X    if (junk[0] != '\0')
  1364. X        strcpy(who, who_tmp);
  1365. X
  1366. X    return(junk[0] != '\0');
  1367. X}
  1368. X
  1369. Xforwarded(buffer, who)
  1370. Xchar *buffer, *who;
  1371. X{
  1372. X    /** change 'from' and date fields to reflect the ORIGINATOR of 
  1373. X        the message by iteratively parsing the >From fields... **/
  1374. X
  1375. X    char machine[SLEN], buff[SLEN];
  1376. X
  1377. X    machine[0] = '\0';
  1378. X    sscanf(buffer, "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %s",
  1379. X                who, machine);
  1380. X
  1381. X    if(machine[0] == '\0')    /* try for address with timezone in date */
  1382. X    sscanf(buffer, "%*s %s %*s %*s %*s %*s %*s %*s %*s %s",
  1383. X                who, machine);
  1384. X
  1385. X    if (machine[0] == '\0') /* try for srm address */
  1386. X      sscanf(buffer, "%*s %s %*s %*s %*s %*s %*s %*s %s",
  1387. X                who, machine);
  1388. X
  1389. X    if (machine[0] == '\0')
  1390. X      sprintf(buff,"anonymous");
  1391. X    else
  1392. X      sprintf(buff,"%s!%s", machine, who);
  1393. X
  1394. X    strncpy(who, buff, SLEN);
  1395. X}
  1396. X
  1397. X
  1398. Xremove_first_word(string)
  1399. Xchar *string;
  1400. X{    /** removes first word of string, ie up to first non-white space
  1401. X        following a white space! **/
  1402. X
  1403. X    register int loc;
  1404. X
  1405. X    for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++) 
  1406. X        ;
  1407. X
  1408. X    while (string[loc] == ' ' || string[loc] == '\t')
  1409. X      loc++;
  1410. X    
  1411. X    move_left(string, loc);
  1412. X}
  1413. X
  1414. Xmove_left(string, chars)
  1415. Xchar string[];
  1416. Xint  chars;
  1417. X{
  1418. X    /** moves string chars characters to the left DESTRUCTIVELY **/
  1419. X
  1420. X    register int i;
  1421. X
  1422. X    chars--; /* index starting at zero! */
  1423. X
  1424. X    for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
  1425. X      string[i-chars] = string[i];
  1426. X
  1427. X    string[i-chars] = '\0';
  1428. X}
  1429. X
  1430. Xshow_header(priority, from, subject, current_folder)
  1431. Xint   priority;
  1432. Xchar *from, *subject;
  1433. Xint   current_folder;
  1434. X{
  1435. X    /** output header in clean format, including abbreviation
  1436. X        of return address if more than one machine name is
  1437. X        contained within it! **/
  1438. X    char buffer[SLEN];
  1439. X    int  loc, i=0, exc=0, len;
  1440. X
  1441. X#ifndef INTERNET
  1442. X    /* Remove bogus "@host.domain" string. */
  1443. X
  1444. X    sprintf(buffer, "@%s%s", hostname, hostdomain);
  1445. X
  1446. X    if (chloc(from, '!') != -1 && in_string(from, buffer))
  1447. X      from[strlen(from) - strlen(buffer)] = '\0';
  1448. X#endif
  1449. X
  1450. X    loc = strlen(from);
  1451. X
  1452. X    while (exc < 2 && loc > 0)
  1453. X      if (from[--loc] == '!')
  1454. X        exc++;
  1455. X
  1456. X    if (exc == 2) { /* lots of machine names!  Get last one */
  1457. X      loc++;
  1458. X      len = strlen(from);
  1459. X      while (loc < len && loc < SLEN)
  1460. X        buffer[i++] = from[loc++];
  1461. X      buffer[i] = '\0';
  1462. X      strcpy(from, buffer);
  1463. X    }
  1464. X
  1465. X    if (strlen(subject) < 2)
  1466. X      strcpy(subject, NO_SUBJECT);
  1467. X    
  1468. X    if (in_window)
  1469. X      if (total_folders > 1)
  1470. X        printf("%s: %s%s -- %s\n",
  1471. X           folders[current_folder].prefix,
  1472. X           priority? "Priority " : "", from, subject);
  1473. X      else
  1474. X        printf("%s%s -- %s\n",
  1475. X           priority? "Priority " : "", from, subject);
  1476. X    else
  1477. X      if (total_folders > 1)
  1478. X        printf(">> %s: %sail from %s - %s\n\r", 
  1479. X          folders[current_folder].prefix,
  1480. X          priority? "Priority m" : "M", from, subject);
  1481. X      else
  1482. X        printf(">> %sail from %s - %s\n\r", 
  1483. X          priority? "Priority m" : "M", from, subject);
  1484. X}    
  1485. X
  1486. Xparse_arpa_from(buffer, newfrom)
  1487. Xchar *buffer, *newfrom;
  1488. X{
  1489. X    /** try to parse the 'From:' line given... It can be in one of
  1490. X        three formats:
  1491. X        From: Dave Taylor <hpcnou!dat>
  1492. X        or  From: hpcnou!dat (Dave Taylor)
  1493. X        or  From: hpcnou!dat
  1494. X        Change 'newfrom' ONLY if sucessfully parsed this entry and
  1495. X        the resulting name is non-null! 
  1496. X    **/
  1497. X
  1498. X    char temp_buffer[SLEN], *temp;
  1499. X    register int i, j = 0, in_parens;
  1500. X
  1501. X    temp = (char *) temp_buffer;
  1502. X    temp[0] = '\0';
  1503. X
  1504. X    no_ret(buffer);        /* blow away '\n' char! */
  1505. X
  1506. X    if (lastch(buffer) == '>') {
  1507. X      for (i=strlen("From: "); buffer[i] != '\0' && buffer[i] != '<' &&
  1508. X           buffer[i] != '('; i++)
  1509. X        temp[j++] = buffer[i];
  1510. X      temp[j] = '\0';
  1511. X    }
  1512. X    else if (lastch(buffer) == ')') {
  1513. X      in_parens = 1;
  1514. X      for (i=strlen(buffer)-2; buffer[i] != '\0' && buffer[i] != '<'; i--) {
  1515. X        switch(buffer[i]) {
  1516. X        case ')':    in_parens++;
  1517. X            break;
  1518. X        case '(':    in_parens--;
  1519. X            break;
  1520. X        }
  1521. X        if(!in_parens) break;
  1522. X        temp[j++] = buffer[i];
  1523. X      }
  1524. X      temp[j] = '\0';
  1525. X      reverse(temp);
  1526. X    }
  1527. X    else
  1528. X    /* Unusual to have address like -  From: hpcnou!dat
  1529. X     * but valid */
  1530. X    {
  1531. X      for (i=strlen("From: "); buffer[i] != '\0'; i++)
  1532. X        temp[j++] = buffer[i];
  1533. X      temp[j] = '\0';
  1534. X    }        
  1535. X    
  1536. X    if (strlen(temp) > 0) {        /* mess with buffer... */
  1537. X
  1538. X      /* remove leading spaces... */
  1539. X
  1540. X      while (whitespace(temp[0]))
  1541. X        temp = (char *) (temp + 1);        /* increment address! */
  1542. X
  1543. X      /* remove trailing spaces... */
  1544. X
  1545. X      i = strlen(temp) - 1;
  1546. X
  1547. X      while (whitespace(temp[i]))
  1548. X       temp[i--] = '\0';
  1549. X
  1550. X      /* if anything is left, let's change 'from' value! */
  1551. X
  1552. X      if (strlen(temp) > 0)
  1553. X        strcpy(newfrom, temp);
  1554. X    }
  1555. X}
  1556. X
  1557. Xreverse(string)
  1558. Xchar *string;
  1559. X{
  1560. X    /** reverse string... pretty trivial routine, actually! **/
  1561. X
  1562. X    char buffer[SLEN];
  1563. X    register int i, j = 0;
  1564. X
  1565. X    for (i = strlen(string)-1; i >= 0; i--)
  1566. X      buffer[j++] = string[i];
  1567. X
  1568. X    buffer[j] = '\0';
  1569. X
  1570. X    strcpy(string, buffer);
  1571. X}
  1572. X
  1573. Xlong
  1574. Xbytes(name)
  1575. Xchar *name;
  1576. X{
  1577. X    /** return the number of bytes in the specified file.  This
  1578. X        is to check to see if new mail has arrived....  **/
  1579. X
  1580. X    int ok = 1;
  1581. X    extern int errno;    /* system error number! */
  1582. X    struct stat buffer;
  1583. X
  1584. X    if (stat(name, &buffer) != 0)
  1585. X      if (errno != 2) {
  1586. X        fprintf(stderr,"Error %d attempting fstat on %s", errno, name);
  1587. X        exit(1);
  1588. X      }
  1589. X      else
  1590. X        ok = 0;
  1591. X    
  1592. X    return(ok ? buffer.st_size : 0);
  1593. X}
  1594. X
  1595. Xchar  *getusername()
  1596. X{
  1597. X    /** Getting the username on some systems is a real pain, so...
  1598. X       This routine is guaranteed to return a usable username **/
  1599. X
  1600. X    char *return_value, *getlogin(), *cuserid();
  1601. X
  1602. X    if ((return_value = getlogin()) == NULL)
  1603. X      if ((return_value = cuserid(NULL)) == NULL) {
  1604. X        printf("Newmail: I can't get username!\n");
  1605. X        exit(1);
  1606. X      }
  1607. X
  1608. X    return( (char *) return_value);
  1609. X}
  1610. X
  1611. Xusage(name)
  1612. Xchar *name;
  1613. X{
  1614. X    /* print a nice friendly usage message */
  1615. X
  1616. X    fprintf(stderr, 
  1617. X"\nUsage: %s [-d] [-i interval] [-w] {folders}\n", name);
  1618. X    fprintf(stderr, "\nWhere:\n");
  1619. X    fprintf(stderr, "  -d\tturns on debugging output\n");
  1620. X    fprintf(stderr,
  1621. X"  -i D\tsets the interval checking time to 'D' seconds\n");
  1622. X    fprintf(stderr,
  1623. X"  -w\tforces 'window'-style output, and bypasses auto-background\n\n");
  1624. X    fprintf(stderr,
  1625. X"folders can be specified by relative or absolute path names, can be the name\n");
  1626. X    fprintf(stderr,
  1627. X"of a mailbox in the incoming mail directory to check, or can have one of the\n");
  1628. X    fprintf(stderr,
  1629. X"standard Elm mail directory prefix chars (e.g. '+', '=' or '%').\n");
  1630. X    fprintf(stderr,
  1631. X"Furthermore, any folder can have '=string' as a suffix to indicate a folder\n");
  1632. X    fprintf(stderr,
  1633. X"identifier other than the basename of the file\n\n");
  1634. X}
  1635. X
  1636. X
  1637. Xexpand_filename(name, store_space)
  1638. Xchar *name, *store_space;
  1639. X{
  1640. X    strcpy(store_space, name);
  1641. X    if (expand(store_space) == 0) {
  1642. X      fprintf(stderr,"Sorry, but I couldn't expand \"%s\"\n",name);
  1643. X      exit(1);
  1644. X    }
  1645. X}
  1646. X
  1647. Xpad_prefixes()
  1648. X{
  1649. X    /** This simple routine is to ensure that we have a nice
  1650. X        output format.  What it does is whip through the different
  1651. X        prefix strings we've been given, figures out the maximum
  1652. X        length, then space pads the other prefixes to match.
  1653. X    **/
  1654. X
  1655. X    register int i, j, len = 0;
  1656. X
  1657. X    for (i=0; i < total_folders; i++)
  1658. X      if (len < (j=strlen(folders[i].prefix)))
  1659. X        len = j;
  1660. X    
  1661. X    for (i=0; i < total_folders; i++)
  1662. X      for (j = strlen(folders[i].prefix); j < len; j++)
  1663. X        strcat(folders[i].prefix, " ");
  1664. X}
  1665. SHAR_EOF
  1666. chmod 0444 utils/newmail.c || echo "restore of utils/newmail.c fails"
  1667. echo "x - extracting utils/printmail (Text)"
  1668. sed 's/^X//' << 'SHAR_EOF' > utils/printmail &&
  1669. X: Use /bin/sh
  1670. X# printmail: part of the Elm mail system
  1671. X# @(#)$Id: printmail,v 4.1 90/04/28 22:44:50 syd Exp $
  1672. Xif [ "$1" = "-p" ]; then
  1673. X  flags="-p";shift
  1674. Xfi
  1675. Xif [ "$1" != "" ]; then
  1676. X  if [ ! -r $1 ]; then
  1677. X    echo printmail: cannot open folder $1 1>&2
  1678. X    exit 1
  1679. X  else
  1680. X    flags="$flags -f $1"
  1681. X  fi
  1682. Xfi
  1683. X# and now invoke readmsg
  1684. Xexec readmsg $flags \*
  1685. SHAR_EOF
  1686. chmod 0444 utils/printmail || echo "restore of utils/printmail fails"
  1687. echo "x - extracting utils/readmsg.c (Text)"
  1688. sed 's/^X//' << 'SHAR_EOF' > utils/readmsg.c &&
  1689. X
  1690. Xstatic char rcsid[] = "@(#)$Id: readmsg.c,v 4.1 90/04/28 22:44:52 syd Exp $";
  1691. X
  1692. X/*******************************************************************************
  1693. X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
  1694. X *
  1695. X *             Copyright (c) 1986, 1987 Dave Taylor
  1696. X *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  1697. X *******************************************************************************
  1698. X * Bug reports, patches, comments, suggestions should be sent to:
  1699. X *
  1700. X *    Syd Weinstein, Elm Coordinator
  1701. X *    elm@DSI.COM            dsinc!elm
  1702. X *
  1703. X *******************************************************************************
  1704. X * $Log:    readmsg.c,v $
  1705. X * Revision 4.1  90/04/28  22:44:52  syd
  1706. X * checkin of Elm 2.3 as of Release PL0
  1707. X * 
  1708. X *
  1709. X ******************************************************************************/
  1710. X
  1711. X/** This routine adds the functionality of the "~r" command to the Elm mail 
  1712. X    system while still allowing the user to use the editor of their choice.
  1713. X
  1714. X    The program, without any arguments, tries to read a file in the users home 
  1715. X    directory called ".readmsg" (actually defined in the sysdefs.h system 
  1716. X    defines file) and if it finds it reads the current message.  If it doesn't 
  1717. X    find it, it will return a usage error.
  1718. X
  1719. X    The program can also be called with an explicit message number, list of 
  1720. X    message numbers, or a string to match in the message (including the header).
  1721. X    NOTE that when you use the string matching option it will match the first 
  1722. X    message containing that EXACT (case sensitive) string and then exit.
  1723. X**/
  1724. X
  1725. X#include <stdio.h>
  1726. X#include <ctype.h>
  1727. X
  1728. X#include "defs.h"
  1729. X
  1730. X/** three defines for what level of headers to display **/
  1731. X
  1732. X#define ALL        1
  1733. X#define WEED        2
  1734. X#define NONE        3
  1735. X
  1736. X#define metachar(c)    (c == '=' || c == '+' || c == '%')
  1737. X
  1738. Xstatic char ident[] = { WHAT_STRING };
  1739. X
  1740. X#define  MAX_LIST    25        /* largest single list of arguments */
  1741. X
  1742. X#define  LAST_MESSAGE    9999        /* last message in list ('$' char)  */
  1743. X#define  LAST_CHAR    '$'        /* char to delimit last message..   */
  1744. X#define  STAR        '*'        /* char to delimit all messages...  */
  1745. X
  1746. Xint read_message[MAX_LIST];         /* list of messages to read        */
  1747. Xint messages = 0;            /* index into list of messages      */
  1748. X
  1749. Xint numcmp();                /* strcmp, but for numbers          */
  1750. Xchar *words();                /* function defined below...        */
  1751. X
  1752. X
  1753. Xextern char *optarg;        /* for parsing the ...             */
  1754. Xextern int   optind;            /*  .. starting arguments           */
  1755. X
  1756. Xchar *getenv();                /* keep lint happy */
  1757. X
  1758. Xmain(argc, argv)
  1759. Xint argc;
  1760. Xchar *argv[];
  1761. X{
  1762. X    FILE *file;                    /* generic file descriptor! */
  1763. X    char filename[SLEN],             /* filename buffer          */
  1764. X         infile[SLEN],            /* input filename        */
  1765. X         buffer[SLEN],             /* file reading buffer      */
  1766. X         string[SLEN],            /* string match buffer      */
  1767. X         *cp;
  1768. X
  1769. X    int current_in_queue = 0,         /* these are used for...     */
  1770. X        current = 0,            /* ...going through msgs     */
  1771. X        list_all_messages = 0,        /* just list 'em all??       */
  1772. X        num,                 /* for argument parsing      */
  1773. X        page_breaks = 0,            /* use "^L" breaks??         */
  1774. X            total,                /* number of msgs current    */
  1775. X        include_headers = WEED,         /* flag: include msg header? */
  1776. X        last_message = 0,             /* flag: read last message?  */
  1777. X        not_in_header = 0,            /* flag: in msg header?      */
  1778. X#ifdef MMDF
  1779. X        newheader = 0,            /* flag: hit ^A^A^A^A line   */
  1780. X#endif /* MMDF */
  1781. X        string_match = 0;            /* flag: using string match? */
  1782. X        string[0] = '\0';            /* init match string to empty */
  1783. X
  1784. X    /**** start of the actual program ****/
  1785. X
  1786. X    while ((num = getopt(argc, argv, "nhf:p")) != EOF) {
  1787. X      switch (num) {
  1788. X        case 'n' : include_headers = NONE;        break;
  1789. X        case 'h' : include_headers = ALL;        break;
  1790. X        case 'f' : strcpy(infile, optarg);    
  1791. X               if (metachar(infile[0]))
  1792. X                     if (expand(infile) == 0)
  1793. X                       printf("%s: couldn't expand filename %s!\n", 
  1794. X                      argv[0], infile);
  1795. X               break;
  1796. X        case 'p' : page_breaks++;            break;
  1797. X        case '?' : printf(
  1798. X            "Usage: %s [-n|-h] [-f filename] [-p] <message list>\n",
  1799. X             argv[0]);
  1800. X                 exit(1);
  1801. X      }
  1802. X    }
  1803. X    
  1804. X    /** whip past the starting arguments so that we're pointing
  1805. X        to the right stuff... **/
  1806. X
  1807. X    *argv++;    /* past the program name... */
  1808. X
  1809. X    while (optind-- > 1) {
  1810. X      *argv++;
  1811. X      argc--;
  1812. X    }
  1813. X
  1814. X    /** now let's figure out the parameters to the program... **/
  1815. X
  1816. X    if (argc == 1) {    /* no arguments... called from 'Elm'? */
  1817. X      sprintf(filename, "%s/%s", getenv("HOME"), readmsg_file);
  1818. X      if ((file = fopen(filename, "r")) != NULL) {
  1819. X        fscanf(file, "%d", &(read_message[messages++]));
  1820. X        fclose(file);
  1821. X      }
  1822. X      else {    /* no arguments AND no .readmsg file!! */
  1823. X        fprintf(stderr,
  1824. X            "Usage: readmsg [-n|-h] [-f filename] [-p] <message list>\n");
  1825. X        exit(1);
  1826. X      }
  1827. X    }
  1828. X    else if (! isdigit(*argv[0]) && *argv[0] != LAST_CHAR && 
  1829. X             *argv[0] != STAR) {  
  1830. X      string_match++;
  1831. X     
  1832. X      while (*argv)
  1833. X        sprintf(string, "%s%s%s", string, string[0] == '\0'? "" : " ",
  1834. X            *argv++);
  1835. X    }
  1836. X    else if (*argv[0] == STAR)         /* all messages....   */
  1837. X      list_all_messages++;
  1838. X    else {                       /* list of nums   */
  1839. X
  1840. X      while (--argc > 0) {
  1841. X        num = -1;
  1842. X
  1843. X        sscanf(*argv,"%d", &num);
  1844. X
  1845. X        if (num < 0) {
  1846. X          if (*argv[0] == LAST_CHAR) {
  1847. X            last_message++;
  1848. X        num = LAST_MESSAGE;
  1849. X          }
  1850. X          else {
  1851. X            fprintf(stderr,"I don't understand what '%s' means...\n",
  1852. X            *argv); 
  1853. X               exit(1); 
  1854. X          }
  1855. X        }
  1856. X        else if (num == 0) {    /* another way to say "last" */
  1857. SHAR_EOF
  1858. echo "End of part 25"
  1859. echo "File utils/readmsg.c is continued in part 26"
  1860. echo "26" > s2_seq_.tmp
  1861. exit 0
  1862.  
  1863. exit 0 # Just in case...
  1864.