home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume4 / msg / part3 < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  60.1 KB

  1. From: decvax!hplabs!hpcnou!dat (Dave Taylor)
  2. Subject: Msg Shar.part.3
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 4, Issue 7
  7. Submitted by: decvax!hplabs!hpcnou!dat (Dave Taylor)
  8.  
  9. # Msg Shar part 3 of 7
  10.  
  11. # Shell Archive created by hpcnou!dat at Wed Feb 26 15:56:06 1986
  12.  
  13. # To unpack the enclosed files, please use this file as input to the
  14. # Bourne (sh) shell.  This can be most easily done by the command;
  15. #     sh < thisfilename
  16.  
  17. # This archive contains;
  18. #  src/addr_utils.c src/alias.c         src/aliasdb.c       src/aliaslib.c
  19. #  src/args.c       src/curses.c        src/date.c          src/delete.c
  20. #  src/encode.c     src/file.c
  21.  
  22.  
  23. if [ ! -d src ]
  24. then
  25.   echo creating directory src
  26.   mkdir src
  27. fi
  28.  
  29. # ---------- file src/addr_utils.c ----------
  30.  
  31.  
  32. if [ -f src/addr_utils.c ]
  33. then
  34.   echo File 'src/addr_utils.c' already exists\!
  35.   exit 1
  36. fi
  37.  
  38. echo extracting file src/addr_utils.c...
  39. cat << 'END-OF-FILE' > src/addr_utils.c
  40. /**            addr_utils.c            **/
  41.  
  42. /** This file contains addressing utilities 
  43.  
  44.     (C) Copyright 1986 Dave Taylor 
  45. **/
  46.  
  47. #include "headers.h"
  48.  
  49. #include <sys/types.h>
  50. #include <sys/stat.h>
  51. #include <ctype.h>
  52.  
  53. #ifdef BSD
  54. #undef tolower
  55. #endif
  56.  
  57. char *shift_lower(), *get_alias_address(), *get_token();
  58.  
  59. int
  60. talk_to(sitename)
  61. char *sitename;
  62. {
  63.     /** If we talk to the specified site, return true, else
  64.         we're going to have to expand this baby out, so 
  65.         return false! **/
  66.  
  67.     struct lsys_rec  *sysname;
  68.  
  69.     dprint1("talk-to(sitename='%s')\n", sitename);
  70.  
  71.     sysname = talk_to_sys;
  72.  
  73.     if (sysname == NULL) {
  74.       dprint0("\tWarning: talk_to_sys is currently set to NULL!\n");
  75.       return(0);
  76.     }
  77.  
  78.     while (sysname != NULL) {
  79.       if (strcmp(sysname->name, sitename) == 0)
  80.         return(1);
  81.       else
  82.         sysname = sysname->next;
  83.     }
  84.  
  85.     return(0);
  86. }
  87.  
  88. remove_domains(host)
  89. char *host;
  90. {
  91.     /** Remove all entries following the first '.' to ensure that
  92.         entries like "MIT.ARPA" will match "MIT" in the database
  93.     **/
  94.  
  95.     register int loc = 0;
  96.  
  97.     dprint1("remove_domains(host='%s')\n", host);
  98.  
  99.     while (host[loc] != '.' && host[loc] != '\0')
  100.       loc++;
  101.  
  102.     if (host[loc] == '.') host[loc] = '\0';
  103. }
  104.  
  105. add_site(buffer, site, lastsite)
  106. char *buffer, *site, *lastsite;
  107. {
  108.     /** add site to buffer, unless site is 'uucp', current machine, or
  109.             site is the same as lastsite.   If not, set lastsite to
  110.             site.
  111.     **/
  112.  
  113.     char local_buffer[LONG_SLEN];
  114.  
  115.     dprint3("add_site(buffer='%s', site='%s', lastsite='%s')\n", 
  116.              buffer, site, lastsite);
  117.  
  118.     if (strcmp(site, "uucp") != 0)
  119.       if (strcmp(site, lastsite) != 0) 
  120.         if (strcmp(site, hostname) != 0) {
  121.           if (buffer[0] == '\0')
  122.             strcpy(buffer, strip_parens(site));         /* first in list! */
  123.           else {
  124.             sprintf(local_buffer,"%s!%s", buffer, strip_parens(site));
  125.             strcpy(buffer, local_buffer);
  126.           }
  127.           strcpy(lastsite, strip_parens(site)); /* don't want THIS twice! */
  128.         }
  129. }
  130.  
  131. #ifdef USE_EMBEDDED_ADDRESSES
  132.  
  133. get_address_from(prefix, line, buffer)
  134. char *prefix, *line, *buffer;
  135. {
  136.     /** This routine extracts the address from either a 'From:' line
  137.         or a 'Reply-To:' line...the algorithm is quite simple, too:
  138.         increment 'line' past header, then check last character of 
  139.         line.  If it's a '>' then the address is contained within '<>'
  140.         and if it's a ')' then the address is in the 'clear'... **/
  141.  
  142.     register int i, j = 0;
  143.     
  144.     no_ret(line);
  145.  
  146.     dprint2("get_address_from(prefix='%s', line='%s', <buffer>)\n", 
  147.          prefix, line);
  148.  
  149.     line = (char *) (line + strlen(prefix) + 1);
  150.  
  151.     if (line[strlen(line)-1] == '>') {
  152.       for (i=strlen(line)-2; i > -1 && line[i] != '<'; i--)
  153.         buffer[j++] = line[i];
  154.       buffer[j] = 0;
  155.       reverse(buffer);
  156.     }
  157.     else {    /* either ')' or address in the clear... */
  158.       for (i=0; i < strlen(line) && line[i] != '('; i++)
  159.         buffer[j++] = line[i];
  160.       if (buffer[j-1] == '(') j--;
  161.       buffer[j] = 0;
  162.     }
  163. }
  164.  
  165. #endif
  166.  
  167. translate_return(addr, ret_addr)
  168. char *addr, *ret_addr;
  169. {
  170.     /** Return ret_addr to be the same as addr, but with the login 
  171.             of the person sending the message replaced by '%s' for 
  172.             future processing... 
  173.         Fixed to make "%xx" "%%xx" (dumb 'C' system!) 
  174.     **/
  175.  
  176.     register int loc, loc2, index = 0;
  177.     
  178.     dprint1("translate_return(addr='%s', <return buffer>)\n", addr);
  179.  
  180.     loc2 = chloc(addr,'@');
  181.     if ((loc = chloc(addr, '%')) < loc2)
  182.       loc2 = loc;
  183.  
  184.     if (loc2 != -1) {    /* ARPA address. */
  185.       /* algorithm is to get to '@' sign and move backwards until
  186.          we've hit the beginning of the word or another metachar.
  187.       */
  188.       for (loc = loc2 - 1; loc > -1 && addr[loc] != '!'; loc--)
  189.          ;
  190.     }
  191.     else {            /* usenet address */
  192.       /* simple algorithm - find last '!' */
  193.  
  194.       loc2 = strlen(addr);    /* need it anyway! */
  195.  
  196.       for (loc = loc2; loc > -1 && addr[loc] != '!'; loc--)
  197.           ;
  198.     }
  199.     
  200.     /** now copy up to 'loc' into destination... **/
  201.  
  202.     while (index <= loc) {
  203.       ret_addr[index] = addr[index];
  204.       index++;
  205.     }
  206.  
  207.     /** now append the '%s'... **/
  208.  
  209.     ret_addr[index++] = '%';
  210.     ret_addr[index++] = 's';
  211.  
  212.     /** and, finally, if anything left, add that **/
  213.  
  214.     while (loc2 < strlen(addr)) {
  215.       ret_addr[index++] = addr[loc2++];
  216.       if (addr[loc2-1] == '%')    /* tweak for "printf" */
  217.         ret_addr[index++] = '%';
  218.     }
  219.     
  220.     ret_addr[index] = '\0';
  221.  
  222.     dprint1("\treturning address: '%s'\n", ret_addr);
  223. }
  224.  
  225. build_address(to, full_to)
  226. char *to, *full_to;
  227. {
  228.     /** loop on all words in 'to' line...append to full_to as
  229.         we go along, until done or length > len **/
  230.  
  231.     register int i, changed = 0;
  232.     char word[SLEN], *ptr, buffer[SLEN];
  233.     char new_to_list[LONG_SLEN];
  234.  
  235.     dprint1("build_address(to='%s', <buffer>)\n", to);
  236.  
  237.     new_to_list[0] = '\0';
  238.  
  239.     i = get_word(to, 0, word);
  240.  
  241.     full_to[0] = '\0';
  242.  
  243.     while (i > 0) {
  244.  
  245.       if (strpbrk(word,"!@:") != NULL)
  246.         sprintf(full_to, "%s%s%s", full_to,
  247.                     full_to[0] != '\0'? ", " : "", expand_system(word, 1));
  248.       else if ((ptr = get_alias_address(word, 1, 0)) != NULL)
  249.         sprintf(full_to, "%s%s%s", full_to, 
  250.                     full_to[0] != '\0'? ", " : "", ptr);
  251.       else if (strlen(word) > 0) {
  252.         if (valid_name(word)) 
  253.           sprintf(full_to, "%s%s%s", full_to,
  254.                       full_to[0] != '\0'? ", " : "", word);
  255.         else if (check_only) {
  256.           printf("(alias \"%s\" is unknown)\n\r",
  257.              word);
  258.           changed++;
  259.         }
  260.         else if (! isatty(fileno(stdin)) ) {    /* batch mode error! */
  261.           fprintf(stderr,"Cannot expand alias '%s'!\n\r", word);
  262.           fprintf(stderr,"Use \"checkalias\" to find valid addresses!\n\r");
  263.           leave(1);
  264.         }
  265.         else {
  266.           dprint1("-- Unknown address '%s'\n", buffer);
  267.           sprintf(buffer, "'%s' is an unknown address.  Replace with: ", 
  268.                   word);
  269.           word[0] = '\0';
  270.           if (mail_only) 
  271.              printf("%s", buffer);
  272.           else
  273.             PutLine(LINES, 0, buffer);
  274.         
  275.           (void) optionally_enter(word, LINES, strlen(buffer), FALSE);
  276.           if (strlen(word) > 0)
  277.             sprintf(new_to_list, "%s%s%s", new_to_list,
  278.             strlen(new_to_list) > 0? " ":"", word);
  279.           if (mail_only) printf("\n\r");
  280.           changed++;
  281.           clear_error();
  282.           continue;
  283.         }
  284.       }
  285.  
  286.       i = get_word(to, i, word);
  287.     }
  288.  
  289.     if (changed)
  290.       strcpy(to, new_to_list);
  291. }
  292.  
  293. int
  294. real_from(buffer, entry)
  295. char *buffer;
  296. struct header_rec *entry;
  297. {
  298.     /***** Returns true iff 's' has the seven 'from' fields, (or
  299.            8 - some machines include the TIME ZONE!!!)
  300.            Initializing the date and from entries in the record 
  301.            and also the message received date/time.  *****/
  302.  
  303.     char junk[SLEN], timebuff[SLEN];
  304.     int  eight_fields = 0;
  305.  
  306.     dprint1("real_from(buffer='%s', <header_rec>)\n", buffer);
  307.  
  308.     entry->year[0] = '\0';
  309.     junk[0] = '\0';
  310.  
  311.     /* From <user> <day> <month> <day> <hr:min:sec> <year> */
  312.  
  313.     sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %s", timebuff, junk);
  314.  
  315.     if (timebuff[1] != ':' && timebuff[2] != ':') 
  316.       return(FALSE);
  317.     if (junk[0] != '\0') {    /* try for 8 field entry */
  318.       junk[0] = '\0';
  319.       sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %*s %s", timebuff, junk);
  320.       if (junk[0] != '\0')
  321.         return(FALSE);
  322.       eight_fields++;
  323.     }
  324.  
  325.     /** now get the info out of the record! **/
  326.  
  327.     if (eight_fields) 
  328.       sscanf(buffer, "%s %s %s %s %s %s %*s %s",
  329.                 junk, entry->from, entry->dayname, entry->month, 
  330.                     entry->day, entry->time, entry->year);
  331.     else
  332.       sscanf(buffer, "%s %s %s %s %s %s %s",
  333.                 junk, entry->from, entry->dayname, entry->month, 
  334.                     entry->day, entry->time, entry->year);
  335.     
  336.     resolve_received(entry);
  337.     return(entry->year[0] != '\0');
  338. }
  339.  
  340. forwarded(buffer, entry)
  341. char *buffer;
  342. struct header_rec *entry;
  343. {
  344.     /** change 'from' and date fields to reflect the ORIGINATOR of 
  345.         the message by iteratively parsing the >From fields... 
  346.         Modified to deal with headers that include the time zone
  347.         of the originating machine... **/
  348.  
  349.     char machine[SLEN], buff[SLEN];
  350.  
  351.     dprint1("forwarded(buffer='%s', <header_rec>)\n", buffer);
  352.  
  353.     machine[0] = '\0';
  354.  
  355.     sscanf(buffer, "%*s %s %s %s %s %s %s %*s %*s %s",
  356.                 entry->from, entry->dayname, entry->month, 
  357.                     entry->day, entry->time, entry->year, machine);
  358.  
  359.     if (isdigit(entry->month[0])) { /* try for veeger address */
  360.       sscanf(buffer, "%*s %s %s%*c %s %s %s %s %*s %*s %s",
  361.                 entry->from, entry->dayname, entry->day, entry->month, 
  362.                     entry->year, entry->time, machine);
  363.     }
  364.     if (isalpha(entry->year[0])) { /* try for address including tz */
  365.       sscanf(buffer, "%*s %s %s %s %s %s %*s %s %*s %*s %s",
  366.                 entry->from, entry->dayname, entry->month, 
  367.                     entry->day, entry->time, entry->year, machine);
  368.     }
  369.     
  370.  
  371.     if (machine[0] == '\0')
  372.       sprintf(buff,"anonymous");
  373.     else
  374.       sprintf(buff,"%s!%s", machine, entry->from);
  375.  
  376.     strncpy(entry->from, buff, SLEN);
  377. }
  378.  
  379. parse_arpa_from(buffer, newfrom)
  380. char *buffer, *newfrom;
  381. {
  382.     /** try to parse the 'From:' line given... It can be in one of
  383.         two formats:
  384.         From: Dave Taylor <hpcnou!dat>
  385.         or  From: hpcnou!dat (Dave Taylor)
  386.         Change 'newfrom' ONLY if sucessfully parsed this entry and
  387.         the resulting name is non-null! 
  388.         Added: removes quotes if name is quoted (12/12)
  389.     **/
  390.  
  391.     char temp_buffer[SLEN], *temp;
  392.     register int i, j = 0;
  393.  
  394.     dprint1("parse_arpa_from(buffer='%s', <old from>)\n", buffer);
  395.  
  396.     temp = (char *) temp_buffer;
  397.     temp[0] = '\0';
  398.  
  399.     no_ret(buffer);        /* blow away '\n' char! */
  400.  
  401.     if (lastch(buffer) == '>') {
  402.       for (i=strlen("From: "); buffer[i] != '\0' && buffer[i] != '<' &&
  403.            buffer[i] != '('; i++)
  404.         temp[j++] = buffer[i];
  405.       temp[j] = '\0';
  406.     }
  407.     else if (lastch(buffer) == ')') {
  408.       for (i=strlen(buffer)-2; buffer[i] != '\0' && buffer[i] != '(' &&
  409.            buffer[i] != '<'; i--)
  410.         temp[j++] = buffer[i];
  411.       temp[j] = '\0';
  412.       reverse(temp);
  413.     }
  414.       
  415.     if (strlen(temp) > 0) {        /* mess with buffer... */
  416.  
  417.       /* remove leading spaces and quotes... */
  418.  
  419.       while (whitespace(temp[0]) || quote(temp[0]))
  420.         temp = (char *) (temp + 1);        /* increment address! */
  421.  
  422.       /* remove trailing spaces and quotes... */
  423.  
  424.       i = strlen(temp) - 1;
  425.  
  426.       while (whitespace(temp[i]) || quote(temp[i]))
  427.        temp[i--] = '\0';
  428.  
  429.       /* if anything is left, let's change 'from' value! */
  430.  
  431.       if (strlen(temp) > 0)
  432.         strcpy(newfrom, temp);
  433.     }
  434. }
  435.  
  436. parse_arpa_date(string, entry)
  437. char *string;
  438. struct header_rec *entry;
  439. {
  440.     /** Parse and figure out the given date format... return
  441.         the entry fields changed iff it turns out we have a
  442.         valid parse of the date!  **/
  443.  
  444.     char word[15][NLEN], buffer[SLEN], *bufptr;
  445.     char *aword, *strtok();
  446.     int  words = 0;
  447.  
  448.     dprint1("parse_arpa_date(string='%s', <header_rec>)\n", string);
  449.  
  450.     strcpy(buffer, string);
  451.     bufptr = (char *) buffer;
  452.  
  453.     /** break the line down into words... **/
  454.  
  455.     while ((aword = strtok(bufptr," \t '\"-/(),.")) != NULL) {
  456.       strcpy(word[words++], aword);
  457.       bufptr = NULL;
  458.     }
  459.  
  460.     if (words < 6)        /* strange format.  We're outta here! */
  461.       return;
  462.  
  463.     /* There are now five possible combinations that we could have:
  464.      
  465.         Date: day_number month_name year_number time timezone
  466.         Date: day_name day_number month_name year_number ...
  467.         Date: day_name month_name day_number time year_number
  468.         Date: day_name month_name day_number year_number time
  469.         Date: day_number month_name year_number time timezone day_name
  470.  
  471.        Note that they are distinguishable by checking the first
  472.        character of the second, third and fourth words... 
  473.     */
  474.  
  475.     if (isdigit(word[1][0])) {            /*** type one! ***/
  476.       if (! valid_date(word[1], word[2], word[3]))
  477.         return;        /* strange date! */
  478.       strncpy(entry->day, word[1], 3);
  479.       strncpy(entry->month, word[2], 3);
  480.       strncpy(entry->year,  word[3], 4);
  481.       strncpy(entry->time,  word[4], 10);
  482.     }
  483.     else if (isdigit(word[2][0])) {                /*** type two! ***/
  484.       if (! valid_date(word[2], word[3], word[4]))
  485.         return;        /* strange date! */
  486.       strncpy(entry->day, word[2], 3);
  487.       strncpy(entry->month, word[3], 3);
  488.       strncpy(entry->year,  word[4], 4);
  489.       strncpy(entry->time,  word[5], 10);
  490.     }
  491.     else if (isdigit(word[3][0])) {        
  492.       if (word[4][1] == ':' || 
  493.               word[4][2] == ':') {                   /*** type three! ***/
  494.         if (! valid_date(word[3], word[2], word[5]))
  495.           return;
  496.         strncpy(entry->year,  word[5], 4);
  497.         strncpy(entry->time,  word[4], 10);
  498.       }
  499.       else {                       /*** type four!  ***/ 
  500.         if (! valid_date(word[3], word[2], word[4]))
  501.           return;
  502.         strncpy(entry->year,  word[4], 4);
  503.         strncpy(entry->time, word[5], 10);
  504.       }
  505.       strncpy(entry->day, word[3], 3);
  506.       strncpy(entry->month, word[2], 3);
  507.     }
  508. }
  509.  
  510. fix_arpa_address(address)
  511. char *address;
  512. {
  513.     /** Given a pure ARPA address, try to make it reasonable.
  514.  
  515.         This means that if you have something of the form a@b@b make 
  516.             it a@b.  If you have something like a%b%c%b@x make it a%b@x...
  517.     **/
  518.  
  519.     register int host_count = 0, i;
  520.     char     hosts[MAX_HOPS][2*NLEN];    /* array of machine names */
  521.     char     *host, *addrptr;
  522.  
  523.     dprint1("fix_arpa_address(%s)\n", address);
  524.  
  525.     /*  break down into a list of machine names, checking as we go along */
  526.     
  527.     addrptr = (char *) address;
  528.  
  529.     while ((host = get_token(addrptr, "%@", 2)) != NULL) {
  530.       for (i = 0; i < host_count && ! equal(hosts[i], host); i++)
  531.           ;
  532.  
  533.       if (i == host_count) {
  534.         strcpy(hosts[host_count++], host);
  535.         if (host_count == MAX_HOPS) {
  536.            error("Can't build return address - hit MAX_HOPS limit!");
  537.            return(1);
  538.         }
  539.       }
  540.       else 
  541.         host_count = i + 1;
  542.       addrptr = NULL;
  543.     }
  544.  
  545.     /** rebuild the address.. **/
  546.  
  547.     address[0] = '\0';
  548.  
  549.     for (i = 0; i < host_count; i++)
  550.       sprintf(address, "%s%s%s", address, 
  551.               address[0] == '\0'? "" : 
  552.              (i == host_count - 1 ? "@" : "%"),
  553.               hosts[i]);
  554.  
  555.     dprint1("\tmodified to address '%s'\n", address);
  556.  
  557.     return(0);
  558. }
  559. END-OF-FILE
  560.  
  561. size=`wc -c < src/addr_utils.c`
  562.  
  563. if [ $size != 13730 ]
  564. then
  565.   echo Warning: src/addr_utils.c changed - should be 13730 bytes, not $size bytes
  566. fi
  567.  
  568. chmod 644 src/addr_utils.c
  569.  
  570. # ---------- file src/alias.c ----------
  571.  
  572.  
  573. if [ -f src/alias.c ]
  574. then
  575.   echo File 'src/alias.c' already exists\!
  576.   exit 1
  577. fi
  578.  
  579. echo extracting file src/alias.c...
  580. cat << 'END-OF-FILE' > src/alias.c
  581. /**            alias.c            **/
  582.  
  583. /** This file contains alias stuff
  584.  
  585.     (C) Copyright 1986 Dave Taylor
  586. **/
  587.  
  588. #include "headers.h"
  589.  
  590. char *expand_group(), *get_alias_address(), *expand_system(), *get_token();
  591.  
  592. read_alias_files()
  593. {
  594.     /** read the system and user alias files, if present.
  595.         Set the flags 'systemfiles' and 'userfiles' accordingly.
  596.     **/
  597.  
  598.     char fname[SLEN];
  599.     int  hash;
  600.  
  601.     dprint0("read_alias_files()\n");
  602.  
  603.     if ((hash = open(system_hash_file, O_RDONLY)) == -1) 
  604.       goto user;
  605.  
  606.     read(hash, system_hash_table, sizeof system_hash_table);
  607.     close(hash);
  608.  
  609.     /* and data file opened.. */
  610.  
  611.     if ((system_data = open(system_data_file, O_RDONLY)) == -1) 
  612.       goto user;
  613.  
  614.     system_files++;    /* got the system files! */
  615.  
  616. user:   sprintf(fname,  "%s/.alias_hash", home); 
  617.  
  618.     if ((hash = open(fname, O_RDONLY)) == -1) 
  619.       return;
  620.  
  621.     read(hash, user_hash_table, sizeof user_hash_table);
  622.     close(hash);
  623.  
  624.     sprintf(fname,  "%s/.alias_data", home); 
  625.  
  626.     if ((user_data = open(fname, O_RDONLY)) == -1) 
  627.       return;
  628.  
  629.     user_files++;    /* got user files too! */
  630. }
  631.  
  632. int
  633. add_alias()
  634. {
  635.     /** add an alias to the user alias text file.  Return zero 
  636.         if alias not added in actuality **/
  637.  
  638.     char name[SLEN], *address, address1[LONG_STRING];
  639.     char comment[SLEN];
  640.  
  641.     dprint0("add_alias()\n");
  642.  
  643.     PutLine(LINES-2,0,"Enter alias name: ");
  644.     CleartoEOLN();
  645.     Raw(OFF);
  646.     gets(name, 20);
  647.     Raw(ON);
  648.     if ((address = (char *) get_alias_address(name, 0, 0)) != NULL) {
  649.       if (address[0] == '!') {
  650.         address[0] = ' ';
  651.         error1("already a group with that name:%s", address);
  652.       }
  653.       else
  654.         error1("already an alias for that: %s", address);
  655.       return(0);
  656.     }
  657.     PutLine(LINES-2,0,"Full name for %s: ", name);
  658.     CleartoEOLN();
  659.     Raw(OFF);
  660.     gets(comment,SLEN);
  661.     Raw(ON);
  662.     PutLine(LINES-2,0,"Enter address for %s: ",name);
  663.     CleartoEOLN();
  664.     Raw(OFF);
  665.     gets(address1,LONG_SLEN);
  666.     Raw(ON);
  667.     add_to_alias_text(name, comment, address1);
  668.     return(1);
  669. }
  670.  
  671. int
  672. add_current_alias()
  673. {
  674.     /** alias the current message to the specified name and
  675.         add it to the alias text file, for processing as
  676.         the user leaves the program.  Returns non-zero iff
  677.         alias actually added to file **/
  678.  
  679.     char name[SLEN], address1[LONG_STRING], *address;
  680.     char comment[SLEN];
  681.  
  682.     dprint0("add_current_alias()\n");
  683.  
  684.     if (current == 0) {
  685.       error("No message to alias to!");
  686.       return(0);
  687.     }
  688.     
  689.     PutLine(LINES-2,0,"Current message address aliased to: ");
  690.     CleartoEOLN();
  691.     Raw(OFF);
  692.     gets(name, 20);
  693.     Raw(ON);
  694.     if ((address = (char *) get_alias_address(name, 0, 0)) != NULL) {
  695.       if (address[1] == '!') {
  696.         address[0] = ' ';
  697.         error1("already a group with that name:%s", address);
  698.       }
  699.       else 
  700.         error1("already an alias for that: %s", address); 
  701.           return(0);
  702.     }
  703.     PutLine(LINES-2,0,"Full name of %s: ", name);
  704.     CleartoEOLN();
  705.     Raw(OFF);
  706.     gets(comment, 40);
  707.     Raw(ON);
  708.     get_return(address1);    /* grab the return address of this message */
  709.     optimize_return(address1);
  710.     PutLine(LINES-2,0,"%s (%s) = %s", comment, name, address1);
  711.     CleartoEOLN();
  712.     add_to_alias_text(name, comment, address1);
  713.     return(1);
  714. }
  715.  
  716. add_to_alias_text(name, comment, address)
  717. char *name, *comment, *address;
  718. {
  719.     /** add the data to the user alias text file... **/
  720.     
  721.     FILE *file;
  722.     char fname[SLEN];
  723.     
  724.     dprint3("add_to_alias_text(name='%s', comment='%s', address='%s')\n", 
  725.          name, comment, address);
  726.  
  727.     sprintf(fname,"%s/.alias_text", home);
  728.     
  729.     if ((file = fopen(fname, "a")) == NULL)
  730.       return(error1("couldn't open %s to add new alias!", fname));
  731.  
  732.     fprintf(file,"%s : %s : %s\n", name, comment, address);
  733.     fclose(file);
  734.  
  735.     chown(fname, userid, getgid());
  736.  
  737.     return(0);
  738. }
  739.  
  740. show_alias_menu()
  741. {
  742.     MoveCursor(LINES-7,0); CleartoEOLN();    
  743.     MoveCursor(LINES-6,0); CleartoEOLN();    
  744.     MoveCursor(LINES-5,0); CleartoEOLN();
  745.     
  746.     PutLine(LINES-7,COLUMNS-45, "Alias commands");
  747.     Centerline(LINES-5,
  748. "A)lias current msg, Check a P)erson or S)ystem, M)ake new alias, or R)eturn"
  749.     );
  750. }
  751.  
  752. alias()
  753. {
  754.     /** work with alias commands... **/
  755.     char name[NLEN], *address, ch, buffer[SLEN];
  756.     int  newaliases = 0;
  757.  
  758.     dprint0("alias()\n");
  759.  
  760.     if (mini_menu)
  761.       show_alias_menu();
  762.  
  763.     define_softkeys(ALIAS);
  764.  
  765.     while (1) {
  766.       PutLine(LINES-3,0,"Alias: ");
  767.       CleartoEOLN();
  768.       ch = ReadCh();
  769.       MoveCursor(LINES-1,0); CleartoEOLN();
  770.       
  771.       switch (tolower(ch)) {
  772.         case 'a': newaliases += add_current_alias();    break;
  773.         case 'm': newaliases += add_alias();         break;
  774.  
  775.         case RETURN:
  776.         case 'q':
  777.         case 'x':
  778.         case 'r': if (newaliases) install_aliases();
  779.               return;
  780.         case 'p': if (newaliases)
  781.             error("Warning: new aliases not installed yet!");
  782.               PutLine(LINES-2,0,"Check for person: ");
  783.               CleartoEOLN();
  784.               Raw(OFF);
  785.                   gets(name,NLEN);
  786.               Raw(ON);
  787.               if ((address = get_alias_address(name, 0, 0))!=NULL) {
  788.                     if (address[0] == '!') {
  789.                       address[0] = ' ';
  790.                       PutLine(LINES-1,0,"Group alias:%-65.65s", address);
  791.                       CleartoEOLN();
  792.                 }
  793.             else
  794.               PutLine(LINES-1,0,"Aliased addresss: %-65.65s", 
  795.               address);
  796.               }
  797.                   else
  798.             error("not found");
  799.               break;
  800.  
  801.         case 's': PutLine(LINES-2,0,"Check for system: ");
  802.               CleartoEOS();
  803.               Raw(OFF);
  804.                   gets(name,NLEN);
  805.               Raw(ON);
  806.               sprintf(buffer, "(user)@%s", name);
  807.                address = (char *) expand_system(buffer, 0, FALSE);
  808.               if (strlen(address) > strlen(name) + 7)
  809.                 PutLine(LINES-1,0,"Address is: %.65s", address);
  810.               else
  811.                 error1("couldn't expand system '%s'", name);
  812.               break;
  813.  
  814.         case '@': PutLine(LINES-2,0,"Fully expand alias: ");
  815.               CleartoEOS();
  816.               Raw(OFF);
  817.                   gets(name,NLEN);
  818.               Raw(ON);
  819.               if ((address = get_alias_address(name, 1, 0)) != NULL) {
  820.                     ClearScreen();
  821.             PutLine(3,0,"Aliased address:\n\r%s", address);
  822.                     PutLine(LINES-1,0,"Press <return> to continue: ");
  823.             (void) getchar();
  824.               }
  825.                   else
  826.             error("not found");
  827.               break;
  828.         default : error("Invalid input!");
  829.       }
  830.     }
  831. }
  832.  
  833. install_aliases()
  834. {
  835.     /** run the 'newalias' program and install the newly
  836.         added aliases before going back to the main
  837.         program! 
  838.     **/
  839.  
  840.     dprint0("install_aliases()\n");
  841.  
  842.     error("Adding new aliases...");
  843.     if (system_call(newalias, SH) == 0) {
  844.       error("Re-reading the database in...");
  845.       read_alias_files();
  846.       set_error("New aliases installed successfully");
  847.     }
  848.     else
  849.       set_error("'newalias' failed.  Please check alias_text");
  850. }
  851. END-OF-FILE
  852.  
  853. size=`wc -c < src/alias.c`
  854.  
  855. if [ $size != 6455 ]
  856. then
  857.   echo Warning: src/alias.c changed - should be 6455 bytes, not $size bytes
  858. fi
  859.  
  860. chmod 644 src/alias.c
  861.  
  862. # ---------- file src/aliasdb.c ----------
  863.  
  864.  
  865. if [ -f src/aliasdb.c ]
  866. then
  867.   echo File 'src/aliasdb.c' already exists\!
  868.   exit 1
  869. fi
  870.  
  871. echo extracting file src/aliasdb.c...
  872. cat << 'END-OF-FILE' > src/aliasdb.c
  873. /**            aliasdb.c            **/
  874.  
  875. /** Alias database files...
  876.  
  877.     (C) Copyright 1986 Dave Taylor
  878. **/
  879.  
  880.  
  881. #include "headers.h"
  882.  
  883. #include <sys/types.h>
  884. #include <sys/stat.h>
  885.  
  886. char *shift_lower();
  887.  
  888. findnode(name, display_error)
  889. char *name;
  890. int   display_error;
  891. {
  892.     /** break 'name' into machine!user or user@machine and then
  893.         see if you can find 'machine' in the path database..
  894.         If so, return name as the expanded address.  If not,
  895.         return what was given to us!   If display_error, then
  896.         do so...
  897.     **/
  898.  
  899.     char   address[SLEN];
  900.     
  901.     dprint2("findnode(name='%s', display_error=%s)\n", 
  902.          name, display_error? "ON" : "OFF");
  903.  
  904.     if (strlen(name) == 0)
  905.       return;
  906.     
  907.     if (expand_site(name, address) == -1) {
  908.       if (display_error && name[0] != '!') {
  909.         error1("Warning: couldn't expand %s...", name);
  910.         sleep(1);
  911.       }
  912.     }
  913.     else
  914.       strcpy(name, address);
  915.     
  916.     return;
  917. }
  918.  
  919. int
  920. expand_site(cryptic, expanded)
  921. char *cryptic, *expanded;
  922. {
  923.  
  924.     /** Given an address of the form 'xyz@site' or 'site!xyz'
  925.         return an address of the form <expanded address for site>
  926.             with 'xyz' embedded according to the path database entry.
  927.         Note that 'xyz' can be eiher a simple address (as in "joe")
  928.         or a complex address (as in "joe%xerox.parc@Xerox.ARPA")!
  929.         0 = found, -1 return means unknown site code **/
  930.  
  931.     char   name[VERY_LONG_STRING], sitename[VERY_LONG_STRING], 
  932.                address[VERY_LONG_STRING], temp[VERY_LONG_STRING];
  933.     register int i = 0, j = 0, domain_name;
  934.  
  935.     dprint1("expand_site(cryptic='%s', <expanded>)\n", cryptic);
  936.  
  937.     /** break down **/
  938.  
  939.     while (cryptic[i] != '@' && cryptic[i] != '!' && cryptic[i] != '\0')
  940.       sitename[j++] = cryptic[i++];
  941.  
  942.     sitename[j++] = '\0';
  943.  
  944.     j = 0;
  945.     
  946.     if (cryptic[i] == '\0') return(-1);    /* nothing to expand! */
  947.  
  948.     domain_name = (cryptic[i] == '@');
  949.  
  950.     i++;
  951.  
  952.     while (cryptic[i] != '\0')
  953.       name[j++] = cryptic[i++];
  954.  
  955.     name[j] = '\0';
  956.  
  957.     if (domain_name) {
  958.       strcpy(temp, name);
  959.       strcpy(name, sitename);
  960.       strcpy(sitename, temp);
  961.     }
  962.  
  963.     if (talk_to(sitename)) {
  964.       sprintf(expanded,"%s!%s", sitename, name);
  965.       return(0);
  966.     }
  967.  
  968.     if (size_of_pathfd > 0) {
  969.           if (binary_search(sitename, address) == -1)
  970.         return(-1);
  971.     }
  972.     else {
  973.       sprintf(expanded,"%s!%s", sitename, name);
  974.       return(0);
  975.     }
  976.  
  977.     /* otherwise... */
  978.  
  979.     sprintf(expanded, address, name);
  980.     return(0);
  981. }
  982.  
  983. int
  984. binary_search(name, address)
  985. char *name, *address;
  986. {
  987.     /* binary search file for name.  Return 0 if found, -1 if not */
  988.  
  989.     char machine[20];
  990.     register long first = 0, last, middle;
  991.     register int  compare;
  992.  
  993.     dprint1("binary_search(name='%s', <address>)\n", name);
  994.  
  995.     address[0] = '\0';
  996.  
  997.     last = size_of_pathfd;
  998.  
  999.     do {
  1000.  
  1001.       middle = (long) ((first+last) / 2);
  1002.  
  1003.       get_entry(machine, address, pathfd, middle);
  1004.  
  1005.       compare = strcmp(name, machine);
  1006.  
  1007.       if (compare < 0) 
  1008.         last = middle - 1;
  1009.       else if (compare == 0) 
  1010.         return(0);
  1011.       else  /* greater */
  1012.         first = middle + 1; 
  1013.     } while (abs(last) - abs(first) > FIND_DELTA);
  1014.  
  1015.     return(-1);
  1016. }
  1017.  
  1018. get_entry(machine, address, fileid, offset)
  1019. char *machine, *address;
  1020. FILE *fileid;
  1021. long offset;
  1022. {
  1023.     /** get entry...return machine and address immediately
  1024.         following given offset in fileid.  **/
  1025.  
  1026.     fseek(fileid, offset, 0L);
  1027.  
  1028.     /* read until we hit an end-of-line */
  1029.  
  1030.     while (getc(fileid) != '\n')
  1031.        ;
  1032.  
  1033.     fscanf(fileid, "%s\t%s", machine, address);
  1034. }
  1035.  
  1036. init_findnode()
  1037. {
  1038.     /** Initialize the FILE and 'size_of_file' values for the 
  1039.         findnode procedure **/
  1040.  
  1041.     struct stat buffer;
  1042.  
  1043.     dprint0("init_findnode()\n");
  1044.  
  1045.     if (stat(pathfile, &buffer) == -1) {
  1046.       dprint1("\tWarning: No pathalias file '%s' found!\n", pathfile);
  1047.       size_of_pathfd = 0;
  1048.       return;
  1049.     }
  1050.  
  1051.     size_of_pathfd = buffer.st_size;
  1052.  
  1053.     if ((pathfd = fopen(pathfile,"r")) == NULL) {
  1054.       dprint1("\tFound pathalias file '%s' but couldn't open to read\n",
  1055.           pathfile);
  1056.       size_of_pathfd = 0;
  1057.     }
  1058.     else
  1059.       dprint1("\tOpened file '%s' as path alias database\n", pathfile);
  1060. }
  1061. END-OF-FILE
  1062.  
  1063. size=`wc -c < src/aliasdb.c`
  1064.  
  1065. if [ $size != 3888 ]
  1066. then
  1067.   echo Warning: src/aliasdb.c changed - should be 3888 bytes, not $size bytes
  1068. fi
  1069.  
  1070. chmod 644 src/aliasdb.c
  1071.  
  1072. # ---------- file src/aliaslib.c ----------
  1073.  
  1074.  
  1075. if [ -f src/aliaslib.c ]
  1076. then
  1077.   echo File 'src/aliaslib.c' already exists\!
  1078.   exit 1
  1079. fi
  1080.  
  1081. echo extracting file src/aliaslib.c...
  1082. cat << 'END-OF-FILE' > src/aliaslib.c
  1083. /**            aliaslib.c            **/
  1084.  
  1085. /** Library of functions dealing with the alias system...
  1086.  
  1087.     (C) Copyright 1986 Dave Taylor
  1088.  **/
  1089.  
  1090. #include "headers.h"
  1091.  
  1092. char *expand_group(), *get_alias_address(), *expand_system();
  1093. char *get_token();
  1094.  
  1095. char *get_alias_address(name, mailing, depth)
  1096. char *name;
  1097. int   mailing, depth;
  1098. {
  1099.     /** return the line from either datafile that corresponds 
  1100.         to the specified name.  If 'mailing' specified, then
  1101.         fully expand group names.  Depth specifies the nesting
  1102.         depth - the routine should always initially be called
  1103.         with this equal 0.  Returns NULL if not found   **/
  1104.  
  1105.     static char buffer[VERY_LONG_STRING];
  1106.     int    loc;
  1107.  
  1108.     dprint3("get_alias_address(name='%s', mailing=%s, depth=%d)\n", 
  1109.          name, mailing? "ON" : "OFF", depth);
  1110.  
  1111.     if (strlen(name) == 0)
  1112.       return( (char *) NULL);
  1113.  
  1114.     if (user_files) 
  1115.       if ((loc = find(name, user_hash_table, MAX_UALIASES)) >= 0) {
  1116.         lseek(user_data, user_hash_table[loc].byte, 0L);
  1117.         get_line(user_data, buffer);
  1118.         if (buffer[0] == '!' && mailing)
  1119.           return( (char *) expand_group(buffer, depth));
  1120.         else
  1121.           return( (char *) expand_system(buffer, depth, TRUE));
  1122.       }
  1123.      
  1124.     if (system_files) 
  1125.       if ((loc = find(name, system_hash_table, MAX_SALIASES)) >= 0) {
  1126.         lseek(system_data, system_hash_table[loc].byte, 0L);
  1127.         get_line(system_data, buffer);
  1128.         if (buffer[0] == '!' && mailing)
  1129.           return( (char *) expand_group(buffer, depth));
  1130.         else
  1131.           return( (char *) expand_system(buffer, depth, TRUE));
  1132.       }
  1133.     
  1134.     return( (char *) NULL);
  1135. }
  1136.  
  1137. char *expand_system(buffer, depth, show_errors)
  1138. char *buffer;
  1139. int   depth, show_errors;
  1140. {
  1141.     /** This routine will check the first machine name in the 
  1142.         given path (if any) and expand it out if it is an 
  1143.         alias...if not, it will return what it was given.
  1144.         If show_errors is false, it won't display errors 
  1145.         encountered...
  1146.     **/
  1147.  
  1148.     dprint2("expand_system(buffer='%s', show_errors=%s)\n", 
  1149.              buffer, show_errors? "ON" : "OFF");
  1150.  
  1151.     findnode(buffer, show_errors);
  1152.  
  1153.     return( (char *) buffer);
  1154. }
  1155.           
  1156. char *expand_group(members, depth)
  1157. char *members;
  1158. int  depth;
  1159. {
  1160.     /** Given a group of names separated by commas, this routine
  1161.         will return a string that is the full addresses of each
  1162.         member separated by spaces. Depth is an internal counter
  1163.         that keeps track of the depth of nesting that the routine
  1164.         is in...it's for the get_token routine!  **/
  1165.  
  1166.     static char buffer[VERY_LONG_STRING];
  1167.     char   buf[LONG_STRING], *word, *address, *bufptr;
  1168.  
  1169.     dprint2("expand_group(members='%s', depth=%d)\n", members, depth);
  1170.  
  1171.     strcpy(buf, members);     /* parameter safety! */
  1172.     buffer[0] = '\0';    /* nothing in yet!   */
  1173.     bufptr = (char *) buf;    /* grab the address  */
  1174.     depth++;        /* one deeper!       */
  1175.  
  1176.     while ((word = get_token(bufptr, "!, ", depth)) != NULL) {
  1177.       if ((address = get_alias_address(word, 1, depth)) == NULL) {
  1178.         if (! valid_name(word)) {
  1179.           error1("%s is an illegal address!", word);
  1180.           return( (char *) NULL);
  1181.         }
  1182.         else if (strcmp(buffer, word) != 0)
  1183.           sprintf(buffer, "%s%s%s", buffer,
  1184.             (strlen(buffer) > 0)? ", ":"", word);
  1185.       }
  1186.       else if (strcmp(buffer, address) != 0)
  1187.         sprintf(buffer,"%s%s%s", buffer, 
  1188.             (strlen(buffer) > 0)? ", ":"", address);
  1189.  
  1190.       bufptr = NULL;
  1191.     }
  1192.  
  1193.     return( (char *) buffer);
  1194. }
  1195.  
  1196. int
  1197. find(word, table, size)
  1198. char *word;
  1199. struct alias_rec table[];
  1200. int size;
  1201. {
  1202.     /** find word and return loc, or -1 **/
  1203.     register int loc;
  1204.     
  1205.     if (strlen(word) > 20)
  1206.       exit(printf("Bad alias name: %s.  Too long.\n", word));
  1207.  
  1208.     loc = hash_it(word, size);
  1209.  
  1210.     while (strcmp(word, table[loc].name) != 0) {
  1211.       if (table[loc].name[0] == '\0') 
  1212.         return(-1);
  1213.       loc = (loc + 1) % size; 
  1214.     }
  1215.  
  1216.     return(loc);
  1217. }
  1218.  
  1219. int
  1220. hash_it(string, table_size)
  1221. char *string;
  1222. int   table_size;
  1223. {
  1224.     /** compute the hash function of the string, returning
  1225.         it (mod table_size) **/
  1226.  
  1227.     register int i, sum = 0;
  1228.     
  1229.     for (i=0; string[i] != '\0'; i++)
  1230.       sum += (int) string[i];
  1231.  
  1232.     return(sum % table_size);
  1233. }
  1234.  
  1235. get_line(fd, buffer)
  1236. int fd;
  1237. char *buffer;
  1238. {
  1239.     /* read from file fd.  End read upon reading either 
  1240.        EOF or '\n' character (this is where it differs 
  1241.        from a straight 'read' command!) */
  1242.  
  1243.     register int i= 0;
  1244.     char     ch;
  1245.  
  1246.     while (read(fd, &ch, 1) > 0)
  1247.       if (ch == '\n' || ch == '\r') {
  1248.         buffer[i] = 0;
  1249.         return;
  1250.       }
  1251.       else
  1252.         buffer[i++] = ch;
  1253. }
  1254. END-OF-FILE
  1255.  
  1256. size=`wc -c < src/aliaslib.c`
  1257.  
  1258. if [ $size != 4347 ]
  1259. then
  1260.   echo Warning: src/aliaslib.c changed - should be 4347 bytes, not $size bytes
  1261. fi
  1262.  
  1263. chmod 644 src/aliaslib.c
  1264.  
  1265. # ---------- file src/args.c ----------
  1266.  
  1267.  
  1268. if [ -f src/args.c ]
  1269. then
  1270.   echo File 'src/args.c' already exists\!
  1271.   exit 1
  1272. fi
  1273.  
  1274. echo extracting file src/args.c...
  1275. cat << 'END-OF-FILE' > src/args.c
  1276.  
  1277. /**            args.c            **/
  1278.  
  1279. /** starting argument parsing routines for MSG system...
  1280.  
  1281.     (C) Copyright 1986 Dave Taylor
  1282. **/
  1283.  
  1284. #include "headers.h"
  1285.  
  1286. #define DONE        0
  1287. #define ERROR        -1
  1288.  
  1289. char *optional_arg;            /* optional argument as we go */
  1290. int   opt_index;            /* argnum + 1 when we leave   */
  1291.  
  1292. parse_arguments(argc, argv, to_whom)
  1293. int argc;
  1294. char *argv[], *to_whom;
  1295. {
  1296.     /** set flags according to what was given to program.  If we are 
  1297.         fed a name or series of names, put them into the 'to_whom' buffer
  1298.         and set "mail_only" to TRUE **/
  1299.  
  1300.     register char c = 0;
  1301.  
  1302.     infile[0] = '\0';
  1303.     to_whom[0] = '\0';
  1304.     batch_subject[0] = '\0';
  1305.  
  1306.     while ((c = get_options(argc, argv, "?cdf:hkmsS:z")) > 0) {
  1307.        switch (c) {
  1308.          case 'c' : check_only++;        break;
  1309.          case 'd' : debug++;        break;
  1310.          case 'f' : strcpy(infile, optional_arg); 
  1311.                     mbox_specified = 2;  break;
  1312.          case '?' :
  1313.          case 'h' : args_help();
  1314.          case 'k' : hp_terminal++;    break;
  1315.          case 'm' : mini_menu = 0;    break;
  1316.          case 's' : hp_softkeys++;    break;
  1317.          case 'S' : strcpy(batch_subject, optional_arg);    break;
  1318.          case 'z' : check_mailfile_size(); break;
  1319.         }
  1320.      }
  1321.  
  1322.      if (c == ERROR) {
  1323.        printf("Usage: msg [cdhkmsz] [-f file] [-S subject] <names>\n\n");
  1324.        args_help();
  1325.     }
  1326.  
  1327.      if (opt_index < argc) 
  1328.        while (opt_index < argc) {
  1329.          sprintf(to_whom, "%s%s%s", to_whom, 
  1330.             to_whom[0] != '\0'? " " : "", argv[opt_index]);
  1331.          mail_only++;
  1332.          opt_index++;
  1333.        }
  1334.  
  1335.      if (strlen(batch_subject) > 0 && ! mail_only) 
  1336.        exit(printf(
  1337.      "\n\rDon't understand specifying a subject and no-one to send to!\n\r"));
  1338.  
  1339.     if (! isatty(fileno(stdin)) && strlen(batch_subject) == 0)
  1340.       strcpy(batch_subject, DEFAULT_BATCH_SUBJECT);
  1341.         
  1342. }
  1343.  
  1344. args_help()
  1345. {
  1346.     /**  print out possible starting arguments... **/
  1347.  
  1348.     printf("\nPossible Starting Arguments for MSG program:\n\n");
  1349.     printf("\targ\t\t\tMeaning\n");
  1350.     printf("\t -c \t\tCheckalias - check the given aliases only\n");
  1351.     printf("\t -d \t\tDebug - turn on debug mode (output to file)\n");
  1352.     printf("\t -f \t\tFile - read file (specified) rather than mailbox\n");
  1353.     printf("\t -h \t\tHelp - give this list of options\n");
  1354.     printf("\t -k \t\tKeypad - force knowledge of HP terminal keyboard\n");
  1355.     printf("\t -m \t\tMenu - Turn off menu, using more of the screen\n");
  1356.     printf("\t -s \t\tSoftkeys - enable use of softkeys\n");
  1357.     printf("\t -z \t\tZero - don't enter MSG if no mail is pending\n");
  1358.     printf("\n");
  1359.     printf("\t -S \t\tSubject - for batchmailing, combined with address\n");
  1360.     printf("\n");
  1361.     exit(1);
  1362. }
  1363.  
  1364. int  _indx = 1, _argnum = 1;
  1365.  
  1366. int
  1367. get_options(argc, argv, options)
  1368. int argc;
  1369. char *argv[], *options;
  1370. {
  1371.     /** Returns the character argument next, and optionally instantiates 
  1372.         "argument" to the argument associated with the particular option 
  1373.     **/
  1374.     
  1375.     char       *word, *strchr();
  1376.  
  1377.     if (_indx >= strlen(argv[_argnum])) {
  1378.       _argnum++;
  1379.       _indx = 1;        /* zeroeth char is '-' */
  1380.     }
  1381.  
  1382.     if (_argnum >= argc) {
  1383.       opt_index = argc;
  1384.       return(DONE);
  1385.     }
  1386.     
  1387.     if (argv[_argnum][0] != '-') {
  1388.       opt_index = _argnum;
  1389.       return(DONE);
  1390.     }
  1391.  
  1392.         word = strchr(options, argv[_argnum][_indx++]);
  1393.  
  1394.     if (strlen(word) == 0) 
  1395.       return(ERROR);
  1396.     
  1397.     if (word[1] == ':') {
  1398.  
  1399.       /** Two possibilities - either tailing end of this argument or the 
  1400.           next argument in the list **/
  1401.  
  1402.       if (_indx < strlen(argv[_argnum])) { /* first possibility */
  1403.         optional_arg = (char *) (argv[_argnum] + _indx);
  1404.         _argnum++;
  1405.         _indx = 1;
  1406.       }
  1407.       else {                /* second choice     */
  1408.         if (++_argnum >= argc) 
  1409.           return(ERROR);            /* no argument!!     */
  1410.  
  1411.         optional_arg = (char *) argv[_argnum++];
  1412.         _indx = 1;
  1413.       }
  1414.     }
  1415.  
  1416.     return((int) word[0]);
  1417. }
  1418. END-OF-FILE
  1419.  
  1420. size=`wc -c < src/args.c`
  1421.  
  1422. if [ $size != 3662 ]
  1423. then
  1424.   echo Warning: src/args.c changed - should be 3662 bytes, not $size bytes
  1425. fi
  1426.  
  1427. chmod 644 src/args.c
  1428.  
  1429. # ---------- file src/curses.c ----------
  1430.  
  1431.  
  1432. if [ -f src/curses.c ]
  1433. then
  1434.   echo File 'src/curses.c' already exists\!
  1435.   exit 1
  1436. fi
  1437.  
  1438. echo extracting file src/curses.c...
  1439. cat << 'END-OF-FILE' > src/curses.c
  1440. /**             curses.c        **/
  1441.  
  1442. /**  This library gives programs the ability to easily access the
  1443.      termcap information and write screen oriented and raw input
  1444.      programs.  The routines can be called as needed, except that
  1445.      to use the cursor / screen routines there must be a call to
  1446.      InitScreen() first.  The 'Raw' input routine can be used
  1447.      independently, however.
  1448.  
  1449.      Modified 2/86 to work (hopefully) on Berkeley systems.  If
  1450.      there are any problems with BSD Unix, please report them to
  1451.      the author at hpcnoe!dat@HPLABS (fixed, if possible!)
  1452.  
  1453.      (C) Copyright 1985 Dave Taylor, HP Colorado Networks
  1454. **/
  1455.  
  1456. #include <stdio.h>
  1457.  
  1458. #ifdef RAWMODE
  1459. # ifdef BSD
  1460. #  include <sgtty.h>
  1461. # else
  1462. #  include <termio.h>
  1463. # endif
  1464. #endif
  1465.  
  1466. #include <ctype.h>
  1467.  
  1468. #ifdef BSD
  1469. #undef tolower
  1470. #endif
  1471. #include "curses.h"
  1472.  
  1473. #ifdef BSD
  1474. # include "/usr/include/curses.h"      /* don't ask! */
  1475. #endif
  1476.  
  1477. #ifdef RAWMODE
  1478. # define TTYIN    0
  1479. #endif
  1480.  
  1481. extern int debug;
  1482.  
  1483. #ifdef RAWMODE
  1484. #  ifndef BSD
  1485.     struct termio _raw_tty, 
  1486.                 _original_tty;
  1487. #  endif
  1488.  
  1489. static int _inraw = 0;                  /* are we IN rawmode?    */
  1490.  
  1491. #endif
  1492.  
  1493. static int _intransmit;            /* are we transmitting keys? */
  1494.  
  1495. static
  1496. char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
  1497.      *_setbold, *_clearbold, *_setunderline, *_clearunderline, 
  1498.      *_sethalfbright, *_clearhalfbright, *_setinverse, *_clearinverse,
  1499.      *_cleartoeoln, *_cleartoeos, *_transmit_on, *_transmit_off;
  1500. static
  1501. int
  1502.      _lines, _columns;
  1503.  
  1504. static char _terminal[1024];              /* Storage for terminal entry */
  1505. static char _capabilities[256];           /* String for cursor motion */
  1506.  
  1507. static char *ptr = _capabilities;    /* for buffering         */
  1508.  
  1509. int    outchar();            /* char output for tputs */
  1510.  
  1511. InitScreen()
  1512. {
  1513.    /* Set up all this fun stuff: returns zero if all okay, or;
  1514.         -1 indicating no terminal name associated with this shell,
  1515.         -2..-n  No termcap for this terminal type known
  1516.    */
  1517.  
  1518.    int  tgetent(),      /* get termcap entry */
  1519.         error;
  1520.    char *tgetstr(),     /* Get termcap capability */
  1521.         termname[40];
  1522.  
  1523.    if (strcpy(termname, getenv("TERM")) == NULL)
  1524.      return(-1);
  1525.  
  1526.    if ((error = tgetent(_terminal, termname)) != 1)
  1527.      return(error-2);
  1528.  
  1529.    /* load in all those pesky values */
  1530.    _clearscreen       = tgetstr("cl", &ptr);
  1531.    _moveto            = tgetstr("cm", &ptr);
  1532.    _up                = tgetstr("up", &ptr);
  1533.    _down              = tgetstr("do", &ptr);
  1534.    _right             = tgetstr("nd", &ptr);
  1535.    _left              = tgetstr("bs", &ptr); 
  1536.    _setbold           = tgetstr("so", &ptr);
  1537.    _clearbold         = tgetstr("se", &ptr);
  1538.    _setunderline      = tgetstr("us", &ptr);
  1539.    _clearunderline    = tgetstr("ue", &ptr);
  1540.    _setinverse        = tgetstr("so", &ptr);
  1541.    _clearinverse      = tgetstr("se", &ptr);
  1542.    _sethalfbright     = tgetstr("hs", &ptr);
  1543.    _clearhalfbright   = tgetstr("he", &ptr);
  1544.    _cleartoeoln       = tgetstr("ce", &ptr);
  1545.    _cleartoeos        = tgetstr("cd", &ptr);
  1546.    _lines          = tgetnum("li");
  1547.    _columns          = tgetnum("co");
  1548.    _transmit_on          = tgetstr("ks", &ptr);
  1549.    _transmit_off      = tgetstr("ke", &ptr);
  1550.  
  1551.  
  1552.    if (!_left) {
  1553.       _left = ptr;
  1554.       *ptr++ = '\b';
  1555.       *ptr++ = '\0';
  1556.    }
  1557.  
  1558. #ifdef BSD
  1559.     initscr();    /* initalize curses too! */
  1560. #endif
  1561.  
  1562.    return(0);
  1563. }
  1564.  
  1565. char *return_value_of(termcap_label)
  1566. char *termcap_label;
  1567. {
  1568.     /** this will return the string kept by termcap for the 
  1569.         specified capability **/
  1570.  
  1571.        char *tgetstr();             /* Get termcap capability */
  1572.  
  1573.     return( (char *) tgetstr(termcap_label, &ptr));
  1574. }
  1575.  
  1576. transmit_functions(newstate)
  1577. int newstate;
  1578. {
  1579.     /** turn function key transmission to ON | OFF **/
  1580.     
  1581.     if (newstate != _intransmit) {
  1582.       _intransmit = ! _intransmit;
  1583.       if (newstate == ON)
  1584.            tputs(_transmit_on, 1, outchar);
  1585.       else 
  1586.            tputs(_transmit_off, 1, outchar);
  1587.  
  1588.          fflush(stdout);      /* clear the output buffer */
  1589.     }
  1590. }
  1591.  
  1592. /****** now into the 'meat' of the routines...the cursor stuff ******/
  1593.  
  1594. ScreenSize(lines, columns)
  1595. int *lines, *columns;
  1596. {
  1597.     /** returns the number of lines and columns on the display. **/
  1598.  
  1599.     *lines = _lines - 1;        /* assume index from zero */
  1600.     *columns = _columns;
  1601. }
  1602.  
  1603. ClearScreen()
  1604. {
  1605.         /* clear the screen: returns -1 if not capable */
  1606.  
  1607.    if (!_clearscreen) 
  1608.      return(-1);
  1609.  
  1610.    tputs(_clearscreen, 1, outchar);
  1611.    fflush(stdout);      /* clear the output buffer */
  1612.    return(0);
  1613. }
  1614.  
  1615. MoveCursor(row, col)
  1616. int row, col;
  1617. {
  1618.         /** move cursor to the specified row column on the screen.
  1619.             0,0 is the top left! **/
  1620.  
  1621.            char *tgoto();
  1622.        char *stuff;
  1623.  
  1624.            if (!_moveto) 
  1625.              return(-1);
  1626.  
  1627.            stuff = (char *) tgoto(_moveto, col, row);
  1628.        tputs(stuff, 1, outchar);
  1629.            fflush(stdout);
  1630.            return(0);
  1631. }
  1632.  
  1633.  
  1634. CursorUp()
  1635. {
  1636.         /** move the cursor up one line **/
  1637.  
  1638.         if (!_up)
  1639.            return(-1);
  1640.  
  1641.        tputs(_up, 1, outchar);
  1642.     fflush(stdout);
  1643.         return(0);
  1644. }
  1645.  
  1646.  
  1647. CursorDown()
  1648. {
  1649.         /** move the cursor down one line **/
  1650.  
  1651.        if (!_down) 
  1652.           return(-1);
  1653.  
  1654.        tputs(_down, 1, outchar);
  1655.        fflush(stdout);
  1656.        return(0);
  1657. }
  1658.  
  1659.  
  1660. CursorLeft()
  1661. {
  1662.         /** move the cursor one character to the left **/
  1663.  
  1664.        if (!_left) 
  1665.           return(-1);
  1666.  
  1667.        tputs(_left, 1, outchar);
  1668.        fflush(stdout);
  1669.        return(0);
  1670. }
  1671.  
  1672.  
  1673. CursorRight()
  1674. {
  1675.         /** move the cursor one character to the right (nondestructive) **/
  1676.  
  1677.        if (!_right) 
  1678.           return(-1);
  1679.  
  1680.        tputs(_right, 1, outchar);
  1681.        fflush(stdout);
  1682.        return(0);
  1683. }
  1684.  
  1685.  
  1686. StartBold()
  1687. {
  1688.         /** start boldface/standout mode **/
  1689.  
  1690.        if (!_setbold) 
  1691.          return(-1);
  1692.  
  1693.        tputs(_setbold, 1, outchar);
  1694.        fflush(stdout);
  1695.        return(0);
  1696. }
  1697.  
  1698.  
  1699. EndBold()
  1700. {
  1701.         /** compliment of startbold **/
  1702.  
  1703.         if (!_clearbold) 
  1704.            return(-1);
  1705.  
  1706.        tputs(_clearbold, 1, outchar);
  1707.        fflush(stdout);
  1708.        return(0);
  1709. }
  1710.  
  1711.  
  1712. StartUnderline()
  1713. {
  1714.         /** start underline mode **/
  1715.  
  1716.        if (!_setunderline) 
  1717.           return(-1);
  1718.  
  1719.        tputs(_setunderline, 1, outchar);
  1720.        fflush(stdout);
  1721.        return(0);
  1722. }
  1723.  
  1724.  
  1725. EndUnderline()
  1726. {
  1727.         /** the compliment of start underline mode **/
  1728.  
  1729.        if (!_clearunderline) 
  1730.           return(-1);
  1731.  
  1732.        tputs(_clearunderline, 1, outchar);
  1733.        fflush(stdout);
  1734.        return(0);
  1735. }
  1736.  
  1737.  
  1738. StartHalfbright()
  1739. {
  1740.         /** start half intensity mode **/
  1741.  
  1742.        if (!_sethalfbright) 
  1743.          return(-1);
  1744.  
  1745.        tputs(_sethalfbright, 1, outchar);
  1746.        fflush(stdout);
  1747.        return(0);
  1748. }
  1749.  
  1750. EndHalfbright()
  1751. {
  1752.         /** compliment of starthalfbright **/
  1753.  
  1754.        if (!_clearhalfbright) 
  1755.           return(-1);
  1756.  
  1757.        tputs(_clearhalfbright, 1, outchar);
  1758.        fflush(stdout);
  1759.        return(0);
  1760. }
  1761.  
  1762. StartInverse()
  1763. {
  1764.         /** set inverse video mode **/
  1765.  
  1766.        if (!_setinverse) 
  1767.          return(-1);
  1768.  
  1769.        tputs(_setinverse, 1, outchar);
  1770.        fflush(stdout);
  1771.        return(0);
  1772. }
  1773.  
  1774.  
  1775. EndInverse()
  1776. {
  1777.         /** compliment of startinverse **/
  1778.  
  1779.        if (!_clearinverse) 
  1780.          return(-1);
  1781.  
  1782.        tputs(_clearinverse, 1, outchar);
  1783.        fflush(stdout);
  1784.        return(0);
  1785. }
  1786.  
  1787. PutLine(x, y, line, args)
  1788. int x,y;
  1789. char *line; 
  1790. int  args;
  1791. {
  1792.         /** write line at location x,y **/
  1793.  
  1794.         MoveCursor(x,y);
  1795.     _doprnt(line, &args, stdout);
  1796.         fflush(stdout);    /* ensure it actually gets out! */
  1797. }
  1798.  
  1799. CleartoEOLN()
  1800. {
  1801.         /** clear to end of line **/
  1802.  
  1803.        if (!_cleartoeoln) 
  1804.          return(-1);
  1805.  
  1806.        tputs(_cleartoeoln, 1, outchar);
  1807.        fflush(stdout);  /* clear the output buffer */
  1808.        return(0);
  1809. }
  1810.  
  1811. CleartoEOS()
  1812. {
  1813.         /** clear to end of screen **/
  1814.  
  1815.        if (!_cleartoeos) 
  1816.          return(-1);
  1817.  
  1818.        tputs(_cleartoeos, 1, outchar);
  1819.        fflush(stdout);  /* clear the output buffer */
  1820.        return(0);
  1821. }
  1822.  
  1823. #ifdef RAWMODE
  1824.  
  1825. Raw(state)
  1826. int state;
  1827. {
  1828.     /** state is either ON or OFF, as indicated by call **/
  1829.  
  1830.         if (state == OFF && _inraw) {
  1831. #ifdef BSD
  1832.       echo();
  1833.       nocrmode();
  1834. #else
  1835.       (void) ioctl(TTYIN, TCSETAW, &_original_tty);
  1836. #endif
  1837.           _inraw = 0;
  1838.     }
  1839.         else if (state == ON && ! _inraw) {
  1840. #ifdef BSD
  1841.        noecho();
  1842.        crmode();
  1843. #else
  1844.       (void) ioctl(TTYIN, TCGETA, &_original_tty);    /** current setting **/
  1845.           
  1846.       (void) ioctl(TTYIN, TCGETA, &_raw_tty);    /** again! **/
  1847.       _raw_tty.c_iflag &= ~(INLCR | ICRNL |BRKINT);
  1848.       _raw_tty.c_iflag |= IXON;
  1849.       _raw_tty.c_oflag |= OPOST;
  1850.       _raw_tty.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
  1851.       _raw_tty.c_lflag &= ~(ICANON | ECHO);
  1852.       _raw_tty.c_cc[VMIN] = '\01';
  1853.       _raw_tty.c_cc[VTIME] = '\0';
  1854.       (void) ioctl(TTYIN, TCSETAW, &_raw_tty);
  1855. #endif
  1856.  
  1857.           _inraw = 1;
  1858.         }
  1859. }
  1860.  
  1861. int
  1862. ReadCh()
  1863. {
  1864.         /** read a character with Raw mode set! **/
  1865.  
  1866.         register int result;
  1867.         char ch;
  1868.  
  1869.         result = read(0, &ch, 1);
  1870.     
  1871.     return(result == 0? EOF : ch);
  1872. }
  1873.  
  1874. #endif
  1875.  
  1876. outchar(c)
  1877. char c;
  1878. {
  1879.     /** output the given character.  From tputs... **/
  1880.     /** Note: this CANNOT be a macro!              **/
  1881.  
  1882.     putc(c, stdout);
  1883. }
  1884. END-OF-FILE
  1885.  
  1886. size=`wc -c < src/curses.c`
  1887.  
  1888. if [ $size != 8965 ]
  1889. then
  1890.   echo Warning: src/curses.c changed - should be 8965 bytes, not $size bytes
  1891. fi
  1892.  
  1893. chmod 644 src/curses.c
  1894.  
  1895. # ---------- file src/date.c ----------
  1896.  
  1897.  
  1898. if [ -f src/date.c ]
  1899. then
  1900.   echo File 'src/date.c' already exists\!
  1901.   exit 1
  1902. fi
  1903.  
  1904. echo extracting file src/date.c...
  1905. cat << 'END-OF-FILE' > src/date.c
  1906. /**        date.c        **/
  1907.  
  1908. /** return the current date and time in a readable format! **/
  1909. /** also returns an ARPA RFC-822 format date...            **/
  1910.  
  1911. /** (C) Copyright 1985, Dave Taylor **/
  1912.  
  1913. #include "headers.h"
  1914. #ifdef BSD
  1915. #  include <sys/time.h>
  1916. #else
  1917. #  include <time.h>
  1918. #endif
  1919.  
  1920. #include <ctype.h>
  1921.  
  1922. #ifdef BSD
  1923. #undef toupper
  1924. #endif
  1925.  
  1926. #define MONTHS_IN_YEAR    11    /* 0-11 equals 12 months! */
  1927. #define FEB         1    /* 0 = January           */
  1928. #define DAYS_IN_LEAP_FEB 29    /* leap year only       */
  1929.  
  1930. #define ampm(n)        (n > 12? n - 12 : n)
  1931. #define am_or_pm(n)    (n > 11? (n > 23? "am" : "pm") : "am")
  1932. #define leapyear(year)    ((year % 4 == 0) && (year % 100 != 0))
  1933.  
  1934. char *dayname[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  1935.           "Friday", "Saturday", "" };
  1936.  
  1937. char *monname[] = { "January", "February", "March", "April", "May", "June",
  1938.           "July", "August", "September", "October", "November",
  1939.           "December", ""};
  1940.  
  1941. char *arpa_dayname[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
  1942.           "Fri", "Sat", "" };
  1943.  
  1944. char *arpa_monname[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1945.           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""};
  1946.  
  1947. int  days_in_month[] = { 31,    28,    31,    30,    31,     30, 
  1948.           31,     31,    30,   31,    30,     31,  -1};
  1949.  
  1950. #ifdef BSD
  1951.   char *tzname();
  1952. #else
  1953.   extern char *tzname[];
  1954. #endif
  1955.  
  1956. char *get_date()
  1957. {
  1958.     /** return the date in the format exemplified by;
  1959.         Thursday, April 18th 1985 at 8:35 pm
  1960.     **/
  1961.  
  1962.     static char buffer[SLEN];    /* static character buffer       */
  1963.     struct tm *the_time,        /* Time structure, see CTIME(3C) */
  1964.               *localtime();
  1965.     char      *suffix();         /* digit suffix for date     */
  1966.     long      junk;            /* time in seconds....         */
  1967.  
  1968.     dprint0("get_date()\n");
  1969.  
  1970.     junk = time(0);    /* this must be here for it to work! */
  1971.     the_time = localtime(&junk);
  1972.         
  1973.     sprintf(buffer, "%s, %s %d%s %d at %d:%02d %s",
  1974.          dayname[the_time->tm_wday],    /* weekday */
  1975.          monname[the_time->tm_mon],    /* month   */
  1976.          the_time->tm_mday,        /* day     */
  1977.          suffix(the_time->tm_mday),    /* suffix  */
  1978.          the_time->tm_year + 1900,    /* year    */
  1979.          ampm(the_time->tm_hour),    /* hour    */
  1980.          the_time->tm_min,        /* minute  */
  1981.                  ((the_time->tm_hour == 12 || the_time->tm_hour == 24)
  1982.           && the_time->tm_min == 0) ? (the_time->tm_hour == 12? "noon" :
  1983.           "midnight") : am_or_pm(the_time->tm_hour));    /* am | pm */
  1984.  
  1985.     return( (char *) buffer);
  1986. }
  1987.  
  1988. char *suffix(day)
  1989. int day;
  1990. {
  1991.     /** this routine returns the suffix appropriate for the
  1992.         specified number to make it an ordinal number.  ie,
  1993.         if given '1' it would return 'st', and '2' => 'nd'
  1994.     **/
  1995.  
  1996.     static char buffer[10];
  1997.     register int digit;
  1998.  
  1999.     digit = day % 10;
  2000.  
  2001.     if (digit == 0 || digit > 3)
  2002.       strcpy(buffer,"th");
  2003.     else if (digit == 1)
  2004.       strcpy(buffer,"st");
  2005.     else if (digit == 2)
  2006.       strcpy(buffer, "nd");
  2007.     else
  2008.       strcpy(buffer, "rd");
  2009.  
  2010.     return( (char *) buffer);
  2011. }
  2012.  
  2013. char *get_arpa_date()
  2014. {
  2015.     /** returns an ARPA standard date.  The format for the date
  2016.         according to DARPA document RFC-822 is exemplified by;
  2017.  
  2018.                      Mon, 12 Aug 85 6:29:08 MST
  2019.  
  2020.     **/
  2021.  
  2022.     static char buffer[SLEN];    /* static character buffer       */
  2023.     struct tm *the_time,        /* Time structure, see CTIME(3C) */
  2024.           *localtime();
  2025.     long       junk;        /* time in seconds....         */
  2026.  
  2027.     dprint0("get_arpa_date()\n");
  2028.  
  2029.     junk = time(0);    /* this must be here for it to work! */
  2030.     the_time = localtime(&junk);
  2031.  
  2032.     sprintf(buffer, "%s, %d %s %d %d:%02d:%02d %s",
  2033.       arpa_dayname[the_time->tm_wday],
  2034.       the_time->tm_mday % 32,
  2035.       arpa_monname[the_time->tm_mon],
  2036.       the_time->tm_year % 100,
  2037.       the_time->tm_hour % 24,
  2038.       the_time->tm_min  % 61,
  2039.       the_time->tm_sec  % 61,
  2040. #ifdef BSD
  2041.       tzname());
  2042. #else
  2043.       tzname[the_time->tm_isdst]);
  2044. #endif
  2045.     
  2046.     return( (char *) buffer);
  2047. }
  2048.  
  2049. char *full_month(month)
  2050. char *month;
  2051. {
  2052.     /** Given a three letter month abbreviation, return the 
  2053.         full name of the month.   If can't figure it out, just
  2054.         return the given argument. **/
  2055.  
  2056.     char   name[4];
  2057.     register int i;
  2058.  
  2059.     /** ensure name in correct case... **/
  2060.  
  2061.     strncpy(name, shift_lower(month), 3);
  2062.     name[0] = toupper(name[0]);
  2063.  
  2064.     /** now simply step through arpa_monname table to find a match **/
  2065.  
  2066.     for (i=0; i < 12; i++)
  2067.       if (strncmp(name, arpa_monname[i], 3) == 0)
  2068.         return((char *) monname[i]);
  2069.     
  2070.     return( (char *) month);
  2071. }
  2072.  
  2073. days_ahead(days, buffer)
  2074. int days;
  2075. char *buffer;
  2076. {
  2077.     /** return in buffer the date (Day, Mon Day, Year) of the date
  2078.         'days' days after today. **/
  2079.  
  2080.     struct tm *the_time,        /* Time structure, see CTIME(3C) */
  2081.           *localtime();
  2082.     long       junk;        /* time in seconds....         */
  2083.  
  2084.     dprint1("days_ahead(days=%d)\n", days);
  2085.  
  2086.     junk = time(0);    /* this must be here for it to work! */
  2087.     the_time = localtime(&junk);
  2088.  
  2089.     /* increment the day of the week */
  2090.  
  2091.     the_time->tm_wday = (the_time->tm_wday + days) % 7;
  2092.  
  2093.     /* the day of the month... */
  2094.     the_time->tm_mday += days;
  2095.     
  2096.     if (the_time->tm_mday > days_in_month[the_time->tm_mon]) {
  2097.       if (the_time->tm_mon == FEB && leapyear(the_time->tm_year)) {
  2098.         if (the_time->tm_mday > DAYS_IN_LEAP_FEB) {
  2099.           the_time->tm_mday -= days_in_month[the_time->tm_mon];
  2100.           the_time->tm_mon += 1;
  2101.         }
  2102.       }
  2103.       else {
  2104.         the_time->tm_mday -= days_in_month[the_time->tm_mon];
  2105.         the_time->tm_mon += 1;
  2106.       }
  2107.     }
  2108.  
  2109.     /* check the month of the year */
  2110.     if (the_time->tm_mon > MONTHS_IN_YEAR) {
  2111.       the_time->tm_mon -= MONTHS_IN_YEAR;
  2112.       the_time->tm_year += 1;
  2113.     }
  2114.  
  2115.     /* now, finally, build the actual date string */
  2116.  
  2117.     sprintf(buffer, "%s, %d %s %d",
  2118.       arpa_dayname[the_time->tm_wday],
  2119.       the_time->tm_mday % 32,
  2120.       arpa_monname[the_time->tm_mon],
  2121.       the_time->tm_year % 100);
  2122. }
  2123.  
  2124. int
  2125. valid_date(day, mon, year)
  2126. char *day, *mon, *year;
  2127. {
  2128.     /** validate the given date - returns TRUE iff the date
  2129.         handed is reasonable and valid.  **/
  2130.  
  2131.     register int daynum, yearnum;
  2132.  
  2133.     dprint3("valid_date(day='%s', month='%s', year='%s')\n", day, mon, year);
  2134.  
  2135.     daynum = atoi(day);
  2136.     yearnum = atoi(year);
  2137.     
  2138.     if (daynum < 1 || daynum > 31)
  2139.       return(0);
  2140.     
  2141.     if (yearnum < 1 || (yearnum > 100 && yearnum < 1900) ||
  2142.         yearnum > 2000)
  2143.       return(0);
  2144.     
  2145.     return(1);
  2146. }
  2147.  
  2148. fix_date(entry)
  2149. struct header_rec *entry;
  2150. {
  2151.     /** This routine will 'fix' the date entry for the specified
  2152.         message.  This consists of 1) adjusting the year to 0-99
  2153.         and 2) altering time from HH:MM:SS to HH:MM am|pm **/ 
  2154.  
  2155.     dprint3("fix_date(month='%s', day='%s', year='%s')\n",
  2156.          entry->month, entry->day, entry->year);
  2157.  
  2158.     if (atoi(entry->year) > 99)     
  2159.       sprintf(entry->year,"%d", atoi(entry->year) - 1900);
  2160.  
  2161.     fix_time(entry->time);
  2162. }
  2163.  
  2164. fix_time(timestring)
  2165. char *timestring;
  2166. {
  2167.     /** Timestring in format HH:MM:SS (24 hour time).  This routine
  2168.         will fix it to display as: HH:MM [am|pm] **/
  2169.  
  2170.     int hour, minute;
  2171.  
  2172.     dprint1("fix_time(string='%s')\n", timestring);
  2173.  
  2174.     sscanf(timestring, "%d:%d", &hour, &minute);
  2175.  
  2176.     if (hour < 1 || hour == 24) 
  2177.       sprintf(timestring, "12:%2d (midnight)", minute);
  2178.     else if (hour < 12)
  2179.       sprintf(timestring, "%d:%2.2d am", hour, minute);
  2180.     else if (hour == 12)
  2181.       sprintf(timestring, "%d:%2.2d (noon)", hour, minute);
  2182.     else if (hour < 24)
  2183.       sprintf(timestring, "%d:%2.2d pm", hour-12, minute);
  2184. }
  2185.  
  2186. #ifdef BSD
  2187.  
  2188. char *tzname()
  2189. {
  2190.     /** Return the name of the timezone (three letters) by plowing about
  2191.         in the various time structures on the Berkeley systems.  This is
  2192.         a pretty grungy routine, actually! **/
  2193.  
  2194.     struct timeval  *current_time;
  2195.     struct timezone *time_zone;    
  2196.     char   *timezone();
  2197.  
  2198.     gettimeofday(current_time, time_zone);
  2199.  
  2200.     return (timezone(time_zone->tz_minuteswest, time_zone->tz_dsttime));
  2201. }
  2202.  
  2203. #endif
  2204. END-OF-FILE
  2205.  
  2206. size=`wc -c < src/date.c`
  2207.  
  2208. if [ $size != 7441 ]
  2209. then
  2210.   echo Warning: src/date.c changed - should be 7441 bytes, not $size bytes
  2211. fi
  2212.  
  2213. chmod 644 src/date.c
  2214.  
  2215. # ---------- file src/delete.c ----------
  2216.  
  2217.  
  2218. if [ -f src/delete.c ]
  2219. then
  2220.   echo File 'src/delete.c' already exists\!
  2221.   exit 1
  2222. fi
  2223.  
  2224. echo extracting file src/delete.c...
  2225. cat << 'END-OF-FILE' > src/delete.c
  2226. /**        delete.c        **/
  2227.  
  2228. /**  Delete or undelete files: just set flag in header record! 
  2229.  
  2230.      (C) Copyright 1985  Dave Taylor
  2231. **/
  2232.  
  2233. #include "headers.h"
  2234.  
  2235. delete(real_del)
  2236. int real_del;
  2237. {
  2238.     /** Delete current message.  If real-del is false, then we're
  2239.         actually requested to toggle the state of the current
  2240.         message... **/
  2241.  
  2242.     dprint1("delete(real_del=%s)\n", real_del? "ON" : "OFF");
  2243.  
  2244.     if (real_del)
  2245.       header_table[current-1].delete = 1;
  2246.     else
  2247.       header_table[current-1].delete = ! header_table[current-1].delete;
  2248.  
  2249.     show_msg_status(current-1);
  2250. }
  2251.  
  2252. undelete()
  2253. {
  2254.     /** clear the deleted message flag **/
  2255.  
  2256.     dprint0("undelete()\n");
  2257.  
  2258.     header_table[current-1].delete = 0;
  2259.  
  2260.     show_msg_status(current-1);
  2261. }
  2262.  
  2263. show_delete_flags()
  2264. {
  2265.     /** display page of headers (10) if present.  First check to 
  2266.         ensure that header_page is in bounds, fixing silently if not **/
  2267.  
  2268.     register int first = 0, last = 0, line = 4;
  2269.  
  2270.     dprint0("show_delete_flags()\n");
  2271.  
  2272.     (void) fix_header_page();
  2273.  
  2274.     /** compute last header to display **/
  2275.  
  2276.     first = header_page*headers_per_page;
  2277.     last  = first + (headers_per_page - 1);
  2278.  
  2279.     if (last > message_count) 
  2280.       last = message_count;
  2281.  
  2282.     /** okay, now let's show the stuff **/
  2283.  
  2284.     while (first <= last) {
  2285.       MoveCursor(line++,7);
  2286.       if (header_table[first].delete) putchar('*');
  2287.        else                  putchar(' ');
  2288.       first++;
  2289.     }
  2290. }
  2291.  
  2292. show_msg_status(msg)
  2293. int msg;
  2294. {
  2295.     /** show the status of the current message only.  **/
  2296.  
  2297.     register int line;
  2298.  
  2299.     dprint1("show_msg_status(msg=%d)\n", msg);
  2300.  
  2301.     line = (msg % headers_per_page) + 4;
  2302.  
  2303.     MoveCursor(line,7);
  2304.     putchar( header_table[msg].delete? '*' : ' ');
  2305. }
  2306. END-OF-FILE
  2307.  
  2308. size=`wc -c < src/delete.c`
  2309.  
  2310. if [ $size != 1591 ]
  2311. then
  2312.   echo Warning: src/delete.c changed - should be 1591 bytes, not $size bytes
  2313. fi
  2314.  
  2315. chmod 644 src/delete.c
  2316.  
  2317. # ---------- file src/encode.c ----------
  2318.  
  2319.  
  2320. if [ -f src/encode.c ]
  2321. then
  2322.   echo File 'src/encode.c' already exists\!
  2323.   exit 1
  2324. fi
  2325.  
  2326. echo extracting file src/encode.c...
  2327. cat << 'END-OF-FILE' > src/encode.c
  2328. /**        encode.c        **/
  2329.  
  2330. /** This is a heavily mangled version of the 'cypher' program written by
  2331.     person or persons unknown.  
  2332.  
  2333.     (C) Copyright 1986, Dave Taylor
  2334. **/
  2335.  
  2336. #include <stdio.h>
  2337. #include "curses.h"
  2338. #include "headers.h"
  2339.  
  2340. #define RS    94
  2341. #define RN    4
  2342. #define RMASK    0x7fff    /* use only 15 bits */
  2343.  
  2344. static char r[RS][RN];        /* rotors */
  2345. static char ir[RS][RN];        /* inverse rotors */
  2346. static char h[RS];        /* half rotor */
  2347. static char s[RS];        /* shuffle vector */
  2348. static int  p[RN];        /* rotor indices */
  2349.  
  2350. static char the_key[SLEN];    /* unencrypted key */
  2351. static char *encrypted_key;    /* encrypted key   */
  2352.  
  2353. getkey(send)
  2354. int send;
  2355. {
  2356.     /** this routine prompts for and returns an encode/decode
  2357.         key for use in the rest of the program.  If send == 1
  2358.         then need to mess with rawmode. **/
  2359.  
  2360.     char buffer[NLEN];
  2361.     int gotkey = 0;
  2362.  
  2363.     dprint1("getkey(send=%s)\n", send? "ON" : "OFF");
  2364.  
  2365.     ClearLine(21);
  2366.  
  2367.     if (send) Raw(OFF);
  2368.  
  2369.     while ( !gotkey ) {
  2370.       MoveCursor(LINES-1,0);
  2371.       ClearLine(LINES-1);
  2372.       if (send)
  2373.         strcpy( buffer, getpass( "Enter encryption key: "));
  2374.       else
  2375.         strcpy( buffer, getpass( "Enter decryption key: "));
  2376.       MoveCursor(LINES-1,0);
  2377.       if ( strcmp( buffer, getpass( "Please enter it again: "))) {
  2378.         error("Your keys were not the same!");
  2379.         sleep(1);
  2380.         clear_error();
  2381.         continue;
  2382.       }
  2383.       strcpy(the_key, buffer);    /* save unencrypted key */
  2384.       makekey( buffer );
  2385.       gotkey = 1;
  2386.     }
  2387.  
  2388.     if (send) Raw(ON);
  2389.  
  2390.     setup();        /** initialize the rotors etc. **/
  2391.  
  2392.     ClearLine(LINES-1);        
  2393.     clear_error();
  2394. }
  2395.  
  2396. get_key_no_prompt()
  2397. {
  2398.     /** This performs the same action as get_key, but assumes that
  2399.         the current value of 'the_key' is acceptable.  This is used
  2400.         when a message is encrypted twice... **/
  2401.  
  2402.     char buffer[SLEN];
  2403.  
  2404.     dprint0("get_key_no_prompt()\n");
  2405.  
  2406.     strcpy(buffer, the_key);
  2407.  
  2408.     makekey( buffer );
  2409.  
  2410.     setup();
  2411. }
  2412.  
  2413. encode(line)
  2414. char *line;
  2415. {
  2416.     /** encrypt or decrypt the specified line.  Uses the previously
  2417.         entered key... **/
  2418.  
  2419.     register int i, index, j, ph = 0;
  2420.  
  2421.     dprint1("encode(line='%s')\n", line);
  2422.  
  2423.     for (index=0; index < strlen(line); index++) {
  2424.       i = (int) line[index];
  2425.  
  2426.       if ( (i >= ' ') && (i < '~') ) {
  2427.         i -= ' ';
  2428.  
  2429.         for ( j = 0; j < RN; j++ )        /* rotor forwards */
  2430.           i = r[(i+p[j])%RS][j];
  2431.  
  2432.         i = ((h[(i+ph)%RS])-ph+RS)%RS;    /* half rotor */
  2433.  
  2434.         for ( j--  ; j >= 0; j-- )        /* rotor backwards */
  2435.           i = (ir[i][j]+RS-p[j])%RS;
  2436.  
  2437.         j = 0;                /* rotate rotors */
  2438.         p[0]++;
  2439.         while ( p[j] == RS ) {
  2440.           p[j] = 0;
  2441.           j++;
  2442.           if ( j == RN ) break;
  2443.           p[j]++;
  2444.             }
  2445.   
  2446.         if ( ++ph == RS )
  2447.           ph = 0;
  2448.  
  2449.         i += ' ';
  2450.       }
  2451.       
  2452.       line[index] = (char) i;    /* replace with altered one */
  2453.     }
  2454. }
  2455.  
  2456.  
  2457. makekey( rkey)
  2458. char *rkey;
  2459. {
  2460.     /** encrypt the key using the system routine 'crypt' **/
  2461.  
  2462.     char key[8], salt[2], *crypt();
  2463.  
  2464.     dprint1("makekey(rkey='%s')\n", rkey);
  2465.  
  2466.     strncpy( key, rkey, 8);
  2467.     salt[0] = key[0];
  2468.     salt[1] = key[1];
  2469.     encrypted_key = crypt( key, salt);
  2470. }
  2471.  
  2472. /*
  2473.  * shuffle rotors.
  2474.  * shuffle each of the rotors indiscriminately.  shuffle the half-rotor
  2475.  * using a special obvious and not very tricky algorithm which is not as
  2476.  * sophisticated as the one in crypt(1) and Oh God, I'm so depressed.
  2477.  * After all this is done build the inverses of the rotors.
  2478.  */
  2479.  
  2480. setup()
  2481. {
  2482.     register long i, j, k, temp;
  2483.     long seed;
  2484.  
  2485.     dprint0("setup()\n");
  2486.  
  2487.     for ( j = 0; j < RN; j++ ) {
  2488.         p[j] = 0;
  2489.         for ( i = 0; i < RS; i++ )
  2490.             r[i][j] = i;
  2491.     }
  2492.  
  2493.     seed = 123;
  2494.     for ( i = 0; i < 13; i++)        /* now personalize the seed */
  2495.       seed = (seed*encrypted_key[i] + i) & RMASK;
  2496.  
  2497.     for ( i = 0; i < RS; i++ )        /* initialize shuffle vector */
  2498.       h[i] = s[i] = i;
  2499.  
  2500.     for ( i = 0; i < RS; i++) {        /* shuffle the vector */
  2501.       seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
  2502.       k = ((seed % 65521) & RMASK) % RS;
  2503.       temp = s[k];
  2504.       s[k] = s[i];
  2505.       s[i] = temp;
  2506.     }
  2507.  
  2508.     for ( i = 0; i < RS; i += 2 ) {    /* scramble the half-rotor */
  2509.       temp = h[s[i]];            /* swap rotor elements ONCE */
  2510.       h[s[i]] = h[s[i+1]];
  2511.       h[s[i+1]] = temp;
  2512.     }
  2513.  
  2514.     for ( j = 0; j < RN; j++) {            /* select a rotor */
  2515.  
  2516.       for ( i = 0; i < RS; i++) {        /* shuffle the vector */
  2517.         seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
  2518.         k = ((seed % 65521) & RMASK) % RS;
  2519.         temp = r[i][j];
  2520.         r[i][j] = r[k][j];
  2521.         r[k][j] = temp;
  2522.       }
  2523.  
  2524.       for ( i = 0; i < RS; i++)         /* create inverse rotors */
  2525.         ir[r[i][j]][j] = i;
  2526.        }
  2527. }
  2528. END-OF-FILE
  2529.  
  2530. size=`wc -c < src/encode.c`
  2531.  
  2532. if [ $size != 4349 ]
  2533. then
  2534.   echo Warning: src/encode.c changed - should be 4349 bytes, not $size bytes
  2535. fi
  2536.  
  2537. chmod 644 src/encode.c
  2538.  
  2539. # ---------- file src/file.c ----------
  2540.  
  2541.  
  2542. if [ -f src/file.c ]
  2543. then
  2544.   echo File 'src/file.c' already exists\!
  2545.   exit 1
  2546. fi
  2547.  
  2548. echo extracting file src/file.c...
  2549. cat << 'END-OF-FILE' > src/file.c
  2550. /**        file.c        **/
  2551.  
  2552. /** File I/O routines, including deletion from the mailbox! 
  2553.  
  2554.     (C) Copyright 1986, Dave Taylor
  2555. **/
  2556.  
  2557. #include "headers.h"
  2558. #include <ctype.h>
  2559.  
  2560. #ifdef BSD
  2561. #undef tolower
  2562. #endif
  2563.  
  2564. int
  2565. save()
  2566. {
  2567.     /** save current message in file.  Append if possible!
  2568.         Returns zero iff no file specified (cancelled command) **/
  2569.  
  2570.     char filename[SLEN], address[LONG_SLEN], buffer[SLEN];
  2571.     FILE *save_file;
  2572.  
  2573.     dprint0("save()\n");
  2574.  
  2575.     MoveCursor(LINES-2,0);
  2576.     printf("File current message in: ");
  2577.  
  2578.     if (save_by_name) {
  2579.       /** build default filename to save to **/
  2580.       get_return(address);
  2581.       get_return_name(address, buffer);
  2582.       sprintf(filename, "=/%s", buffer);
  2583.     }
  2584.     else
  2585.       filename[0] = '\0';
  2586.  
  2587.     optionally_enter(filename, LINES-2, 25, FALSE);
  2588.     MoveCursor(LINES-1,0);
  2589.  
  2590.     if (strlen(filename) == 0)  /** <return> means 'cancel', right? **/
  2591.       return(0);
  2592.  
  2593.     if (! expand_filename(filename))
  2594.       return(0);    /* failed expanding name! */
  2595.  
  2596.     if ((save_file = fopen(filename,"a")) == NULL) {
  2597.       error1("Couldn't append to file %s!", filename);
  2598.       return(0); 
  2599.     }
  2600.  
  2601.     copy_message("", save_file, FALSE);
  2602.  
  2603.     fclose(save_file);
  2604.  
  2605.     chown(filename, getuid(), getgid());    /* owned by user */
  2606.     error1("Message saved in file %s", filename);
  2607.     return(1);
  2608. }
  2609.  
  2610. int
  2611. expand_filename(filename)
  2612. char *filename;
  2613. {
  2614.     /* expands '~' and '=' to specified file names, also will
  2615.        try to expand shell variables if encountered.. */
  2616.  
  2617.     char buffer[SLEN], varname[SLEN], env_value[SLEN];
  2618.     register int i = 1, index = 0;
  2619.  
  2620.     dprint1("expand_filename(filename='%s')\n", filename);
  2621.  
  2622.     /** new stuff - make sure no illegal char as last **/
  2623.  
  2624.     if (lastch(filename) == '\n' || lastch(filename) == '\r')
  2625.       lastch(filename) = '\0';
  2626.       
  2627.     if (filename[0] == '~') {
  2628.       sprintf(buffer, "%s%s%s", home, 
  2629.         (filename[1] != '/' && lastch(folders) != '/')? "/" : "",
  2630.           (char *) filename + 1);
  2631.       strcpy(filename, buffer);
  2632.     }
  2633.     else if (filename[0] == '=') {
  2634.       if (strlen(folders) == 0) {
  2635.         error("MAILDIR not defined.  Can't expand '='");
  2636.         return(0);
  2637.       }
  2638.       sprintf(buffer, "%s%s%s", folders, 
  2639.         (filename[1] != '/' && lastch(folders) != '/')? "/" : "",
  2640.         (char *) filename + 1);
  2641.       strcpy(filename, buffer);
  2642.     }
  2643.     else if (filename[0] == '$') {    /* env variable! */
  2644.       while (isalnum(filename[i]))
  2645.         varname[index++] = filename[i++];
  2646.       varname[index] = '\0';
  2647.  
  2648.       strcpy(env_value, getenv(varname));
  2649.  
  2650.       if (strlen(env_value) == 0) {
  2651.         error1("Don't know what the value of $%s is!", varname);
  2652.         return(0);
  2653.       }
  2654.  
  2655.       sprintf(buffer, "%s%s%s", env_value, 
  2656.           (filename[i] != '/' && lastch(env_value) != '/')? "/" : "",
  2657.           (char *) filename + i);
  2658.       strcpy(filename, buffer);
  2659.     }
  2660.       
  2661.     return(1);
  2662. }
  2663. END-OF-FILE
  2664.  
  2665. size=`wc -c < src/file.c`
  2666.  
  2667. if [ $size != 2644 ]
  2668. then
  2669.   echo Warning: src/file.c changed - should be 2644 bytes, not $size bytes
  2670. fi
  2671.  
  2672. chmod 644 src/file.c
  2673.  
  2674. echo done
  2675.  
  2676. exit 0
  2677.  
  2678.  
  2679.  
  2680.  
  2681.