home *** CD-ROM | disk | FTP | other *** search
/ Super Net 1 / SUPERNET_1.iso / PC / OTROS / UNIX / ARCHIE / CLIENTS / XARCHIE2.TAR / support.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-02  |  17.5 KB  |  663 lines

  1. /*
  2.  * Copyright (c) 1989, 1990, 1991 by the University of Washington
  3.  *
  4.  * For copying and distribution information, please see the file
  5.  * <copyright.h>.
  6.  *
  7.  * v1.2.1 - 09/23/91 (gf)  - made it use regex.h---much nicer
  8.  * v1.2.0 - 09/17/91 (bpk) - added BULL & USG stuff, thanks to Jim Sillas
  9.  * v1.1.1 - 08/30/91 (bpk) - added VMS support; cast index()
  10.  */
  11.  
  12. /*
  13.  * Miscellaneous routines pulled from ~beta/lib/pfs and ~beta/lib/filters
  14.  */
  15.  
  16. #include <copyright.h>
  17. #include <stdio.h>
  18. #if defined(USG) || defined(UTS) || defined(_AIX)
  19. # include <string.h>
  20. #else
  21. # include <strings.h>
  22. #endif
  23.  
  24. #include <errno.h>
  25.  
  26. #ifdef VMS
  27. # ifdef WALLONGONG
  28. #  include "twg$tcp:[netdist.include]netdb.h"
  29. # else /* Multinet */
  30. #  include "multinet_root:[multinet.include]netdb.h"
  31. # endif
  32. # include <vms.h>
  33. #else /* not VMS */
  34. # include <netdb.h>
  35. # include <sys/types.h>
  36. # include <sys/file.h>
  37. # include <sys/param.h>
  38. #endif /* VMS */
  39.  
  40. #include <pfs.h>
  41. #include <pprot.h>
  42. #include <perrno.h>
  43. #include <pcompat.h>
  44. #include <pauthent.h>
  45. #include <pmachine.h>
  46.  
  47. #include "regex.h"
  48.  
  49. int    pfs_enable = PMAP_ATSIGN;
  50.  
  51. #ifndef FALSE
  52. # define TRUE     1
  53. # define FALSE   0
  54. #endif
  55.  
  56. /* 
  57.  * wcmatch - Match string s against template containing widlcards
  58.  *
  59.  *         WCMATCH takes a string and a template, and returns
  60.  *         true if the string matches the template, and 
  61.  *         FALSE otherwise.
  62.  *
  63.  *    ARGS:  s        - string to be tested
  64.  *           template - Template containing optional wildcards
  65.  *
  66.  * RETURNS:  TRUE (non-zero) on match.  FALSE (0) otherwise.
  67.  *
  68.  *    NOTE:  If template is NULL, will return TRUE.
  69.  *
  70.  */
  71. int
  72. wcmatch(s,template)
  73.     char    *s;
  74.     char    *template;
  75.     {
  76.     char    temp[200];
  77.     char    *p = temp;
  78.  
  79.     if(!template) return(TRUE);
  80.     *p++ = '^';
  81.  
  82.     while(*template) {
  83.         if(*template == '*') {*(p++)='.'; *(p++) = *(template++);}
  84.         else if(*template == '?') {*(p++)='.';template++;}
  85.         else if(*template == '.') {*(p++)='\\';*(p++)='.';template++;}
  86.         else if(*template == '[') {*(p++)='\\';*(p++)='[';template++;}
  87.         else if(*template == '$') {*(p++)='\\';*(p++)='$';template++;}
  88.         else if(*template == '^') {*(p++)='\\';*(p++)='^';template++;}
  89.         else if(*template == '\\') {*(p++)='\\';*(p++)='\\';template++;}
  90.         else *(p++) = *(template++);
  91.     }
  92.         
  93.     *p++ = '$';
  94.     *p++ = '\0';
  95.  
  96.     if(re_comp(temp)) return(FALSE);
  97.  
  98. #ifdef AUX
  99.     if (re_exec(s) == (char *)NULL)
  100.       return 0;
  101.     return 1;
  102. #else
  103.     return(re_exec(s));
  104. #endif
  105.     }
  106.  
  107. /*
  108.  * ul_insert - Insert a union link at the right location
  109.  *
  110.  *             UL_INSERT takes a directory and a union link to be added
  111.  *             to a the list of union links in the directory.  It then
  112.  *             inserts the union link in the right spot in the linked
  113.  *             list of union links associated with that directory.
  114.  *
  115.  *           If an identical link already exists, then the link which
  116.  *             would be evaluated earlier (closer to the front of the list)
  117.  *             wins and the other one is freed.  If this happens, an error
  118.  *             will also be returned.
  119.  *        
  120.  *    ARGS:    ul    - link to be inserted
  121.  *           vd    - directory to get link
  122.  *             p     - vl that this link will apper after
  123.  *                     NULL - This vl will go at end of list
  124.  *                     vd   - This vl will go at head of list
  125.  *
  126.  * RETURNS:    Success, or UL_INSERT_ALREADY_THERE or UL_INSERT_SUPERSEDING
  127.  */
  128. int
  129. ul_insert(ul,vd,p)
  130.     VLINK    ul;        /* Link to be inserted                   */
  131.     VDIR    vd;        /* Directory to receive link             */
  132.     VLINK    p;        /* Union link to appear prior to new one */
  133.     {
  134.     VLINK    current;
  135.  
  136.     /* This is the first ul in the directory */
  137.     if(vd->ulinks == NULL) {
  138.         vd->ulinks = ul;
  139.         ul->previous = NULL;
  140.         ul->next = NULL;
  141.         return(PSUCCESS);
  142.     }
  143.  
  144.     /* This ul will go at the head of the list */
  145.     if(p == (VLINK) vd) {
  146.         ul->next = vd->ulinks;
  147.         ul->next->previous = ul;
  148.         vd->ulinks = ul;
  149.         ul->previous = NULL;
  150.     }
  151.     /* Otherwise, decide if it must be inserted at all  */
  152.     /* If an identical link appears before the position */
  153.     /* at which the new one is to be inserted, we can   */
  154.     /* return without inserting it                 */
  155.     else {
  156.         current = vd->ulinks;
  157.  
  158.         while(current) {
  159.         /* p == NULL means we insert after last link */
  160.         if(!p && (current->next == NULL))
  161.             p = current;
  162.  
  163.         if(vl_comp(current,ul) == 0) {
  164.             vlfree(ul);
  165.             return(UL_INSERT_ALREADY_THERE);
  166.         }
  167.  
  168.         if(current == p) break;
  169.         current = current->next;
  170.         }
  171.  
  172.         /* If current is null, p was not found */
  173.         if(current == NULL)
  174.         return(UL_INSERT_POS_NOTFOUND);
  175.  
  176.         /* Insert ul */
  177.         ul->next = p->next;
  178.         p->next = ul;
  179.         ul->previous = p;
  180.         if(ul->next) ul->next->previous = ul;
  181.     }
  182.  
  183.     /* Check for identical links after ul */
  184.     current = ul->next;
  185.  
  186.     while(current) {
  187.         if(vl_comp(current,ul) == 0) {
  188.         current->previous->next = current->next;
  189.         if(current->next)
  190.             current->next->previous = current->previous;
  191.         vlfree(current);
  192.         return(UL_INSERT_SUPERSEDING);
  193.         }
  194.         current = current->next;
  195.     }
  196.     
  197.     return(PSUCCESS);
  198.     }
  199.  
  200. /*
  201.  * vl_insert - Insert a directory link at the right location
  202.  *
  203.  *             VL_INSERT takes a directory and a link to be added to a 
  204.  *             directory and inserts it in the linked list of links for
  205.  *             that directory.  
  206.  *
  207.  *             If a link already exists with the same name, and if the
  208.  *             information associated with the new link matches that in
  209.  *             the existing link, an error is returned.  If the information
  210.  *             associated with the new link is different, but the magic numbers
  211.  *             match, then the new link will be added as a replica of the
  212.  *             existing link.  If the magic numbers do not match, the new
  213.  *             link will only be added to the list of "replicas" if the
  214.  *             allow_conflict flag has been set.
  215.  * 
  216.  *             If the link is not added, an error is returned and the link
  217.  *             is freed.  Ordering for the list of links is by the link name.  
  218.  *        
  219.  *             If vl is a union link, then VL_INSERT calls ul_insert with an
  220.  *           added argument indicating the link is to be included at the
  221.  *             end of the union link list.
  222.  * 
  223.  *    ARGS:    vl - Link to be inserted, vd - directory to get link
  224.  *             allow_conflict - insert links with conflicting names
  225.  *
  226.  * RETURNS:    Success, or VL_INSERT_ALREADY_THERE
  227.  */
  228. int
  229. vl_insert(vl,vd,allow_conflict)
  230.     VLINK    vl;        /* Link to be inserted               */
  231.     VDIR    vd;        /* Directory to receive link         */
  232.     int        allow_conflict;    /* Allow duplicate names             */
  233.     {
  234.     VLINK    current;    /* To step through list             */
  235.     VLINK    crep;        /* To step through list of replicas  */
  236.     int    retval;        /* Temp for checking returned values */
  237.  
  238.     /* This can also be used to insert union links at end of list */
  239.     if(vl->linktype == 'U') return(ul_insert(vl,vd,NULL));
  240.  
  241.     /* If this is the first link in the directory */
  242.     if(vd->links == NULL) {
  243.         vd->links = vl;
  244.         vl->previous = NULL;
  245.         vl->next = NULL;
  246.         vd->lastlink = vl;
  247.         return(PSUCCESS);
  248.     }
  249.  
  250.     /* If no sorting is to be done, just insert at end of list */
  251.     if(allow_conflict == VLI_NOSORT) {
  252.         vd->lastlink->next = vl;
  253.         vl->previous = vd->lastlink;
  254.         vl->next = NULL;
  255.         vd->lastlink = vl;
  256.         return(PSUCCESS);
  257.     }
  258.  
  259.     /* If it is to be inserted at start of list */
  260.     if(vl_comp(vl,vd->links) < 0) {
  261.         vl->next = vd->links;
  262.         vl->previous = NULL;
  263.         vl->next->previous = vl;
  264.         vd->links = vl;
  265.         return(PSUCCESS);
  266.     }
  267.  
  268.     current = vd->links;
  269.  
  270.     /* Otherwise, we must find the right spot to insert it */
  271.     while((retval = vl_comp(vl,current)) > 0) {
  272.         if(!current->next) {
  273.         /* insert at end */
  274.         vl->previous = current;
  275.         vl->next = NULL;
  276.         current->next = vl;
  277.         vd->lastlink = vl;
  278.         return(PSUCCESS);
  279.         }
  280.         current = current->next;
  281.     }
  282.  
  283.     /* If we found an equivilant entry already in list */
  284.     if(!retval) {
  285.         if(vl_equal(vl,current)) {
  286.         vlfree(vl);
  287.         return(VL_INSERT_ALREADY_THERE);
  288.         }
  289.         if((allow_conflict == VLI_NOCONFLICT) &&
  290.            ((vl->f_magic_no != current->f_magic_no) ||
  291.         (vl->f_magic_no==0)))
  292.         return(VL_INSERT_CONFLICT);
  293.         /* Insert the link into the list of "replicas" */
  294.         /* If magic is 0, then create a pseudo magic number */
  295.         if(vl->f_magic_no == 0) vl->f_magic_no = -1;
  296.         crep = current->replicas;
  297.         if(!crep) {
  298.         current->replicas = vl;
  299.         vl->next = NULL;
  300.         vl->previous = NULL;
  301.         }
  302.         else {
  303.         while(crep->next) {
  304.             /* If magic was 0, then we need a unique magic number */
  305.             if((crep->f_magic_no < 0) && (vl->f_magic_no < 1))
  306.             (vl->f_magic_no)--;
  307.             crep = crep->next;
  308.         }
  309.         /* If magic was 0, then we need a unique magic number */
  310.         if((crep->f_magic_no < 0) && (vl->f_magic_no < 1))
  311.             (vl->f_magic_no)--;
  312.         crep->next = vl;
  313.         vl->previous = crep;
  314.         vl->next = NULL;
  315.         }
  316.         return(PSUCCESS);
  317.     }
  318.  
  319.     /* We found the spot where vl is to be inserted */
  320.     vl->next = current;
  321.     vl->previous = current->previous;
  322.     current->previous = vl;
  323.     vl->previous->next = vl;
  324.     return(PSUCCESS);
  325.     }
  326.  
  327. /*
  328.  * nlsindex - Find first instance of string 2 in string 1 following newline
  329.  *
  330.  *          NLSINDEX scans string 1 for the first instance of string
  331.  *          2 that immediately follows a newline.  If found, NLSINDEX
  332.  *          returns a pointer to the first character of that instance.
  333.  *          If no instance is found, NLSINDEX returns NULL (0).
  334.  *
  335.  *    NOTE:   This function is only useful for searching strings that
  336.  *            consist of multiple lines.  s1 is assumed to be preceeded
  337.  *           by a newline.  Thus, if s2 is at the start of s1, it will
  338.  *          be found.
  339.  *    ARGS:   s1 - string to be searched
  340.  *            s2 - string to be found
  341.  * RETURNS:   First instance of s2 in s1, or NULL (0) if not found
  342.  */
  343. char *
  344. nlsindex(s1,s2)
  345.     char    *s1;        /* String to be searched */
  346.     char    *s2;        /* String to be found    */
  347.     {
  348.     register int s2len = strlen(s2);
  349.     char    *curline = s1;    /* Pointer to start of current line */
  350.  
  351.     /* In case s2 appears at start of s1 */
  352.     if(strncmp(curline,s2,s2len) == 0)
  353.         return(curline);
  354.  
  355.     /* Check remaining lines of s1 */
  356.     while((curline = (char *) index(curline,'\n')) != NULL) {
  357.         curline++;
  358.         if(strncmp(curline,s2,s2len) == 0)
  359.         return(curline);
  360.     }
  361.  
  362.     /* We didn't find it */
  363.     return(NULL);
  364.     }
  365.  
  366. /*
  367.  * month_sname - Return a month name from it's number
  368.  *
  369.  *               MONTH_SNAME takes a number in the range 0
  370.  *               to 12 and returns a pointer to a string
  371.  *               representing the three letter abbreviation
  372.  *             for that month.  If the argument is out of 
  373.  *         range, MONTH_SNAME returns a pointer to "Unk".
  374.  *
  375.  *       ARGS:   n - Number of the month
  376.  *    RETURNS:   Abbreviation for selected month
  377.  */
  378. char *month_sname(n)
  379.     int n;        /* Month number */
  380. {
  381.     static char *name[] = { "Unk",
  382.         "Jan","Feb","Mar","Apr","May","Jun",
  383.         "Jul","Aug","Sep","Oct","Nov","Dec"
  384.     };
  385.     return((n < 1 || n > 12) ? name[0] : name[n]);
  386. }
  387.  
  388. /*
  389.  * sindex - Find first instance of string 2 in string 1 
  390.  *
  391.  *          SINDEX scans string 1 for the first instance of string
  392.  *          2.  If found, SINDEX returns a pointer to the first
  393.  *          character of that instance.  If no instance is found, 
  394.  *          SINDEX returns NULL (0).
  395.  *
  396.  *    ARGS:   s1 - string to be searched
  397.  *            s2 - string to be found
  398.  * RETURNS:   First instance of s2 in s1, or NULL (0) if not found
  399.  */
  400. char *
  401. sindex(s1,s2)
  402.     char    *s1;        /* String to be searched   */
  403.     char    *s2;        /* String to be found      */
  404.     {
  405.     register int s2len = strlen(s2);
  406.     char    *s = s1;    /* Temp pointer to string  */
  407.  
  408.     /* Check for first character of s2 */
  409.     while((s = (char *) index(s,*s2)) != NULL) {
  410.         if(strncmp(s,s2,s2len) == 0)
  411.         return(s);
  412.         s++;
  413.     }
  414.  
  415.     /* We didn't find it */
  416.     return(NULL);
  417.     }
  418.  
  419. int
  420. scan_error(erst)
  421.     char    *erst;
  422.     {
  423.     *p_err_string = '\0';
  424.  
  425.     if(strncmp(erst,"NOT-A-DIRECTORY",15) == 0) 
  426.         return(DIRSRV_NOT_DIRECTORY);
  427.  
  428.     if(strncmp(erst,"UNIMPLEMENTED",13) == 0) {
  429.         perrno = DIRSRV_UNIMPLEMENTED;
  430.         sscanf(erst+13,"%*[^\n \t\r]%*[ \t]%[^\n]",p_err_string);
  431.         return(perrno);
  432.     }
  433.  
  434.     if(strncmp(erst,"WARNING ",8) == 0) {
  435.         erst += 8;
  436.         *p_warn_string = '\0';
  437.         sscanf(erst,"%*[^\n \t\r]%*[ \t]%[^\n]",p_warn_string);
  438.         /* Return values for warnings are negative */
  439.         if(strncmp(erst,"OUT-OF-DATE",11) == 0) {
  440.         pwarn = PWARN_OUT_OF_DATE;
  441.         return(PSUCCESS);
  442.         }
  443.         if(strncmp(erst,"MESSAGE",7) == 0) {
  444.         pwarn = PWARN_MSG_FROM_SERVER;
  445.         return(PSUCCESS);
  446.         }
  447.         pwarn = PWARNING;
  448.         sscanf(erst,"%[^\n]",p_warn_string);
  449.         return(PSUCCESS);
  450.     }
  451.     else if(strncmp(erst,"ERROR",5) == 0) {
  452.         if(*(erst+5)) sscanf(erst+6,"%[^\n]",p_err_string);
  453.         perrno = DIRSRV_ERROR;
  454.         return(perrno);
  455.     }
  456.     /* The rest start with "FAILURE" */
  457.     else if(strncmp(erst,"FAILURE",7) != 0) {
  458.         /* Unrecognized - Give warning, but return PSUCCESS */
  459.         if(pwarn == 0) {
  460.         *p_warn_string = '\0';
  461.         pwarn = PWARN_UNRECOGNIZED_RESP;
  462.         sscanf(erst,"%[^\n]",p_warn_string);
  463.         }
  464.         return(PSUCCESS);
  465.     }
  466.  
  467.     if(strncmp(erst,"FAILURE ",8) != 0) {
  468.         perrno = PFAILURE;
  469.         return(perrno);
  470.     }    
  471.     erst += 8;
  472.     
  473.     sscanf(erst,"%*[^\n \t\r]%*[ \t]%[^\n]",p_err_string);
  474.  
  475.     /* Still to add               */
  476.     /* DIRSRV_AUTHENT_REQ     242 */
  477.     /* DIRSRV_BAD_VERS        245 */
  478.  
  479.     if(strncmp(erst,"NOT-FOUND",9) == 0) 
  480.         perrno = DIRSRV_NOT_FOUND;
  481.     else if(strncmp(erst,"NOT-AUTHORIZED",13) == 0) 
  482.         perrno = DIRSRV_NOT_AUTHORIZED;
  483.     else if(strncmp(erst,"ALREADY-EXISTS",14) == 0) 
  484.         perrno = DIRSRV_ALREADY_EXISTS;
  485.     else if(strncmp(erst,"NAME-CONFLICT",13) == 0) 
  486.         perrno = DIRSRV_NAME_CONFLICT;
  487.     else if(strncmp(erst,"SERVER-FAILED",13) == 0) 
  488.         perrno = DIRSRV_SERVER_FAILED;
  489.      /* Use it whether it starts with FAILURE or not */
  490.     else if(strncmp(erst,"NOT-A-DIRECTORY",15) == 0) 
  491.         perrno = DIRSRV_NOT_DIRECTORY;
  492.     else perrno = PFAILURE;
  493.  
  494.     return(perrno);
  495.     }
  496.  
  497. PATTRIB 
  498. parse_attribute(line)
  499.     char    *line;
  500.     {
  501.     char    l_precedence[MAX_DIR_LINESIZE];
  502.     char    l_name[MAX_DIR_LINESIZE];
  503.     char    l_type[MAX_DIR_LINESIZE];
  504.     char    l_value[MAX_DIR_LINESIZE];
  505.     PATTRIB    at;
  506.     int    tmp;
  507.  
  508.     tmp = sscanf(line,"OBJECT-INFO %s %s %[^\n]", l_name, l_type, l_value);
  509.     
  510.     if(tmp < 3) {
  511.         tmp = sscanf(line,"LINK-INFO %s %s %s %[^\n]", l_precedence,
  512.              l_name, l_type, l_value);
  513.         if(tmp < 4) {
  514.         perrno = DIRSRV_BAD_FORMAT;
  515.         return(NULL);
  516.         }
  517.     }
  518.  
  519.     at = atalloc();
  520.  
  521.     if(tmp == 4) {
  522.         if(strcmp(l_precedence,"CACHED") == 0) 
  523.         at->precedence = ATR_PREC_CACHED;
  524.         else if(strcmp(l_precedence,"LINK") == 0) 
  525.         at->precedence = ATR_PREC_LINK;
  526.         else if(strcmp(l_precedence,"REPLACEMENT") == 0) 
  527.         at->precedence = ATR_PREC_REPLACE;
  528.         else if(strcmp(l_precedence,"ADDITIONAL") == 0) 
  529.         at->precedence = ATR_PREC_ADD;
  530.     }
  531.  
  532.     at->aname = stcopy(l_name);
  533.     at->avtype = stcopy(l_type);
  534.     if(strcmp(l_type,"ASCII") == 0) 
  535.         at->value.ascii = stcopy(l_value);
  536.     else if(strcmp(l_type,"LINK") == 0) {
  537.         char        ftype[MAX_DIR_LINESIZE];
  538.         char        lname[MAX_DIR_LINESIZE];
  539.         char        htype[MAX_DIR_LINESIZE];
  540.         char        host[MAX_DIR_LINESIZE];
  541.         char        ntype[MAX_DIR_LINESIZE];
  542.         char        fname[MAX_DIR_LINESIZE];
  543.         VLINK        al;
  544.  
  545.         al = vlalloc();
  546.         at->value.link = al;
  547.  
  548.         tmp = sscanf(l_value,"%c %s %s %s %s %s %s %d %d",
  549.              &(al->linktype),
  550.              ftype,lname,htype,host,ntype,fname,
  551.              &(al->version),
  552.              &(al->f_magic_no));
  553.         if(tmp == 9) {
  554.         al->type = stcopyr(ftype,al->type);
  555.         al->name = stcopyr(unquote(lname),al->name);
  556.         al->hosttype = stcopyr(htype,al->hosttype);
  557.         al->host = stcopyr(host,al->host);
  558.         al->nametype = stcopyr(ntype,al->nametype);
  559.         al->filename = stcopyr(fname,al->filename);
  560.         }
  561.         else {
  562.         perrno = DIRSRV_BAD_FORMAT;
  563.         return(NULL);
  564.         }
  565.         
  566.     }
  567.  
  568.     return(at);
  569.     }
  570.  
  571. /*
  572.  * nxtline - Find the next line in the string
  573.  *
  574.  *          NXTLINE takes a string and returns a pointer to
  575.  *          the character immediately following the next newline.
  576.  *
  577.  *    ARGS:   s - string to be searched
  578.  *
  579.  * RETURNS:   Next line or NULL (0) on failure
  580.  */
  581. char *
  582. nxtline(s)
  583.     char    *s;        /* String to be searched */
  584.  {
  585.     s = (char *) index(s,'\n');
  586.     if(s) return(++s);
  587.     else return(NULL);
  588.     }
  589.  
  590.  
  591. /*
  592.  * unquote - unquote string if necessary
  593.  *
  594.  *          UNQUOTE takes a string and unquotes it if it has been quoted.
  595.  *
  596.  *    ARGS:   s - string to be unquoted
  597.  *            
  598.  * RETURNS:   The original string.  If the string has been quoted, then the
  599.  *            result appears in static storage, and must be copied if 
  600.  *            it is to last beyond the next call to quote.
  601.  *
  602.  */
  603. char *
  604. unquote(s)
  605.     char    *s;        /* String to be quoted */
  606.     {
  607.     static char    unquoted[200];
  608.     char        *c = unquoted;
  609.  
  610.     if(*s != '\'') return(s);
  611.  
  612.     s++;
  613.  
  614.     /* This should really treat a quote followed by other */
  615.     /* than a quote or a null as an error                 */
  616.     while(*s) {
  617.         if(*s == '\'') s++;
  618.         if(*s) *c++ = *s++;
  619.     }
  620.  
  621.     *c++ = '\0';
  622.  
  623.     return(unquoted);
  624.     }
  625.  
  626. #if defined(DEBUG) && defined(STRSPN)
  627. /* needed for -D option parsing */
  628. /*
  629.  * strspn - Count initial characters from chrs in s
  630.  *
  631.  *          STRSPN counts the occurances of chacters from chrs
  632.  *            in the string s preceeding the first occurance of
  633.  *            a character not in s.
  634.  *
  635.  *    ARGS:   s    - string to be checked
  636.  *            chrs - string of characters we are looking for
  637.  *
  638.  * RETURNS:   Count of initial characters from chrs in s
  639.  */
  640. strspn(s,chrs)
  641.     char    *s;    /* String to search                         */
  642.     char    *chrs; /* String of characters we are looking for  */
  643.     {
  644.     char    *cp;   /* Pointer to the current character in chrs */
  645.     int    count; /* Count of characters seen so far          */
  646.     
  647.     count = 0;
  648.  
  649.     while(*s) {
  650.         for(cp = chrs;*cp;cp++)
  651.         if(*cp == *s) {
  652.             s++;
  653.             count++;
  654.             goto done;
  655.         }
  656.         return(count);
  657.     done:
  658.         ;
  659.     }
  660.     return(count);
  661.     }
  662. #endif
  663.