home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / ccmd / cmusr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  17.3 KB  |  737 lines

  1. /*
  2.  Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  the City of New York.  Permission is granted to any individual or
  4.  institution to use, copy, or redistribute this software so long as it
  5.  is not sold for profit, provided this copyright notice is retained.
  6.  
  7.  Author: Howie Kaye
  8. */
  9.  
  10. /*
  11.  * #define NO_USERNAME_COMPLETION in machdep.h if you don't want to CCMD to
  12.  * preload the passwd file before parsing user names.  This is generally a
  13.  * lose for systems with large distributed password files where the time to
  14.  * load the usernames (via YP, for instance) can be considerable and the
  15.  * completion doesn't buy much.
  16.  */
  17.  
  18. #define USRERR
  19.  
  20. #if unix
  21.  
  22. /*
  23.  * ccmd user name parser.
  24.  */
  25.  
  26. #define INCUSRS 100
  27.  
  28. #include "ccmdlib.h"            /* ccmd symbols */
  29. #include "cmfncs.h"            /* ccmd internal symbols */
  30. #include "cmusr.h"
  31.  
  32. static brktab usrbrk = {        /* all valid chars for users */
  33.   {                    /* alphanums, "~#/_-\[]" */
  34.     0xff, 0xff, 0xff, 0xff, 0xef, 0xd2, 0x00, 0x3f,
  35.     0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x09,
  36.   },
  37.   {
  38.     0xff, 0xff, 0xff, 0xff, 0xef, 0xd2, 0x00, 0x3f,
  39.     0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x09,
  40.   }
  41. };
  42.  
  43. int usrparse(), usrhelp(), usrcomplete();
  44. ftspec ft_usr =  { usrparse, usrhelp, usrcomplete, 0, &usrbrk };
  45. char *malloc(), *realloc();
  46.  
  47. #ifdef NO_USERNAME_COMPLETION
  48. #define MAXUNAMELEN (sizeof ((struct utmp *)0)->ut_name)
  49.  
  50. /*
  51.  * common code for usrparse and usrcomplete
  52.  */
  53.  
  54. static int
  55. username_test (text, textlen, fdbp, parselen, pwp, completion)
  56. char *text;
  57. int textlen, *parselen, completion;
  58. fdb *fdbp;
  59. struct passwd **pwp;            /* optional return value */
  60. {
  61.     int usrlen, i;
  62.     brktab *btab = fdbp->_cmbrk ? fdbp->_cmbrk : &usrbrk;
  63.     char name[MAXUNAMELEN+1];
  64.     struct passwd *pw;
  65.  
  66.     for (usrlen = 0; usrlen < textlen; usrlen++)
  67.     if ((text[usrlen] & CC_QUO) == 0) {
  68.         int c = text[usrlen] & CC_CHR;
  69.         if (usrlen == 0 ? BREAK1 (btab, c) : BREAKR (btab, c))
  70.         break;
  71.     }
  72.  
  73.     if (usrlen > MAXUNAMELEN)        /* check length */
  74.     return USRxNM;
  75.  
  76.     if (!completion)
  77.     if (usrlen == textlen)        /* terminator found? */
  78.         return CMxINC;
  79.  
  80.     if (usrlen == 0)            /* check for null username */
  81.     return USRxNM;
  82.  
  83. #ifdef USR_PO
  84.     /* if parse-only parse, just return what we have */
  85.     if (fdbp->_cmffl & USR_PO) {
  86.     if (pwp)
  87.         *pwp = NULL;
  88.     *parselen = usrlen;
  89.     return CMxOK;
  90.     }
  91. #endif
  92.  
  93.     /* extract username */
  94.     for (i = 0; i < usrlen; i++)
  95.     name[i] = text[i] & CC_CHR;
  96.     name[usrlen] = 0;
  97.  
  98.     pw = getpwnam (name);
  99.     if (pw) {
  100.     *parselen = usrlen;
  101.     if (pwp)
  102.         *pwp = pw;
  103.     return CMxOK;
  104.     }
  105.     return completion ? CMxINC : USRxNM;
  106. }
  107.  
  108. static struct passwd *kludge[2] = { 0, 0 };
  109.  
  110. usrparse (text, textlen, fdbp, parselen, value)
  111. char *text;
  112. int textlen, *parselen;
  113. fdb *fdbp;
  114. pval *value;
  115. {
  116.     int plen, result;
  117.  
  118.     result = username_test (text, textlen, fdbp, &plen, &kludge[0], 0);
  119.     if (result != CMxOK)
  120.     return result;
  121.  
  122. #ifdef USR_PO
  123.     if (fdbp->_cmffl & USR_PO)
  124.     value->_pvstr = cmcsb._cmabp;
  125.     else
  126. #endif
  127.     value->_pvusr = &kludge[0];
  128.     *parselen = plen;
  129.     return CMxOK;
  130. }
  131.  
  132. usrhelp (text, textlen, fdbp, cust, lines)
  133. char *text;
  134. int textlen, cust;
  135. fdb *fdbp;
  136. int lines;
  137. {
  138.     if (!cust)
  139.     cmxputs ("user name");
  140.  
  141.     return lines;
  142. }
  143.  
  144. usrcomplete (text, textlen, fdbp, full, cplt, cpltlen)
  145. char *text, **cplt;
  146. int textlen, full, *cpltlen;
  147. fdb *fdbp;
  148. {
  149.     int test, len;
  150.     pval pvalue;
  151.     struct passwd *pw;
  152.  
  153.     *cplt = NULL;
  154.     *cpltlen = 0;
  155.     
  156.     test = username_test (text, textlen, fdbp, &len, &pw, 1);
  157.     switch (test) {
  158.       case CMxOK:            /* input is valid username */
  159.     return full ? CMP_GO|CMP_SPC : CMP_GO;
  160.       case CMxINC:            /* input not valid (yet) */
  161.     return CMP_BEL;
  162.     }
  163.     return CMP_GO;            /* some error; usrparse will handle */
  164. }
  165.  
  166. #else
  167.  
  168. static int usedcrunch=FALSE;
  169. /*
  170.  * parse routine for user names.
  171.  */
  172. PASSEDSTATIC int
  173. usrparse(text, textlen, fdbp, parselen, value)
  174. char *text;
  175. int textlen, *parselen;
  176. fdb *fdbp;
  177. pval *value;
  178. {
  179.   static struct usr **u;
  180.   char *term;
  181.   int pmatch, ematch,i;
  182.   struct passwd **builduserlist();
  183.   match(text, textlen, fdbp, &u, &term, &pmatch, &ematch); /* find matches */
  184.  
  185.   if (pmatch == 0) 
  186.       return(USRxNM);
  187.  
  188.   if (term == NULL)            /* unterminated....not done */
  189.     return(CMxINC);            /* return incompleteness */
  190.  
  191.   if (ematch == 0)            /* no matches */
  192.     return(USRxNM);            /* return as such */
  193.  
  194.  
  195.  
  196.   if (ematch > 1)            /* more than one match...ambiguous */
  197.     if (!(fdbp->_cmffl & USR_WILD))    /* this means 2 identical user names */
  198.       return(USRxAMB);            /* unless a wild parse */
  199.  
  200.   value->_pvusr = builduserlist(u,ematch); /* return list of matches */
  201.   *parselen = term-text;        /* and parsed length */
  202.   return(CMxOK);            /* and return successfully */
  203. }
  204.  
  205. /*
  206.  * username help routine.
  207.  */
  208. PASSEDSTATIC int
  209. usrhelp(text, textlen, fdbp, cust, lines) 
  210. char *text;
  211. int textlen, cust;
  212. fdb *fdbp;
  213. int lines;
  214. {
  215.   int ematch, pmatch,i,len,maxlen,cols,curcol;
  216.   struct usr **u;
  217.   char *term;
  218.   int mylines = 0;
  219.  
  220.   if (!cust) {                /* standard help msg */
  221.     cmxputs("user name, one of the following:");
  222.   }
  223.  
  224.                     /* find matches */
  225.   match(text, textlen, fdbp, &u, &term, &pmatch, &ematch);
  226.   if (pmatch == 0) {            /* no matches. */
  227.     cmxnl();                /* just say so */
  228.     cmxputs(" (No usernames match current input)"); /* none here */
  229.     return(lines - 1);            /* all done */
  230.   }
  231.  
  232.   if (pmatch > cmcsb._cmmax) {        /* too many to display */
  233.     cmxnl();
  234.     cmxprintf(" %d matching users.\n",pmatch); /* just say how many. */
  235.     return(lines -2);            /* all done */
  236.   }
  237.     
  238.   maxlen = 0;                /* calculate number of columns */
  239.   len = 0;                /* based on maximum name length */
  240.   mylines++;
  241.   for (i = 0 ; u[i] != NULL; i++) {    /* scan through matches */
  242.     if (u[i]->flags & USR_PARTIAL) {
  243.       len = strlen(u[i]->pwd->pw_name);    /* find longest name */
  244.       if (maxlen < len) maxlen = len;
  245.     }
  246.   }
  247.   maxlen += 3;                /* put some space after the name */
  248.   cols = (cmcsb._cmcmx+2) / maxlen;    /* number of columns per line */
  249.   if (cols <= 0) cols = 1;        /* at least one column */
  250.   curcol = 0;                /* currently printing first column */
  251.  
  252.   for ( i = 0; u[i] != NULL; i++) {    /* scan through again, and print 'em */
  253.     if (u[i]->flags & USR_PARTIAL) {    /* found a match... */
  254.       if (curcol == 0) {
  255.     cmxnl();            /* new line for first column */
  256.     mylines++;
  257.     if (mylines >= lines) {
  258.       if (!cmhelp_more("--space to continue, Q to stop--"))
  259.           return(-1);
  260.       else {
  261.           lines = cmcsb._cmrmx;
  262.           mylines = 0;
  263.       }
  264.         }
  265.  
  266.     cmxputs(" ");            /* and offset a bit */
  267.       }
  268.       cmxputs(u[i]->pwd->pw_name);    /* print the name */
  269.       if (curcol < cols -1) {        /* if not last column */
  270.     int j;                /* space out to end of column */
  271.     for(j = strlen(u[i]->pwd->pw_name); j < maxlen; j++)
  272.       cmxputc(SPACE);
  273.       }
  274.       curcol = (curcol+1) % cols;        /* move to next column */
  275.     }
  276.   }
  277.   cmxnl();                /* newline at the end */
  278.   return(lines-mylines);        /* all done */
  279. }
  280.  
  281.  
  282. /*
  283.  * find a partial completion for a list of users
  284.  */
  285.  
  286. PASSEDSTATIC char *
  287. partial(text,textlen,u,pcount,exact) 
  288. char *text; 
  289. int textlen;
  290. struct usr **u;
  291. int pcount;
  292. int *exact;
  293. {
  294.   int i,j,k;
  295.   static char buf[50];
  296.   char tbuf[50],fbuf[50],uname[50];
  297.   int buflen,fbuflen;
  298.  
  299.   *exact = TRUE;            /* assume we find an exact match */
  300.   strncpy(tbuf,text,textlen);
  301.   tbuf[textlen] = '\0';
  302.  
  303.   buf[0] = '\0';            /* start off with no matches */
  304.   for(i = 0, j = 0; u[i] != NULL && j < pcount; i++) {
  305.     if (u[i]->flags & USR_PARTIAL) {
  306.       strcpy(fbuf,u[i]->pwd->pw_name);
  307.  
  308.       fbuflen = strlen(fbuf);        /* copy then name */
  309.       while(!fmatch(fbuf,tbuf,FALSE)) {    /* shorten until we match */
  310.     fbuf[--fbuflen] = '\0';
  311.       }
  312.       strcpy(uname,u[i]->pwd->pw_name);
  313.       if (j++ == 0)
  314.     strcpy(buf,&uname[fbuflen]);    /* first time, grab completion */
  315.       else {
  316.     buflen = strlen(buf);
  317.     for(k = 0; k < buflen; k++)    /* otherwise trim it to match */
  318.       if (buf[k] != uname[fbuflen+k]) {
  319.         buf[k] = '\0';        /* if end of a name, then exact */
  320.         if (uname[fbuflen+k] != '\0') *exact = FALSE;
  321.         else *exact = TRUE;
  322.         break;
  323.       }
  324.       }
  325.     }
  326.   }
  327.   return(buf);
  328. }
  329.  
  330. /*
  331.  * user name completion routine
  332.  */
  333. usrcomplete(text, textlen, fdbp, full, cplt, cpltlen)
  334. char *text,**cplt;
  335. int textlen,full,*cpltlen;
  336. fdb *fdbp;
  337. {
  338.   int ematch, pmatch,i;
  339.   struct usr **u;
  340.   char *term;
  341.  
  342.   *cplt = NULL; *cpltlen = 0;        /* assume no completions */
  343.                     /* find matches */
  344.   match(text, textlen, fdbp, &u, &term, &pmatch, &ematch);
  345.  
  346.   if (ematch >= 1) {            /* exact match? */
  347.     *cplt = "";
  348.     if (full) return(CMP_GO | CMP_SPC);    /* just use it */
  349.     else return(CMP_PNC);
  350.   }
  351.   if (pmatch == 0) {            /* no match. */
  352.     return(CMP_BEL|CMP_GO);        /* just beep and wake up */
  353.   }
  354.   if (pmatch > 1) {            /* more than one partial match. */
  355.     int exact;
  356.     *cplt = partial(text,textlen,u,pmatch,&exact);
  357.     *cpltlen = strlen(*cplt);
  358.     if (exact) return(CMP_GO|CMP_SPC);
  359.     return(CMP_BEL);            /* we could partial complete */
  360.   }                    /* but for now,just beep */
  361.   if (pmatch == 1) {            /* one match */
  362.     int exact;
  363.     for(i = 0; u[i] != NULL; i++) {
  364.       if (u[i]->flags & USR_PARTIAL) {    /* find it */
  365.                     /* and return the completion */
  366.     *cplt = partial(text,textlen,u,pmatch,&exact);
  367.     *cpltlen = strlen(*cplt);
  368.     if (full) return(CMP_GO | CMP_SPC);
  369.     else return(CMP_PNC);
  370.       }
  371.     }
  372.   }
  373.   return(CMP_BEL);
  374. }
  375.  
  376. /*
  377.  * user name matching routine.
  378.  */
  379.  
  380. static
  381. match(text, textlen, fdbp, who, term, pmatch, ematch) 
  382. char *text, **term;
  383. int textlen, *pmatch,*ematch;
  384. fdb *fdbp;
  385. struct usr ***who;
  386. {
  387.   struct usr **u,**read_usrs();
  388.   char user[100];
  389.   int matches=0, i,inlen;
  390.   brktab *btab;                /* break table to use */
  391.   struct usr ** maybe_read_usrs(), **usrs;
  392.  
  393.   usrs = maybe_read_usrs(fdbp->_cmffl & USR_UPDONLY, fdbp->_cmffl & USR_NOUPD);
  394.   *term = NULL;
  395.   
  396.                     /* just update the user table */
  397.   if (fdbp->_cmffl & USR_UPDONLY) {
  398.       *ematch = *pmatch = 0;        /* no matches */
  399.       return;
  400.   }
  401.  
  402.   if ((btab = fdbp->_cmbrk) == NULL)    /* get supplied break table */
  403.     btab = &usrbrk;            /* or use default */
  404.  
  405.   for (inlen = 0; inlen < textlen; inlen++) { /* find # of usable chars */
  406.                         /* stop on first break char */
  407.     if (text[inlen] & 0x80) {
  408.       text[inlen] &= 0x7f;
  409.       continue;
  410.     }
  411.     else
  412.       if ((!BREAK(btab,0x7f&text[inlen],inlen))) 
  413.     continue;
  414.     else
  415.       break;
  416.   }
  417.  
  418.   if (inlen == textlen)            /* no break char? */
  419.     *term = NULL;            /* then set no terminator */
  420.   else
  421.     *term = text+inlen;            /* else point to it for caller */
  422.  
  423.                     /* copy the string to match */
  424.   for(i = 0; i < inlen; i++) user[i] = text[i]&0x7f;
  425.   user[inlen] = '\0';            /* null terminated of course */
  426.   *pmatch = *ematch = 0;        /* and start with no matches */
  427.  
  428.   for (u = usrs; *u != NULL; u++) {    /* loop through all users */
  429.     (*u)->flags &= ~(USR_PARTIAL | USR_EXACT); /* assume a mismatch */
  430.     if (fmatch((*u)->pwd->pw_name,user,TRUE)) { /* partial match? */
  431.       (*u)->flags |= USR_PARTIAL;    /* flag */
  432.       (*pmatch)++;            /* and count it */
  433.       if (fmatch((*u)->pwd->pw_name,user,FALSE)) { /* exact match? */
  434.     (*u)->flags |= USR_EXACT;    /* flag and count it */
  435.     (*ematch)++;
  436.       }
  437.     }
  438.   }
  439.   *who = usrs;                /* return the list */
  440.   return;
  441. }
  442.  
  443. /*
  444.  * free up user list read from passwd file
  445.  */
  446. free_usrs(usrs) struct usr **usrs; {
  447.   struct usr *u;
  448.   if (usrs == NULL) return;        /* no list.  never mind */
  449.   while (*usrs != NULL) {        /* for all users */
  450.     u = *usrs;
  451.     if (u->pwd != NULL) {        /* if there is a passwd entry here */
  452.                     /* free string space (note this 
  453.                        is all malloc as one chunk, so
  454.                        it is all freed as one chunk */
  455.       if (u->pwd->pw_name != NULL) free(u->pwd->pw_name);
  456.       free(u->pwd);            /* free the passwd entry */
  457.       usrs++;                /* and go to the next user */
  458.     }
  459.   }
  460.   free(usrs);
  461. }
  462.  
  463.  
  464. struct usr **
  465. read_usrs() {
  466.   struct usr **usrs = NULL;
  467.   int maxusrs = 0;
  468.   int nusrs = 0;
  469.   struct passwd *p,*getpwent(),*copyp();
  470.  
  471.   setpwent();
  472.   while (1) {
  473.     p = getpwent();
  474.     if (nusrs == maxusrs) {
  475.       usrs = (struct usr **)cmrealloc(usrs,
  476.                       sizeof(struct usr *)*(maxusrs+INCUSRS));
  477.       maxusrs+=INCUSRS;
  478.     }
  479.     if (p == NULL) break;
  480.     usrs[nusrs] = (struct usr *)malloc(sizeof(struct usr));
  481.     usrs[nusrs]->pwd = copyp(p);
  482.     usrs[nusrs++]->flags = 0;
  483.   }
  484.   usrs[nusrs] = NULL;
  485.   endpwent();
  486.   sort_usrs(usrs);            /* sort them */
  487.   return(usrs);
  488. }
  489.  
  490. usrcmp(a,b) register struct usr **a,**b; {
  491.   return(strcmp((*a)->pwd->pw_name,(*b)->pwd->pw_name));
  492. }
  493.  
  494. sort_usrs(usrs) struct usr **usrs; {
  495.   int i;
  496.   for (i = 0; usrs[i] != NULL; i++);
  497.   qsort(usrs,i,sizeof(struct usr *), usrcmp);
  498. }
  499.  
  500.  
  501.  
  502. struct passwd *
  503. copyp(p) struct passwd *p; {
  504.   struct passwd *p1;
  505.   char *buf,*bp;
  506.   p1 = (struct passwd *) malloc(sizeof (struct passwd));
  507.   buf = (char *)malloc(6 + strlen(p->pw_name) + strlen(p->pw_passwd) +
  508.            strlen(p->pw_gecos) + strlen(p->pw_dir) + strlen(p->pw_shell));
  509.   bp = buf;
  510.   strcpy(bp,p->pw_name);  p1->pw_name = bp; bp += strlen(bp)+1;
  511.   strcpy(bp,p->pw_passwd);  p1->pw_passwd = bp; bp += strlen(bp)+1;
  512.   strcpy(bp,p->pw_gecos);  p1->pw_gecos = bp; bp += strlen(bp)+1;
  513.   strcpy(bp,p->pw_dir);  p1->pw_dir = bp; bp += strlen(bp)+1;
  514.   strcpy(bp,p->pw_shell);  p1->pw_shell = bp; bp += strlen(bp)+1;
  515.   p1->pw_uid = p->pw_uid;
  516.   p1->pw_gid = p->pw_gid;
  517.   return(p1);
  518. }
  519.  
  520. struct passwd**
  521. builduserlist(u,count) struct usr **u; {
  522.   static struct passwd **p=NULL;
  523.   int i,j;
  524.   if (p != NULL) free(p);
  525.   p = (struct passwd **)malloc((count+1)*sizeof(struct passwd*));
  526.   for (i = 0,j = 0; u[i] != NULL; i++) {
  527.     if (u[i]->flags & USR_EXACT)
  528.       p[j++] = u[i]->pwd;
  529.   }
  530.   p[count] = NULL;
  531.   return(p);
  532. }
  533.  
  534.  
  535. struct usr **
  536. maybe_read_usrs(always,dont)
  537. int always, dont;
  538. {
  539.     struct stat buf;
  540.     static struct usr **usrs=NULL;
  541.     static int passwd_time=0;
  542.     int temp = always;
  543.     struct usr **rdcrunch();
  544.     struct stat sbuf;
  545.     
  546.     stat("/etc/passwd",&buf);        /* reread the passwd file? */
  547.     always = passwd_time != 0 && always && !dont;
  548.     dont = passwd_time != 0 && dont && !temp;
  549.     if ((passwd_time == 0 || buf.st_mtime > passwd_time || always) && !dont) {
  550.     passwd_time = buf.st_mtime;
  551.     if (usedcrunch)
  552.         free_crunch_users(usrs);
  553.     else
  554.         free_usrs(usrs);        /* free up old entries */
  555.     if (!((stat("/etc/passwd.crunch", &sbuf) == 0) &&
  556.           (sbuf.st_mtime > buf.st_mtime) &&
  557.           (usrs = rdcrunch("/etc/passwd.crunch")))) {
  558.         usrs = read_usrs();        /* and re read the file */
  559.         usedcrunch = FALSE;
  560.     }
  561.     else
  562.         usedcrunch = TRUE;
  563.     }
  564.     return(usrs);
  565. }
  566.  
  567.  
  568.  
  569. static struct passwd *ents;
  570. static struct usr *usrstore;
  571. static char *ubuf;
  572.  
  573. #define fixstr(s) ((*s) += (int) ubuf)
  574.  
  575. #ifdef howmany
  576. #undef howmany
  577. #endif
  578. struct usr**
  579. rdcrunch(fname)
  580. char *fname;
  581. {
  582.     int fd,len,howmany;
  583.     register int i;
  584.     struct stat sbuf;
  585.     register struct passwd *p;
  586.     register struct usr **usrs;
  587.  
  588.     if (stat(fname,&sbuf) < 0) {
  589.     return(NULL);
  590.     }
  591.     fd = open(fname,O_RDONLY,0);
  592.     if (fd == -1) {
  593.     perror(fname);
  594.     return(NULL);
  595.     }
  596.     if (xread(fd,&howmany,sizeof(howmany)) <= 0) {
  597.     perror("read");
  598.     close(fd);
  599.     return(NULL);
  600.     }
  601.     
  602.     ents = (struct passwd *) malloc(howmany*sizeof(struct passwd));
  603.     if (xread(fd, ents, howmany*sizeof(struct passwd)) <= 0) {
  604.     perror("read");
  605.     close(fd);
  606.     free(ents);
  607.     return(NULL);
  608.     }
  609.     ubuf = malloc(len = sbuf.st_size -
  610.          (sizeof(howmany) + howmany * sizeof(struct passwd)));
  611.     if (xread(fd, ubuf, len) <= 0) {
  612.     perror("read");
  613.     close(fd);
  614.     free(ents);
  615.     free(ubuf);
  616.     return(NULL);
  617.     }
  618.     close(fd);
  619.     usrs = (struct usr**)malloc((howmany+1) * sizeof (struct usr *));
  620.     usrstore = (struct usr *)malloc(howmany * sizeof (struct usr));
  621.     for ( i = 0; i < howmany; i++) {
  622.     p = &ents[i];
  623.     fixstr(&p->pw_name);
  624.     fixstr(&p->pw_passwd);
  625. #ifdef SYSV
  626.     fixstr(&p->pw_age);
  627. #endif
  628.     fixstr(&p->pw_comment);
  629.     fixstr(&p->pw_gecos);
  630.     fixstr(&p->pw_dir);
  631.     fixstr(&p->pw_shell);
  632.     usrs[i] = &usrstore[i];
  633.     usrs[i]->pwd = p;
  634.     usrs[i]->flags = 0;
  635.     }
  636.     usrs[howmany] = NULL;
  637.     return(usrs);
  638. }
  639.  
  640. free_crunch_users(usrs) 
  641. struct usr **usrs;
  642. {
  643.     free(ents);
  644.     free(ubuf);
  645.     free(usrs);
  646.     free(usrstore);
  647.     ents = NULL;
  648.     ubuf = NULL;
  649.     usrs = NULL;
  650.     usrstore = NULL;
  651. }
  652.  
  653. #undef read
  654.  
  655. xread(fd,buf,len)
  656. int fd,len;
  657. char *buf;
  658. {
  659.     int sofar = 0,x;
  660.  
  661.     while(sofar < len) {
  662.     x = read(fd, buf+sofar, len - sofar);
  663.     if (x == -1) {
  664.         return(-1);
  665.     }
  666.     sofar += x;
  667.     if (x == 0) {
  668.         break;
  669.     }
  670.     }
  671.     return(sofar);
  672. }
  673.  
  674. #endif /* NO_USERNAME_COMPLETION */
  675. #else /*  !unix */
  676.  
  677. #include "ccmd.h"            /* ccmd symbols */
  678. #include "cmfncs.h"            /* ccmd internal symbols */
  679.  
  680. static brktab usrbrk = {        /* all valid chars for users */
  681.   {                    /* alphanums, "~#/_-\[]," */
  682.     0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, 0x00, 0x3f,
  683.     0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0b,
  684.   },
  685.   {
  686.     0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, 0x00, 0x3f,
  687.     0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0b,
  688.   }
  689. };
  690.  
  691. int usrparse(), usrhelp(), usrcomplete();
  692. ftspec ft_usr =  { usrparse, usrhelp, usrcomplete, 0, &usrbrk };
  693.  
  694. PASSEDSTATIC int
  695. usrparse(text, textlen, fdbp, parselen, value)
  696. char *text;
  697. int textlen, *parselen;
  698. fdb *fdbp;
  699. pval *value;
  700. {
  701.   int i;
  702.   *parselen = 0;
  703.   value->_pvusr = NULL;
  704.   for (i = 0; i < textlen; i++) 
  705.     if (isspace(text[textlen-1])) 
  706.       return(USRxNM);
  707.   return(CMxINC);
  708. }
  709.  
  710. usrcomplete(text, textlen, fdbp, full, cplt, cpltlen)
  711. char *text,**cplt;
  712. int textlen,full,*cpltlen;
  713. fdb *fdbp;
  714. {
  715.   *cplt = NULL; 
  716.   *cpltlen = 0;
  717.   return(CMP_BEL|CMP_SPC|CMP_GO);
  718. }
  719.  
  720. PASSEDSTATIC int
  721. usrhelp(text, textlen, fdbp, cust) 
  722. char *text;
  723. int textlen, cust;
  724. fdb *fdbp;
  725. {
  726.  
  727.   if (!cust) {                /* standard help msg */
  728.     cmxputs("user name, one of the following:");
  729.   }
  730.  
  731.   cmxnl();
  732.   cmxputs(" (No usernames match current input)");
  733.   return(CMxOK);
  734. }
  735.  
  736. #endif /*  !unix */
  737.