home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / ccmd / cmusr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-08-19  |  17.1 KB  |  726 lines

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