home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / elm.lzh / ELM / SRC / STRINGS.C < prev    next >
Text File  |  1991-01-11  |  15KB  |  592 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: strings.c,v 4.1.1.3 90/08/15 21:48:07 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 4.1.1.3 $   $State: Exp $
  6.  *
  7.  *             Copyright (c) 1986, 1987 Dave Taylor
  8.  *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log:    strings.c,v $
  17.  * Revision 4.1.1.3  90/08/15  21:48:07  syd
  18.  * the user's (unmodified) limit criteria was being compared w/
  19.  * the lower-case version of the header contents.
  20.  * From: dwolfe@earth.sps.mot.com (Dave Wolfe)
  21.  * 
  22.  * Revision 4.1.1.2  90/06/21  22:45:06  syd
  23.  * Make display not show To user if user is also sender
  24.  * From: Marius Olafsson
  25.  * 
  26.  * Revision 4.1.1.1  90/06/05  20:38:58  syd
  27.  * Allow nesting on () in comment in address
  28.  * From: Chip Rosenthal <chip@chinacat.Unicom.COM>
  29.  * 
  30.  * Revision 4.1  90/04/28  22:44:16  syd
  31.  * checkin of Elm 2.3 as of Release PL0
  32.  * 
  33.  *
  34.  ******************************************************************************/
  35.  
  36. /** This file contains all the string oriented functions for the
  37.     ELM Mailer, and lots of other generally useful string functions! 
  38.  
  39.     For BSD systems, this file also includes the function "tolower"
  40.     to translate the given character from upper case to lower case.
  41.  
  42. **/
  43.  
  44. #include "headers.h"
  45. #include <ctype.h>
  46.  
  47. #ifdef BSD
  48. #undef tolower
  49. #undef toupper
  50. #endif
  51.  
  52. /** forward declarations **/
  53.  
  54. char *format_long(), *strip_commas(), *tail_of_string(),
  55.      *get_token(), *strip_parens(), *argv_zero(), *strcpy(), *strncpy();
  56.  
  57. char *index();
  58.  
  59.  
  60. copy_sans_escape(dest, source, len)
  61. char *dest, *source;
  62. int  len;
  63. {
  64.     /** this performs the same function that strncpy() does, but
  65.         also will translate any escape character to a printable
  66.         format (e.g. ^(char value + 32))
  67.     **/
  68.  
  69.     register int i = 0, j = 0;
  70.  
  71.     while (i < len && source[i] != '\0') {
  72.       if (iscntrl(source[i]) && source[i] != '\t') {
  73.          dest[j++] = '^';
  74.          dest[j++] = source[i++] + 'A' - 1;
  75.       }
  76.       else
  77.         dest[j++] = source[i++];
  78.     }
  79.  
  80.     dest[j] = '\0';
  81. }
  82.  
  83. /**
  84.     This routine will return true if the "addr" contains the "user" subject
  85.     to the following contstraints:  (1) either the "user" is at the front
  86.     of "addr" or it is preceded by an appropriate meta-char, and (2)
  87.     either the "user" is at the end of "addr" or it is suceeded by an
  88.     appropriate meta-char.
  89. **/
  90. int addr_matches_user(addr,user)
  91. register char *addr, *user;
  92. {
  93.     int len = strlen(user);
  94.     static char c_before[] = "!:%";    /* these can appear before a username */
  95.     static char c_after[] = ":%@";    /* these can appear after a username  */
  96.  
  97.     do {
  98.       if ( strncmp(addr,user,len) == 0 ) {
  99.         if ( addr[len] == '\0' || index(c_after,addr[len]) != NULL )
  100.           return TRUE;
  101.       }
  102.     } while ( (addr=strpbrk(addr,c_before)) != NULL && *++addr != '\0' ) ;
  103.     return FALSE;
  104. }
  105.  
  106. int
  107. tail_of(from, buffer, to)
  108. char *from, *buffer, *to;
  109. {
  110.     /** Return last two words of 'from'.  This is to allow
  111.         painless display of long return addresses as simply the
  112.         machine!username. 
  113.         Or if the first word of the 'from' address is username or
  114.         full_username and 'to' is not NULL, then use the 'to' line
  115.         instead of the 'from' line.
  116.         If the 'to' line is used, return 1, else return 0.
  117.  
  118.         Also modified to know about X.400 addresses (sigh) and
  119.         that when we ask for the tail of an address similar to
  120.         a%b@c we want to get back a@b ...
  121.     **/
  122.  
  123.     /** Note: '!' delimits Usenet nodes, '@' delimits ARPA nodes,
  124.               ':' delimits CSNet & Bitnet nodes, '%' delimits multi-
  125.           stage ARPA hops, and '/' delimits X.400 addresses...
  126.               (it is fortunate that the ASCII character set only has
  127.              so many metacharacters, as I think we're probably using
  128.           them all!!) **/
  129.  
  130.     register int loc, i = 0, cnt = 0, using_to = 0;
  131.     
  132. #ifndef INTERNET
  133.     
  134.     /** let's see if we have an address appropriate for hacking: 
  135.         what this actually does is remove the spuriously added
  136.         local bogus Internet header if we have one and the message
  137.         has some sort of UUCP component too...
  138.     **/
  139.  
  140.     sprintf(buffer, "@%s", hostfullname); 
  141.     if (chloc(from,'!') != -1 && in_string(from, buffer))
  142.        from[strlen(from)-strlen(buffer)] = '\0';
  143.  
  144. #endif
  145.  
  146.     /**
  147.         Produce a simplified version of the from into buffer.  If the
  148.         from is just "username" or "Full Username" it will be preserved.
  149.         If it is an address, the rightmost "stuff!stuff", "stuff@stuff",
  150.         or "stuff:stuff" will be used.
  151.     **/
  152.     for (loc = strlen(from)-1; loc >= 0 && cnt < 2; loc--) {
  153.       if (from[loc] == BANG || from[loc] == AT_SIGN ||
  154.           from[loc] == COLON) cnt++;
  155.       if (cnt < 2) buffer[i++] = from[loc];
  156.     }
  157.     buffer[i] = '\0';
  158.     reverse(buffer);
  159.  
  160. #ifdef MMDF
  161.     if (strlen(buffer) == 0) {
  162.       if(to && *to != '\0' && !addr_matches_user(to, username)) {
  163.         tail_of(to, buffer, (char *)0);
  164.         using_to = 1;
  165.       } else
  166.         strcpy(buffer, full_username);
  167.         }
  168. #endif /* MMDF */
  169.  
  170.     if ( strcmp(buffer,full_username) == 0 ||
  171.       addr_matches_user(buffer,username) ) {
  172.  
  173.       /* This message is from the user, so use the "to" header instead
  174.        * if possible, to be more informative. Otherwise be nice and
  175.        * use full_username rather than the bare username even if
  176.        * we've only matched on the bare username.
  177.        */
  178.  
  179.       if(to && *to != '\0' && !addr_matches_user(to, username)) {
  180.         tail_of(to, buffer, (char *)0);
  181.         using_to = 1;
  182.       } else
  183.         strcpy(buffer, full_username);
  184.  
  185.     } else {                    /* user%host@host? */
  186.  
  187.       /** The logic here is that we're going to use 'loc' as a handy
  188.           flag to indicate if we've hit a '%' or not.  If we have,
  189.           we'll rewrite it as an '@' sign and then when we hit the
  190.           REAL at sign (we must have one) we'll simply replace it
  191.           with a NULL character, thereby ending the string there.
  192.       **/
  193.  
  194.       loc = 0;
  195.  
  196.       for (i=0; buffer[i] != '\0'; i++)
  197.         if (buffer[i] == '%') {
  198.           buffer[i] = AT_SIGN;
  199.           loc++;
  200.         }
  201.         else if (buffer[i] == AT_SIGN && loc)
  202.           buffer[i] = '\0';
  203.     }
  204.     return(using_to);
  205.  
  206. }
  207.  
  208. char *format_long(inbuff, init_len)
  209. char *inbuff;
  210. int   init_len;
  211. {
  212.     /** Return buffer with \n\t sequences added at each point where it 
  213.         would be more than 80 chars long.  It only allows the breaks at 
  214.         legal points (ie commas followed by white spaces).  init-len is 
  215.         the characters already on the first line...  Changed so that if 
  216.      head of "elm", it'll 
  217.             include "\r\n\t" instead.
  218.         Changed to use ',' as a separator and to REPLACE it after it's
  219.         found in the output stream...
  220.     **/
  221.  
  222.     static char ret_buffer[VERY_LONG_STRING];
  223.     register int iindex = 0, current_length = 0, depth=15, i, len;
  224.     char     buffer[VERY_LONG_STRING];
  225.     char     *word, *bufptr;
  226.  
  227.     strcpy(buffer, inbuff);
  228.  
  229.     bufptr = (char *) buffer;
  230.  
  231.     current_length = init_len + 2;    /* for luck */
  232.  
  233.     while ((word = get_token(bufptr,",", depth)) != NULL) {
  234.  
  235.         /* first, decide what sort of separator we need, if any... */
  236.  
  237.       if (strlen(word) + current_length > 80) {
  238.         if (iindex > 0) {
  239.           ret_buffer[iindex++] = ',';    /* close 'er up, doctor! */
  240.           ret_buffer[iindex++] = '\n';
  241.           ret_buffer[iindex++] = '\t';
  242.         }
  243.         
  244.         /* now add this pup! */
  245.  
  246.         for (i=(word[0] == ' '? 1:0), len = strlen(word); i<len; i++)
  247.           ret_buffer[iindex++] = word[i];
  248.         current_length = len + 8;    /* 8 = TAB */
  249.       }
  250.  
  251.       else {    /* just add this address to the list.. */
  252.  
  253.         if (iindex > 0) {
  254.           ret_buffer[iindex++] = ',';    /* comma added! */
  255.           ret_buffer[iindex++] = ' ';
  256.           current_length += 2;
  257.         }
  258.         for (i=(word[0] == ' '? 1:0), len = strlen(word); i<len; i++)
  259.           ret_buffer[iindex++] = word[i];
  260.         current_length += len;
  261.       }
  262.     
  263.       bufptr = NULL;
  264.     }
  265.     
  266.     ret_buffer[iindex] = '\0';
  267.  
  268.     return( (char *) ret_buffer);
  269. }
  270.  
  271. char *strip_commas(string)
  272. char *string;
  273. {
  274.     /** return string with all commas changed to spaces.  This IS
  275.         destructive and will permanently change the input string.. **/
  276.  
  277.     register char *strptr = string;
  278.  
  279.     for (; *strptr; strptr++)
  280.       if (*strptr == COMMA)
  281.         *strptr = SPACE;
  282.  
  283.     return( (char *) string);
  284. }
  285.  
  286. char *strip_parens(string)
  287. char *string;
  288. {
  289.     /**
  290.         Remove parenthesized information from a string.  More specifically,
  291.         comments as defined in RFC822 are removed.  This procedure is
  292.         non-destructive - a pointer to static data is returned.
  293.     **/
  294.     static char  buffer[VERY_LONG_STRING];
  295.     register char *bufp;
  296.     register int depth;
  297.  
  298.     for ( bufp = buffer, depth = 0 ; *string != '\0' ; ++string ) {
  299.       switch ( *string ) {
  300.       case '(':            /* begin comment on '('        */
  301.         ++depth;
  302.         break;
  303.       case ')':            /* decr nesting level on ')'    */
  304.         --depth;
  305.         break;
  306.       case '\\':            /* treat next char literally    */
  307.         if ( *++string == '\0' ) {        /* gracefully handle    */
  308.           *bufp++ = '\\';            /* '\' at end of string    */
  309.           --string;                /* even tho it's wrong    */
  310.         } else if ( depth == 0 ) {
  311.           *bufp++ = '\\';
  312.           *bufp++ = *string;
  313.         }
  314.         break;
  315.       default:            /* a regular char        */
  316.         if ( depth == 0 )
  317.           *bufp++ = *string;
  318.         break;
  319.       }
  320.     }
  321.     *bufp = '\0';
  322.     return( (char *) buffer);
  323. }
  324.  
  325. move_left(string, chars)
  326. char string[];
  327. int  chars;
  328. {
  329.     /** moves string chars characters to the left DESTRUCTIVELY **/
  330.  
  331.     register int i;
  332.  
  333.     /*  chars--; /* index starting at zero! */
  334.  
  335.     for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
  336.       string[i-chars] = string[i];
  337.  
  338.     string[i-chars] = '\0';
  339. }
  340.  
  341. remove_first_word(string)
  342. char *string;
  343. {    /** removes first word of string, ie up to first non-white space
  344.         following a white space! **/
  345.  
  346.     register int loc;
  347.  
  348.     for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++) 
  349.         ;
  350.  
  351.     while (string[loc] == ' ' || string[loc] == '\t')
  352.       loc++;
  353.     
  354.     move_left(string, loc);
  355. }
  356.  
  357. split_word(buffer, first, rest)
  358. char *buffer, *first, *rest;
  359. {
  360.     /** Rip the buffer into first word and rest of word, translating it
  361.         all to lower case as we go along..
  362.     **/
  363.  
  364.     /** skip leading white space, just in case.. **/
  365.  
  366.     while(whitespace(*buffer)) buffer++;
  367.  
  368.     /** now copy into 'first' until we hit white space or EOLN **/
  369.  
  370.     for (; *buffer && ! whitespace(*buffer); buffer++, first++)
  371.       if (islower(*buffer))
  372.         *first = *buffer;
  373.       else
  374.         *first = tolower(*buffer);
  375.  
  376.     *first = '\0';
  377.     
  378.     while (whitespace(*buffer)) buffer++;
  379.  
  380.     for (; *buffer; buffer++, rest++)
  381.       if (islower(*buffer))
  382.         *rest = *buffer;
  383.       else
  384.         *rest = tolower(*buffer);
  385.  
  386.     *rest = '\0';
  387.  
  388.     return;
  389. }
  390.  
  391. char *tail_of_string(string, maxchars)
  392. char *string;
  393. int  maxchars;
  394. {
  395.     /** Return a string that is the last 'maxchars' characters of the
  396.         given string.  This is only used if the first word of the string
  397.         is longer than maxchars, else it will return what is given to
  398.         it... 
  399.     **/
  400.  
  401.     static char buffer[SLEN];
  402.     register int iindex, i, len;
  403.  
  404.     for (iindex=0, len = strlen(string);! whitespace(string[iindex]) && iindex < len; 
  405.          iindex++)
  406.       ;
  407.  
  408.     if (iindex < maxchars) {
  409.       strncpy(buffer, string, maxchars-2);    /* word too short */
  410.       buffer[maxchars-2] = '.';
  411.       buffer[maxchars-1] = '.';
  412.       buffer[maxchars]   = '.';
  413.       buffer[maxchars+1] = '\0';
  414.     } 
  415.     else {
  416.       i = maxchars;
  417.       buffer[i--] = '\0';
  418.       while (i > 1) 
  419.         buffer[i--] = string[iindex--];
  420.       buffer[2] = '.';
  421.       buffer[1] = '.';
  422.       buffer[0] = '.';
  423.     }
  424.  
  425.     return( (char *) buffer);
  426. }
  427.  
  428. reverse(string)
  429. char *string;
  430. {
  431.     /** reverse string... pretty trivial routine, actually! **/
  432.  
  433.     char buffer[SLEN];
  434.     register int i, j = 0;
  435.  
  436.     for (i = strlen(string)-1; i >= 0; i--)
  437.       buffer[j++] = string[i];
  438.  
  439.     buffer[j] = '\0';
  440.  
  441.     strcpy(string, buffer);
  442. }
  443.  
  444. int
  445. get_word(buffer, start, word)
  446. char *buffer, *word;
  447. int start;
  448. {
  449.     /**    return next word in buffer, starting at 'start'.
  450.         delimiter is space or end-of-line.  Returns the
  451.         location of the next word, or -1 if returning
  452.         the last word in the buffer.  -2 indicates empty
  453.         buffer!  **/
  454.  
  455.     register int loc = 0;
  456.  
  457.     while (buffer[start] == ' ' && buffer[start] != '\0')
  458.       start++;
  459.  
  460.     if (buffer[start] == '\0') return(-2);     /* nothing IN buffer! */
  461.  
  462.     while (buffer[start] != ' ' && buffer[start] != '\0')
  463.       word[loc++] = buffer[start++];
  464.  
  465.     word[loc] = '\0';
  466.     return(start);
  467. }
  468.  
  469. Centerline(line, string)
  470. int line;
  471. char *string;
  472. {
  473.     /** Output 'string' on the given line, centered. **/
  474.  
  475.     register int length, col;
  476.  
  477.     length = strlen(string);
  478.  
  479.     if (length > COLUMNS)
  480.       col = 0;
  481.     else
  482.       col = (COLUMNS - length) / 2;
  483.  
  484.     PutLine0(line, col, string);
  485. }
  486.  
  487. char *argv_zero(string)
  488. char *string;
  489. {
  490.     /** given a string of the form "/something/name" return a
  491.         string of the form "name"... **/
  492.  
  493.     static char buffer[NLEN];
  494.     register int i, j=0;
  495.  
  496.     for (i=strlen(string)-1; string[i] != '/'; i--)
  497.       buffer[j++] = string[i];
  498.     buffer[j] = '\0';
  499.  
  500.     reverse(buffer);
  501.  
  502.     return( (char *) buffer);
  503. }
  504.  
  505. #define MAX_RECURSION        20        /* up to 20 deep recursion */
  506.  
  507. char *get_token(source, keys, depth)
  508. char *source, *keys;
  509. int   depth;
  510. {
  511.     /** This function is similar to strtok() (see "opt_utils")
  512.         but allows nesting of calls via pointers... 
  513.     **/
  514.  
  515.     register int  last_ch;
  516.     static   char *buffers[MAX_RECURSION];
  517.     char     *return_value, *sourceptr;
  518.  
  519.     if (depth > MAX_RECURSION) {
  520.        error1("Get_token calls nested greater than %d deep!", 
  521.           MAX_RECURSION);
  522.        emergency_exit();
  523.     }
  524.  
  525.     if (source != NULL)
  526.       buffers[depth] = source;
  527.     
  528.     sourceptr = buffers[depth];
  529.     
  530.     if (*sourceptr == '\0') 
  531.       return(NULL);        /* we hit end-of-string last time!? */
  532.  
  533.     sourceptr += strspn(sourceptr, keys);      /* skip the bad.. */
  534.     
  535.     if (*sourceptr == '\0') {
  536.       buffers[depth] = sourceptr;
  537.       return(NULL);            /* we've hit end-of-string   */
  538.     }
  539.  
  540.     last_ch = strcspn(sourceptr, keys);   /* end of good stuff   */
  541.  
  542.     return_value = sourceptr;          /* and get the ret     */
  543.  
  544.     sourceptr += last_ch;              /* ...value            */
  545.  
  546.     if (*sourceptr != '\0')        /** don't forget if we're at end! **/
  547.       sourceptr++;                  
  548.     
  549.     return_value[last_ch] = '\0';          /* ..ending right      */
  550.  
  551.     buffers[depth] = sourceptr;          /* save this, mate!    */
  552.  
  553.     return((char *) return_value);         /* and we're outta here! */
  554. }
  555.  
  556.  
  557. quote_args(out_string,in_string)
  558. register char *out_string, *in_string;
  559. {
  560.     /** Copy from "in_string" to "out_string", collapsing multiple
  561.         white space and quoting each word.  Returns a pointer to
  562.         the resulting word.
  563.     **/
  564.  
  565.     int empty_string = TRUE;
  566.  
  567.     while ( *in_string != '\0' ) {
  568.  
  569.         /**    If this is a space then advance to the start of the next word.
  570.         Otherwise, copy through the word surrounded by quotes.
  571.         **/
  572.  
  573.         if ( isspace(*in_string) ) {
  574.         while ( isspace(*in_string) )
  575.             ++in_string;
  576.         } else {
  577.         *out_string++ = '"';
  578.         while ( *in_string != '\0' && !isspace(*in_string) )
  579.             *out_string++ = *in_string++;
  580.         *out_string++ = '"';
  581.         *out_string++ = ' ';
  582.         empty_string = FALSE;
  583.         }
  584.  
  585.     }
  586.  
  587.     if ( !empty_string )
  588.     --out_string;
  589.     *out_string = '\0';
  590. }
  591.  
  592.