home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / mcp / part05 < prev    next >
Encoding:
Internet Message Format  |  1987-02-05  |  48.2 KB

  1. Subject:  v08i045:  Account creation/manipulation program, Part05/08
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: Kyle Jones <xanth!kyle>
  6. Mod.sources: Volume 8, Issue 45
  7. Archive-name: mcp/Part05
  8.  
  9. [  OOPS!  I should have pointed this out earlier:  MCP is a for BSD
  10.    Unix, but I don't think it will be two hard to convert it to
  11.    other variants.  --r$  ]
  12.  
  13. #! /bin/sh
  14. # This is a shell archive.  Remove anything before this line,
  15. # then unpack it by saving it in a file and typing "sh file".
  16. # If all goes well, you will see the message "End of archive 5 (of 8)."
  17. # Contents:  src/complete.c src/describe.c src/lastlog.h src/lists.h
  18. #   src/pause.c
  19. # Wrapped by rs@mirror on Fri Feb  6 15:56:03 1987
  20. PATH=/bin:/usr/bin:/usr/ucb; export PATH
  21. echo shar: extracting "'src/complete.c'" '(29213 characters)'
  22. if test -f 'src/complete.c' ; then 
  23.   echo shar: will not over-write existing file "'src/complete.c'"
  24. else
  25. sed 's/^X//' >src/complete.c <<'@//E*O*F src/complete.c//'
  26. X#include <stdio.h>
  27. X#include <sys/types.h>
  28. X#include <sys/time.h>
  29. X#include <signal.h>
  30. X#include <setjmp.h>
  31. X#include <strings.h>
  32. X#include <errno.h>
  33. X#include "sysdep.h"
  34. X#include "macros.h"
  35. X#include "mem.h"
  36. X#include "history.h"
  37. X#include "lists.h"
  38. X
  39. X#ifdef BSD4_3
  40. Xchar    *getwd();
  41. X#endif
  42. X
  43. X#define swap(a, b)    { char t; t=a; a=b; b=t; }
  44. X#define SCMPN(a, b)    !strncmp((a), (b), strlen(a))
  45. X
  46. Xextern    jmp_buf in_continue;
  47. Xextern    char Working_Directory[];
  48. X
  49. Xchar    *BACKSPACE = "\b \b", *dirof(), *fileof(), *pathcomplete();
  50. Xstatic    char line[BUFSIZ+1], hline[BUFSIZ+1];
  51. X
  52. Xstruct    hist currh = { hline, (char *)0, 0, 0, 0, 0, (struct list *)0 };
  53. Xstruct    list History;
  54. X
  55. Xstatic    char *exprv[] = { 0, 0 };
  56. X
  57. X#ifdef SENDMAIL
  58. Xextern    struct list Aliases;
  59. X#endif
  60. X#ifdef HELPDIR
  61. Xextern    struct list Terms;
  62. X#endif
  63. Xextern    struct list AllCommands, Commands, Classes, Groups, Sigs;
  64. Xextern    struct list Null_List, Users, Vigs, Ranges;
  65. Xextern    int DevTty;
  66. X
  67. X/*
  68. X * Get the last part of a command.
  69. X */
  70. Xchar *
  71. Xtail(s)
  72. Xchar *s;
  73. X
  74. X{
  75. X    char *p, *sp;
  76. X
  77. X    sp = rindex(s, ' ');
  78. X    if (!sp) {
  79. X        p = rindex(s, '-');
  80. X        return p ? ++p : s;
  81. X    }
  82. X    else while (p = index(s, '-')) {
  83. X        if (p > sp) break;
  84. X        s = p + 1;
  85. X    }
  86. X    return s;
  87. X}
  88. X
  89. X/*
  90. X *  Count how many characters in s1 until s1 differs with s2
  91. X */
  92. Xint nmatch(s1, s2)
  93. Xchar *s1, *s2;
  94. X
  95. X{
  96. X    register int i;
  97. X
  98. X    for (i = 0; s1[i] && s2[i] && s1[i] == s2[i]; i++)
  99. X        ;
  100. X    return(i);
  101. X}
  102. X
  103. X/*
  104. X * Command and argument completion.  The variable s will be changed to reflect
  105. X * what it was completed to.  The variable *iscomplete will contain a 1 if s
  106. X * was expanded completely.
  107. X *
  108. X * One of these conditions will be satisfied and the apprpriate steps taken.
  109. X *
  110. X * 1)    If c_list is empty return immediately.
  111. X * 2)    If s is an empty string and c_list contains more than one string,
  112. X *    return an empty string with no change.
  113. X * 3)    If c_list only contains one string and s prefixes it then
  114. X *    completely expand s to that string.
  115. X * 4)    If s is exactly equal to one of the strings in c_list, merely
  116. X *    report that s in completely expanded.
  117. X * 5)    If s prefixes none of the strings in c_list then chop characters
  118. X *    out of s until it will prefix at least one of the strings in c_list.
  119. X * 6)    If s uniquely prefixes a string in c_list then completely expand
  120. X *    s to that string.
  121. X * 7)    If s prefixes more than one string in c_list expand s to the point
  122. X *    where the strings differ.
  123. X *
  124. X * Note: the strings in c_list MUST ALREADY be sorted in collating
  125. X *     sequence!
  126. X */
  127. Xchar *
  128. Xcomplete(s, c_list, iscomplete)
  129. Xchar *s;
  130. Xstruct list *c_list;
  131. Xint *iscomplete;
  132. X
  133. X{
  134. X    static char delta[LONG_BUF * 2];
  135. X    int i, lindex, n_matched, s_len, found;
  136. X
  137. X    delta[0] = '\0'; *iscomplete = 0;
  138. X    /*
  139. X     * Check for condition 1.  If no completion list, forget it.
  140. X     */
  141. X    if (c_list->l_count == 0)
  142. X        return delta;
  143. X    /*
  144. X     * Check for condition 2.  If s is empty and there is more than one
  145. X     * word in the completion list, forget it.
  146. X     */
  147. X    if (*s == '\0' && c_list->l_count > 1)
  148. X        return delta;
  149. X    /*
  150. X     * Check for condition 3.  If there is only one word in the
  151. X     * completion list and s prefixes it, this is the one.
  152. X     */
  153. X    s_len = strlen(s);
  154. X    if (c_list->l_count==1&&!strncmp(s,(char *)c_list->l_list[0],s_len)) {
  155. X        (void) strcpy(delta, &(((char *)c_list->l_list[0])[s_len]));
  156. X        (void) strcat(delta, " ");
  157. X        (void) strcat(s, delta);
  158. X        *iscomplete = 1;
  159. X        return delta;
  160. X    }
  161. X    /*
  162. X     * Binary search the completion list.
  163. X     * If s is not found, all the strings that s prefixes (if any)
  164. X     * will have indices >= lindex .
  165. X     */
  166. X    lindex = search_list(c_list, s, strcmp, &found);
  167. X    /*
  168. X     * Check for condition 4.  If we got a perfect match, skidaddle.
  169. X     */
  170. X    if (found) {
  171. X        (void) strcpy(delta, " ");
  172. X        (void) strcat(s, delta);
  173. X        *iscomplete = 1;
  174. X        return delta;
  175. X    }
  176. X    /*
  177. X     * Check for condition 5.  Hacksaw the garbage until we recognize
  178. X     * something.
  179. X     */
  180. X    n_matched = 0;
  181. X    for (i=lindex-1; i < lindex+2; i++) {
  182. X        if (i < 0)
  183. X            continue;
  184. X        if (i >= c_list->l_count)
  185. X            break;
  186. X        n_matched=max(n_matched, nmatch(s,(char *)c_list->l_list[i]));
  187. X        /*
  188. X         * If s prefixes c_list->l_list[lindex] or one of the words
  189. X         * adjacent to it, we set lindex to its index so that lindex
  190. X         * now indexes to the first word that s prefixes.
  191. X         */
  192. X        if (n_matched == s_len) {
  193. X            lindex = i;
  194. X            break;
  195. X        }
  196. X    }
  197. X    if (n_matched < s_len) {
  198. X        for (i=0; i < s_len - n_matched; i++)
  199. X            (void) strcat(delta, BACKSPACE);
  200. X        s[n_matched] = '\0';
  201. X        return delta;
  202. X    }
  203. X    /*
  204. X     * Check for condition 6.  If s can be unambigously expanded
  205. X     * then do so.
  206. X     */
  207. X    if (lindex+1 == c_list->l_count ||
  208. X        strncmp((char *)c_list->l_list[lindex],
  209. X            (char *)c_list->l_list[lindex+1], s_len)) {
  210. X        (void) strcpy(delta, ((char *)c_list->l_list[lindex])+s_len);
  211. X        (void) strcat(delta, " ");
  212. X        (void) strcat(s, delta);
  213. X        *iscomplete = 1;
  214. X        return delta;
  215. X    }
  216. X    /*
  217. X     * Gotta be condition 7.  Expand as far as possible, but
  218. X     * don't set *iscomplete.
  219. X     */
  220. X    for (i=lindex+1 ;; i++) {
  221. X        if (i == c_list->l_count) {
  222. X            break;
  223. X        }
  224. X        if (strncmp(s, (char *)c_list->l_list[i], s_len) != 0)
  225. X            break;
  226. X    }
  227. X    i--;
  228. X    n_matched = nmatch((char *)c_list->l_list[lindex],
  229. X               (char *)c_list->l_list[i]);
  230. X    (void) strncpy(delta, ((char *)c_list->l_list[lindex])+s_len,
  231. X        n_matched-s_len);
  232. X    delta[n_matched - s_len] = '\0';
  233. X    (void) strcat(s, delta);
  234. X    return delta;
  235. X}
  236. X
  237. Xredraw(prompt, s)
  238. Xchar *prompt, *s;
  239. X
  240. X{
  241. X    char_scr('\r');
  242. X    str_scr(prompt);
  243. X    str_scr(s);
  244. X    return;
  245. X}
  246. X
  247. X#include <ctype.h>
  248. X
  249. X/*
  250. X * If this variable is non-zero then it must contain the length of
  251. X * the latest visible completion help tag, e.g. "[Ambiguous]".
  252. X * If zero then there is no floating completion tag at this time.
  253. X */
  254. Xstatic int MustEraseTag;
  255. X
  256. Xchar
  257. XGET()
  258. X
  259. X{
  260. X    extern int errno;
  261. X    int count, nready, rmask = 1;
  262. X    struct timeval t, *timeout = 0;
  263. X    char c;
  264. X    
  265. X    errno = 0;
  266. X    for (;;) {
  267. X    rmask = 1;
  268. X    if (MustEraseTag) {
  269. X        t.tv_sec = 1;
  270. X        t.tv_usec = 250000;
  271. X        timeout = &t;
  272. X    }
  273. X#ifdef BSD4_3
  274. X    nready = select(1, (fd_set*)&rmask, (fd_set *)0, (fd_set *)0, timeout);
  275. X#else
  276. X    nready = select(1, &rmask, 0, 0, timeout);
  277. X#endif
  278. X    if (nready == -1 && errno != EINTR) {
  279. X        perr("select");
  280. X        panic((char *)0);
  281. X    }
  282. X    if (nready == 0 || MustEraseTag) {
  283. X        erasetag();
  284. X        timeout = 0;
  285. X        if (!nready)
  286. X          continue;
  287. X    }
  288. X    count = read(0, &c, 1);
  289. X    if (count == 1)
  290. X      break;
  291. X    if (count == 0)
  292. X      panic("EOF encountered");
  293. X    if (count == -1 && errno != EINTR) {
  294. X        perr("read");
  295. X        panic((char *)0);
  296. X    }
  297. X    }
  298. X    c &= 0177;
  299. X    return(c);
  300. X}
  301. X
  302. Xshowtag(tag)
  303. Xchar *tag;
  304. X
  305. X{
  306. X    register int i;
  307. X
  308. X    MustEraseTag = strlen(tag) + 1;
  309. X    char_scr(' ');
  310. X    str_scr(tag);
  311. X    for (i=0; i < MustEraseTag; i++)
  312. X      char_scr('\b');
  313. X    return;
  314. X}
  315. X
  316. Xerasetag()
  317. X
  318. X{
  319. X    register int i;
  320. X
  321. X    for (i=0; i < MustEraseTag; i++)
  322. X      char_scr(' ');
  323. X    for (i=0; i < MustEraseTag; i++)
  324. X      char_scr('\b');
  325. X    MustEraseTag = 0;
  326. X    return;
  327. X}
  328. X
  329. X/*
  330. X *  Figure out which argument vector to use for argument completion.
  331. X */
  332. Xstruct list *
  333. Xpicklist(cmd)
  334. Xchar *cmd;
  335. X
  336. X{
  337. X    cmd = tail(cmd);
  338. X    if (SCMPN("user", cmd))
  339. X        return(&Users);
  340. X    else if (SCMPN("sig", cmd))
  341. X        return(&Sigs);
  342. X    else if (SCMPN("class", cmd))
  343. X        return(&Classes);
  344. X    else if (SCMPN("group", cmd))
  345. X        return(&Groups);
  346. X    else if (SCMPN("range", cmd))
  347. X        return(&Ranges);
  348. X    else if (SCMPN("vig", cmd))
  349. X        return(&Vigs);
  350. X    else if (SCMPN("command", cmd))
  351. X        return(&AllCommands);
  352. X#ifdef SENDMAIL
  353. X    else if (SCMPN("alias", cmd))
  354. X        return(&Aliases);
  355. X#endif
  356. X#ifdef HELPDIR
  357. X    else if (SCMPN("is", cmd))
  358. X        return(&Terms);
  359. X#endif
  360. X    else
  361. X        return(&Null_List);
  362. X}
  363. X
  364. X#ifdef sun
  365. X#define    sighandler    (void (*)())
  366. X#else
  367. X#define    sighandler    (int (*)())
  368. X#endif
  369. X
  370. X#ifdef sun
  371. Xvoid
  372. X#endif
  373. Xtstp_cleanup()
  374. X
  375. X{
  376. X    char_scr('\r');
  377. X    nocbreak();
  378. X    (void) signal(SIGTSTP, sighandler SIG_DFL);
  379. X    (void) kill(getpid(), SIGTSTP);
  380. X    return;
  381. X}
  382. X
  383. X#ifdef sun
  384. Xvoid
  385. X#endif
  386. Xinput_continue()
  387. X
  388. X{
  389. X    cbreak();
  390. X    (void) signal(SIGTSTP, tstp_cleanup);
  391. X    longjmp(in_continue, SIGCONT);
  392. X}
  393. X
  394. X/*
  395. X *  Get a line of input using command and smart argument completion.
  396. X */
  397. XGetCommandLine(prompt, nargs, argc, argv)
  398. Xchar *prompt;
  399. Xint nargs, *argc;
  400. Xaddr *argv;
  401. X
  402. X{
  403. X    addr *tmpargv;
  404. X    char buf[LONG_BUF], *p;
  405. X    int c, iscomplete, indx, windex, hindex, i, end_of_line, h_len, d;
  406. X    int quote_open;
  407. X    struct list *c_list;
  408. X    struct hist *hi, hh;
  409. X    
  410. X    cbreak();
  411. X    MustEraseTag = 0;
  412. X    exprv[0] = buf;
  413. X    *argc = 0;
  414. X    *line = '\0';
  415. X    quote_open = end_of_line = windex = indx = 0;
  416. X    hindex = History.l_count;
  417. X    c_list = &Commands;
  418. X    (void) signal(SIGTSTP, tstp_cleanup);
  419. X    (void) signal(SIGCONT, input_continue);
  420. X    (void) setjmp(in_continue);
  421. X    str_scr(prompt);
  422. X    while (!end_of_line && indx < BUFSIZ) {
  423. X        c = GET();
  424. X        if (setjmp(in_continue) == SIGCONT) {
  425. X            redraw(prompt, line);
  426. X            continue;
  427. X        }
  428. X        switch (c) {
  429. X        case ':': 
  430. X            break;    /* ignore colons for pwd, group, etc. files */
  431. X        /*
  432. X         * ^P goes one step back in the history list.
  433. X         */
  434. X        case '\020':
  435. X            if (hindex == 0) {
  436. X                showtag("[History begins here...]");
  437. X                break;
  438. X            }
  439. X            if (hindex == History.l_count) {
  440. X                (void) strcpy(currh.h_line, line);
  441. X                currh.h_prompt = prompt;
  442. X                currh.h_argc = *argc;
  443. X                currh.h_index = indx;
  444. X                currh.h_windex = windex;
  445. X                currh.h_qopen = quote_open;
  446. X                currh.h_list = c_list;
  447. X            }
  448. X            hi = (struct hist *) History.l_list[--hindex];
  449. X            h_len = strlen(line) + strlen(prompt);
  450. X            for (i=strlen(hi->h_line); i < h_len; i++)
  451. X                str_scr(BACKSPACE);
  452. X            (void) strcpy(line, hi->h_line);
  453. X            prompt = hi->h_prompt;
  454. X            *argc = hi->h_argc;
  455. X            indx = hi->h_index;
  456. X            windex = hi->h_windex;
  457. X            quote_open = hi->h_qopen;
  458. X            c_list = hi->h_list;
  459. X            redraw(prompt, line);
  460. X            break;
  461. X        /*
  462. X         * ^N goes one step forward in the history list.
  463. X         */
  464. X        case '\016':
  465. X            if (!History.l_count || hindex == History.l_count) {
  466. X                showtag("[End of history]");
  467. X                break;
  468. X            }
  469. X            if (hindex == History.l_count-1) {
  470. X                h_len = strlen(line) + strlen(prompt);
  471. X                i=strlen(currh.h_line)+strlen(currh.h_prompt);
  472. X                for (; i < h_len; i++)
  473. X                    str_scr(BACKSPACE);
  474. X                (void) strcpy(line, currh.h_line);
  475. X                prompt = currh.h_prompt;
  476. X                *argc = currh.h_argc;
  477. X                indx = currh.h_index;
  478. X                windex = currh.h_windex;
  479. X                quote_open = currh.h_qopen;
  480. X                c_list = currh.h_list;
  481. X                redraw(prompt, line);
  482. X                hindex++;
  483. X                break;
  484. X            }
  485. X            hi = (struct hist *) History.l_list[++hindex];
  486. X            h_len = strlen(line) + strlen(prompt);
  487. X            i = strlen(hi->h_line) + strlen(hi->h_prompt);
  488. X            for (; i < h_len; i++)
  489. X                str_scr(BACKSPACE);
  490. X            (void) strcpy(line, hi->h_line);
  491. X            prompt = hi->h_prompt;
  492. X            *argc = hi->h_argc;
  493. X            indx = hi->h_index;
  494. X            windex = hi->h_windex;
  495. X            quote_open = hi->h_qopen;
  496. X            c_list = hi->h_list;
  497. X            redraw(prompt, line);
  498. X            break;
  499. X        /* 
  500. X         * For space bar, do completion only for the
  501. X         * command (first) word.  Allow normal spaces
  502. X         * only at the end of a word.
  503. X         */
  504. X        case ' ': 
  505. X            if (nargs && *argc == nargs) {
  506. X                showtag("[Press RETURN]");
  507. X                break;
  508. X            }
  509. X            if (windex != 0 && windex != indx) {
  510. X                char_scr(c);
  511. X                line[indx] = c;
  512. X                line[indx + 1] = '\0';
  513. X                indx++;
  514. X                if (!quote_open) {
  515. X                    windex = indx;
  516. X                    (*argc)++;
  517. X                }
  518. X                break;
  519. X            }
  520. X            else if (windex == indx) /* balk at adjacent spaces */
  521. X                break;
  522. X            else
  523. X                /* FALL THROUGH */
  524. X                    ;
  525. X        /* 
  526. X         * Word is completion done here.  Completion is activated
  527. X         * by the TAB or the ESC key.  Activation by CR or LF only
  528. X         * if completing first word.
  529. X         */
  530. X        case '\r':
  531. X        case '\n':
  532. X            if (*argc != 0) {
  533. X                end_of_line++;
  534. X                break;
  535. X            }
  536. X            if (nargs && *argc == nargs) {
  537. X                end_of_line++;
  538. X                break;
  539. X            }
  540. X            /* FALL THROUGH */
  541. X        case '\t': 
  542. X        case '\033': 
  543. X            if (nargs && *argc == nargs) {
  544. X                showtag("[Press RETURN]");
  545. X                break;
  546. X            }
  547. X            p = complete(&line[windex], c_list, &iscomplete);
  548. X            str_scr(p);
  549. X            if (iscomplete) {
  550. X                if (windex == 0)
  551. X                    c_list = picklist(line);
  552. X                windex = strlen(line);
  553. X                (*argc)++;
  554. X            }
  555. X            else if (*p == '\0' && indx != windex)
  556. X              showtag("[Ambiguous]");
  557. X            indx = strlen(line);
  558. X            if ((c == '\r' || c == '\n') && iscomplete)
  559. X                end_of_line++;
  560. X            break;
  561. X        /*
  562. X         * Ctrl-T transposes the two characters before the cursor
  563. X         */
  564. X        case '\024':
  565. X            if (nargs && *argc == nargs) {
  566. X                showtag("[Press RETURN]");
  567. X                break;
  568. X            }
  569. X            /* not enough characters */
  570. X            if (indx < 2)
  571. X                break;
  572. X            /* can't let space to be first character on a line */
  573. X            if (indx == 2 && line[1] == ' ')
  574. X                break;
  575. X            swap(line[indx-1], line[indx-2]);
  576. X            /*
  577. X             * If one of the transposed characters was a space,
  578. X             * we have either broken a word in two or merged
  579. X             * the current word with the preceding one.
  580. X             */
  581. X            if (line[indx-2] == ' ')
  582. X                if (!quote_open && line[indx-1] == '"') {
  583. X                (*argc)++, windex--;
  584. X                c_list = picklist(line);
  585. X                }
  586. X            else if (line[indx-1] == ' ')
  587. X                if (!quote_open && line[indx-2] == '"') {
  588. X                (*argc)--, windex++;
  589. X                c_list = (*argc == 0) ?
  590. X                    &Commands : picklist(line);
  591. X                }
  592. X            str_scr("\b \b\b \b");
  593. X            str_scr(&line[indx-2]);
  594. X            break;
  595. X        /* 
  596. X         * Show user possiblities if he wonders why
  597. X         * a word isn't completing.
  598. X         */
  599. X        case '?': 
  600. X            if (nargs && *argc == nargs) {
  601. X                showtag("[Press RETURN]");
  602. X                break;
  603. X            }
  604. X            str_scr("\r\n");
  605. X            (void) strcpy(buf, "^");
  606. X            (void) strcat(buf, &line[windex]);
  607. X            (void) strcat(buf, ".*");
  608. X            nocbreak();
  609. X            /*
  610. X             * We don't want completion info going to stdout
  611. X             * so we temporarily connect stdout to /dev/tty
  612. X             */
  613. X            d = dup(1);
  614. X            (void) dup2(DevTty, 1);
  615. X            (void) showlist(c_list, (addr *)exprv);
  616. X            /*
  617. X             * Now re-connect stdout to whatever is was before
  618. X             */
  619. X            (void) dup2(d, 1);
  620. X            (void) close(d);
  621. X
  622. X            cbreak();
  623. X            redraw(prompt, line);
  624. X            break;
  625. X        /* 
  626. X         * Ctrl-R simply reprints the command line.
  627. X         */
  628. X        case '\022': 
  629. X            redraw(prompt, line);
  630. X            break;
  631. X        /*
  632. X         * Ctrl-W is accepted as the word-kill character.
  633. X         */
  634. X        case '\027':
  635. X            if (indx == windex && windex != 0) {
  636. X                for (windex -= 2; windex >= 0; windex--) {
  637. X                    if (line[windex] == '"') {
  638. X                        quote_open = !quote_open;
  639. X                        continue;
  640. X                    }
  641. X                    if (!quote_open&& line[windex] == ' ')
  642. X                        break;
  643. X                }
  644. X                windex++;
  645. X                if (windex < 0)
  646. X                    windex = 0;
  647. X                decr(*argc);
  648. X            }
  649. X            if (windex == 0)
  650. X                c_list = &Commands;
  651. X            for (; indx > windex; indx--)
  652. X                str_scr(BACKSPACE);
  653. X            line[indx] = '\0';
  654. X            break;
  655. X        /* 
  656. X         * Backspace (^H) and del are accepted as erase
  657. X         * characters, with the screen eraseure being
  658. X         * accomplished via backspace-space-backpsace
  659. X         * sequences
  660. X         */
  661. X        case '\177':
  662. X        case '\b': 
  663. X            if (--indx < windex) {
  664. X                for (windex-=2; windex >= 0; windex--) {
  665. X                    if (line[windex] == '"') {
  666. X                        quote_open = !quote_open;
  667. X                        continue;
  668. X                    }
  669. X                    if (!quote_open&& line[windex] == ' ')
  670. X                        break;
  671. X                }
  672. X                windex++;
  673. X                if (windex < 0)
  674. X                    windex = 0;
  675. X                decr(*argc);
  676. X            }
  677. X            if (windex == 0)
  678. X                c_list = &Commands;
  679. X            if (indx >= 0) {
  680. X                if (line[indx] == '"')
  681. X                    quote_open = !quote_open;
  682. X                line[indx] = '\0';
  683. X                str_scr(BACKSPACE);
  684. X            }
  685. X            else
  686. X                indx = 0;
  687. X            break;
  688. X        /* 
  689. X         * Ctrl-X and ctrl-U are accepted as line kill
  690. X         * characters.
  691. X         */
  692. X        case '\030':
  693. X        case '\025':
  694. X            if (indx == 0)
  695. X                break;
  696. X            for (; indx>0; indx--)
  697. X                str_scr(BACKSPACE);
  698. X            quote_open = windex = indx = 0;
  699. X            *line = '\0';
  700. X            c_list = &Commands;
  701. X            *argc = 0;
  702. X            break;
  703. X        default: 
  704. X            if (nargs && *argc == nargs) {
  705. X                showtag("[Press RETURN]");
  706. X                break;
  707. X            }
  708. X            if (c == '"')
  709. X                quote_open = !quote_open;
  710. X            /* 
  711. X             * Ignore unrecognized control characters
  712. X             */
  713. X            if (isprint(c)) {
  714. X                char_scr(c);
  715. X                line[indx] = c;
  716. X                line[indx + 1] = '\0';
  717. X            indx++;
  718. X            }
  719. X        break;
  720. X        }
  721. X    }
  722. X    (void) signal(SIGCONT, sighandler SIG_DFL);
  723. X    (void) signal(SIGTSTP, sighandler SIG_DFL);
  724. X    if (quote_open) {
  725. X        char_scr('"');
  726. X        line[indx++] = '"';
  727. X        line[indx] = '\0';
  728. X        quote_open = 0;
  729. X    }
  730. X
  731. X    critical();
  732. X
  733. X    savestr(&hh.h_prompt, prompt);
  734. X    savestr(&hh.h_line, line);
  735. X    hh.h_argc = *argc;
  736. X    hh.h_index = indx;
  737. X    hh.h_windex = windex;
  738. X    hh.h_qopen = quote_open;
  739. X    hh.h_list = c_list;
  740. X    trimhist();
  741. X    genlistadd(&History, (addr)&hh, sizeof (struct hist));
  742. X
  743. X    non_critical();
  744. X
  745. X    if (indx > windex)
  746. X        (*argc)++;
  747. X    (void) cut(line);
  748. X    tmpargv = mkargv(line, *argc);
  749. X    for (i=0; tmpargv[i]; i++)
  750. X        savestr((char **)&argv[i], (char *)tmpargv[i]);
  751. X    argv[i] = NIL;
  752. X    str_scr("\r\n");
  753. X    nocbreak();
  754. X    return;
  755. X}
  756. X
  757. XGetLine(prompt, nargs, argc, argv, c_list)
  758. Xchar *prompt;
  759. Xint nargs, *argc;
  760. Xaddr *argv;
  761. Xstruct list *c_list;
  762. X
  763. X{
  764. X    addr *tmpargv;
  765. X    char buf[LONG_BUF], *p;
  766. X    int c, iscomplete, indx, windex, i, d, quote_open;
  767. X
  768. X    cbreak();
  769. X    exprv[0] = buf;
  770. X    MustEraseTag = 0;
  771. X    *argc = quote_open = 0;
  772. X    *line = '\0';
  773. X    windex = indx = 0;
  774. X    (void) signal(SIGTSTP, tstp_cleanup);
  775. X    (void) signal(SIGCONT, input_continue);
  776. X    (void) setjmp(in_continue);
  777. X    str_scr(prompt);
  778. X    while ((c = GET()) != '\n' && indx < BUFSIZ) {
  779. X        if (setjmp(in_continue) == SIGCONT) {
  780. X            redraw(prompt, line);
  781. X            continue;
  782. X        }
  783. X        if (c == '\r') 
  784. X            break;
  785. X        switch ( c ) {
  786. X        case ':':
  787. X            break;    /* ignore colons for pwd, group, etc. files */
  788. X        case ' ':
  789. X            if (nargs && *argc == nargs) {
  790. X                showtag("[Press RETURN]");
  791. X                break;
  792. X            }
  793. X            if (windex != indx) {
  794. X                char_scr(c);
  795. X                line[indx] = c;
  796. X                line[indx+1] = '\0';
  797. X                indx++;
  798. X                if (!quote_open) {
  799. X                    windex = indx;
  800. X                    (*argc)++;
  801. X                }
  802. X            }
  803. X            break;
  804. X        case '\t':
  805. X        case '\033':
  806. X            if (nargs && *argc == nargs) {
  807. X                showtag("[Press RETURN]");
  808. X                break;
  809. X            }
  810. X            p = complete(&line[windex], c_list, &iscomplete);
  811. X            str_scr(p);
  812. X            if (iscomplete) {
  813. X                windex = strlen(line);
  814. X                (*argc)++;
  815. X            }
  816. X            else if (*p == '\b')
  817. X              showtag("[No match]");
  818. X            else if (*p == '\0' && indx != windex)
  819. X              showtag("[Ambiguous]");
  820. X            indx = strlen(line);
  821. X            break;
  822. X        /*
  823. X         * Ctrl-T transposes the two characters before the cursor
  824. X         */
  825. X        case '\024':
  826. X            if (nargs && *argc == nargs) {
  827. X                showtag("[Press RETURN]");
  828. X                break;
  829. X            }
  830. X            if (indx < 2)
  831. X                break;
  832. X            if (indx == 2 && line[1] == ' ')
  833. X                break;
  834. X            swap(line[indx-1], line[indx-2]);
  835. X            /*
  836. X             * If one of the transposed characters was a space,
  837. X             * we have either broken a word in two or merged
  838. X             * the current word with the preceding one.
  839. X             */
  840. X            if (line[indx-2] == ' ')
  841. X                if (!quote_open && line[indx-1] == '"')
  842. X                (*argc)++, windex--;
  843. X            else if (line[indx-1] == ' ')
  844. X                if (!quote_open && line[indx-2] == '"')
  845. X                (*argc)--, windex++;
  846. X            str_scr("\b \b\b \b");
  847. X            str_scr(&line[indx-2]);
  848. X            break;
  849. X        /* 
  850. X         * Show user possiblities if he wonders why
  851. X         * a word isn't completing.
  852. X         */
  853. X        case '?':
  854. X            if (nargs && *argc == nargs) {
  855. X                showtag("[Press RETURN]");
  856. X                break;
  857. X            }
  858. X            str_scr("\r\n");
  859. X            (void) strcpy(buf, "^");
  860. X            (void) strcat(buf, &line[windex]);
  861. X            (void) strcat(buf, ".*");
  862. X            nocbreak();
  863. X            /*
  864. X             * We don't want completion info going to stdout
  865. X             * so we temporarily connect stdout to /dev/tty
  866. X             */
  867. X            d = dup(1);
  868. X            (void) dup2(DevTty, 1);
  869. X            (void) showlist(c_list, (addr *)exprv);
  870. X            /*
  871. X             * Now re-connect stdout to whatever is was before
  872. X             */
  873. X            (void) dup2(d, 1);
  874. X            (void) close(d);
  875. X
  876. X            cbreak();
  877. X            redraw(prompt, line);
  878. X            break;
  879. X        /* 
  880. X         * Ctrl-R simply reprints the command line.
  881. X         */
  882. X        case '\022':
  883. X            redraw(prompt, line);
  884. X            break;
  885. X        /*
  886. X         * Ctrl-W is accepted as the word-kill character.
  887. X         */
  888. X        case '\027':
  889. X            if (indx == windex && windex != 0) {
  890. X                for (windex -= 2; windex >= 0; windex--) {
  891. X                    if (line[windex] == '"') {
  892. X                        quote_open = !quote_open;
  893. X                        continue;
  894. X                    }
  895. X                    if (!quote_open&& line[windex] == ' ')
  896. X                        break;
  897. X                }
  898. X                windex++;
  899. X                if (windex < 0)
  900. X                    windex = 0;
  901. X                decr(*argc);
  902. X            }
  903. X            for (; indx > windex; indx--)
  904. X                str_scr(BACKSPACE);
  905. X            line[indx] = '\0';
  906. X            break;
  907. X        /* 
  908. X         * Backspace (^H) and del are accepted as erase
  909. X         * characters, with the screen erasure being
  910. X         * accomplished via backspace-space-backpsace
  911. X         * sequences
  912. X         */
  913. X        case '\177':
  914. X        case '\b':
  915. X            if ( --indx < windex ) {
  916. X                for (windex-=2; windex >= 0; windex--) {
  917. X                    if (line[windex] == '"') {
  918. X                        quote_open = !quote_open;
  919. X                        continue;
  920. X                    }
  921. X                    if (!quote_open&& line[windex] == ' ')
  922. X                        break;
  923. X                }
  924. X                windex++;
  925. X                if (windex < 0)
  926. X                    windex = 0;
  927. X                decr(*argc);
  928. X            }
  929. X            if (indx >= 0) {
  930. X                if (line[indx] == '"')
  931. X                    quote_open = !quote_open;
  932. X                line[indx] = '\0';
  933. X                str_scr(BACKSPACE);
  934. X            }
  935. X            else
  936. X                indx = 0;
  937. X            break;
  938. X        /* 
  939. X         * Ctrl-X and ctrl-U are accepted as line kill
  940. X         * characters.
  941. X         */
  942. X        case '\030':
  943. X        case '\025':
  944. X            if (indx == 0)
  945. X                break;
  946. X            for (; indx>0; indx--)
  947. X                str_scr(BACKSPACE);
  948. X            quote_open = windex = indx = 0;
  949. X            *line = '\0';
  950. X            *argc = 0;
  951. X            break;
  952. X         default:
  953. X            if (nargs && *argc == nargs) {
  954. X                showtag("[Press RETURN]");
  955. X                break;
  956. X            }
  957. X            if (c == '"')
  958. X                quote_open = !quote_open;
  959. X            /* 
  960. X             * Ignore unrecognized control characters
  961. X             */
  962. X            if (isprint(c)) {
  963. X                char_scr(c);
  964. X                line[indx] = c;
  965. X                line[indx+1] = '\0';
  966. X                indx++;
  967. X            }
  968. X            break;
  969. X        }
  970. X    }
  971. X    (void) signal(SIGCONT, sighandler SIG_DFL);
  972. X    (void) signal(SIGTSTP, sighandler SIG_DFL);
  973. X    if (quote_open) {
  974. X        char_scr('"');
  975. X        line[indx++] = '"';
  976. X        line[indx] = '\0';
  977. X    }
  978. X    if (indx > windex)
  979. X        (*argc)++;
  980. X    (void) cut(line);
  981. X    tmpargv = mkargv(line, *argc);
  982. X    for (i=0; tmpargv[i]; i++)
  983. X        savestr((char **)&argv[i], (char *)tmpargv[i]);
  984. X    argv[i] = NIL;
  985. X    str_scr("\r\n");
  986. X    nocbreak();
  987. X    return;
  988. X}
  989. X
  990. Xtrimhist()
  991. X
  992. X{
  993. X    struct hist *h;
  994. X
  995. X    if (History.l_count <= MAXHIST) return;
  996. X    h = (struct hist *) listpop(&History);
  997. X    FREEMEM(h->h_line);
  998. X    FREEMEM((char *)h);
  999. X    return;
  1000. X}
  1001. X
  1002. XGetFilenames(prompt, nargs, argc, argv)
  1003. Xchar *prompt;
  1004. Xint nargs, *argc;
  1005. Xaddr *argv;
  1006. X
  1007. X{
  1008. X    addr *tmpargv;
  1009. X    char buf[LONG_BUF], *p;
  1010. X    int c, iscomplete, indx, windex, i, d, quote_open;
  1011. X    static struct list c_list;
  1012. X
  1013. X    exprv[0] = buf;
  1014. X    MustEraseTag = 0;
  1015. X    *argc = 0;
  1016. X    *line = '\0';
  1017. X    quote_open = windex = indx = 0;
  1018. X    zerolist(&c_list);
  1019. X    tmplistadd(&c_list);
  1020. X    cbreak();
  1021. X    (void) signal(SIGTSTP, tstp_cleanup);
  1022. X    (void) signal(SIGCONT, input_continue);
  1023. X    (void) setjmp(in_continue);
  1024. X    str_scr(prompt);
  1025. X    while ((c = GET()) != '\n' && indx < BUFSIZ) {
  1026. X        if (setjmp(in_continue) == SIGCONT) {
  1027. X            redraw(prompt, line);
  1028. X            continue;
  1029. X        }
  1030. X        if (c == '\r') 
  1031. X            break;
  1032. X        switch ( c ) {
  1033. X        case ' ':
  1034. X            if (nargs && *argc == nargs) {
  1035. X                showtag("[Press RETURN]");
  1036. X                break;
  1037. X            }
  1038. X            if (windex != indx) {
  1039. X                char_scr(c);
  1040. X                line[indx] = c;
  1041. X                line[indx+1] = '\0';
  1042. X                indx++;
  1043. X                if (!quote_open) {
  1044. X                    windex = indx;
  1045. X                    (*argc)++;
  1046. X                }
  1047. X            }
  1048. X            break;
  1049. X        case '\t':
  1050. X        case '\033':
  1051. X            if (nargs && *argc == nargs) {
  1052. X                showtag("[Press RETURN]");
  1053. X                break;
  1054. X            }
  1055. X            p = pathcomplete(&line[windex], &c_list, &iscomplete);
  1056. X            str_scr(p);
  1057. X            if (iscomplete) {
  1058. X                windex = strlen(line);
  1059. X                (*argc)++;
  1060. X            }
  1061. X            else if (*p == '\0' && indx != windex)
  1062. X              showtag("[Ambiguous]");
  1063. X            indx = strlen(line);
  1064. X            break;
  1065. X        /*
  1066. X         * Ctrl-T transposes the two characters before the cursor
  1067. X         */
  1068. X        case '\024':
  1069. X            if (nargs && *argc == nargs) {
  1070. X                showtag("[Press RETURN]");
  1071. X                break;
  1072. X            }
  1073. X            if (indx < 2)
  1074. X                break;
  1075. X            if (indx == 2 && line[1] == ' ')
  1076. X                break;
  1077. X            swap(line[indx-1], line[indx-2]);
  1078. X            /*
  1079. X             * If one of the transposed characters was a space,
  1080. X             * we have either broken a word in two or merged
  1081. X             * the current word with the preceding one.
  1082. X             */
  1083. X            if (line[indx-2] == ' ')
  1084. X                if (!quote_open && line[indx-1] == '"')
  1085. X                (*argc)++, windex--;
  1086. X            else if (line[indx-1] == ' ')
  1087. X                if (!quote_open && line[indx-2] == '"')
  1088. X                (*argc)--, windex++;
  1089. X            str_scr("\b \b\b \b");
  1090. X            str_scr(&line[indx-2]);
  1091. X            break;
  1092. X        /* 
  1093. X         * Show user possiblities if he wonders why
  1094. X         * a word isn't completing.
  1095. X         */
  1096. X        case '?':
  1097. X            if (nargs && *argc == nargs) {
  1098. X                showtag("[Press RETURN]");
  1099. X                break;
  1100. X            }
  1101. X            str_scr("\r\n");
  1102. X            freelist(&c_list);
  1103. X            dirscan(dirof(&line[windex]), &c_list);
  1104. X            (void) strcpy(buf, "^");
  1105. X            (void) strcat(buf, fileof(&line[windex]));
  1106. X            (void) strcat(buf, ".*");
  1107. X            nocbreak();
  1108. X            /*
  1109. X             * We don't want completion info going to stdout
  1110. X             * so we temporarily connect stdout to /dev/tty
  1111. X             */
  1112. X            d = dup(1);
  1113. X            (void) dup2(DevTty, 1);
  1114. X            (void) showlist(&c_list, (addr *)exprv);
  1115. X            /*
  1116. X             * Now re-connect stdout to whatever is was before
  1117. X             */
  1118. X            (void) dup2(d, 1);
  1119. X            (void) close(d);
  1120. X
  1121. X            cbreak();
  1122. X            redraw(prompt, line);
  1123. X            break;
  1124. X        /* 
  1125. X         * Ctrl-R simply reprints the command line.
  1126. X         */
  1127. X        case '\022':
  1128. X            redraw(prompt, line);
  1129. X            break;
  1130. X        /*
  1131. X         * Ctrl-W is accepted as the word-kill character.
  1132. X         */
  1133. X        case '\027':
  1134. X            if (indx == windex && windex != 0) {
  1135. X                for (windex -= 2; windex >= 0; windex--) {
  1136. X                    if (line[windex] == '"') {
  1137. X                        quote_open = !quote_open;
  1138. X                        continue;
  1139. X                    }
  1140. X                    if (!quote_open&& line[windex] == ' ')
  1141. X                        break;
  1142. X                }
  1143. X                windex++;
  1144. X                if (windex < 0)
  1145. X                    windex = 0;
  1146. X                decr(*argc);
  1147. X            }
  1148. X            for (; indx > windex; indx--)
  1149. X                str_scr(BACKSPACE);
  1150. X            line[indx] = '\0';
  1151. X            break;
  1152. X        /* 
  1153. X         * Backspace (^H) and del are accepted as erase
  1154. X         * characters, with the screen erasure being
  1155. X         * accomplished via backspace-space-backpsace
  1156. X         * sequences
  1157. X         */
  1158. X        case '\177':
  1159. X        case '\b':
  1160. X            if ( --indx < windex ) {
  1161. X                for (windex-=2; windex >= 0; windex--) {
  1162. X                    if (line[windex] == '"') {
  1163. X                        quote_open = !quote_open;
  1164. X                        continue;
  1165. X                    }
  1166. X                    if (!quote_open&& line[windex] == ' ')
  1167. X                        break;
  1168. X                }
  1169. X                windex++;
  1170. X                if (windex < 0)
  1171. X                    windex = 0;
  1172. X                decr(*argc);
  1173. X            }
  1174. X            if (indx >= 0) {
  1175. X                if (line[indx] == '"')
  1176. X                    quote_open = !quote_open;
  1177. X                line[indx] = '\0';
  1178. X                str_scr(BACKSPACE);
  1179. X            }
  1180. X            else
  1181. X                indx = 0;
  1182. X            break;
  1183. X        /* 
  1184. X         * Ctrl-X and ctrl-U are accepted as line kill
  1185. X         * characters.
  1186. X         */
  1187. X        case '\030':
  1188. X        case '\025':
  1189. X            if (indx == 0)
  1190. X                break;
  1191. X            for (; indx>0; indx--)
  1192. X                str_scr(BACKSPACE);
  1193. X            quote_open = windex = indx = 0;
  1194. X            *line = '\0';
  1195. X            *argc = 0;
  1196. X            break;
  1197. X         default:
  1198. X            if (nargs && *argc == nargs) {
  1199. X                showtag("[Press RETURN]");
  1200. X                break;
  1201. X            }
  1202. X            if (c == '"')
  1203. X                quote_open = !quote_open;
  1204. X            /* 
  1205. X             * Ignore unrecognized control characters
  1206. X             */
  1207. X            if (isprint(c)) {
  1208. X                char_scr(c);
  1209. X                line[indx] = c;
  1210. X                line[indx+1] = '\0';
  1211. X                indx++;
  1212. X            }
  1213. X            break;
  1214. X        }
  1215. X    }
  1216. X    (void) signal(SIGCONT, sighandler SIG_DFL);
  1217. X    (void) signal(SIGTSTP, sighandler SIG_DFL);
  1218. X    if (quote_open) {
  1219. X        char_scr('"');
  1220. X        line[indx++] = '"';
  1221. X        line[indx] = '\0';
  1222. X    }
  1223. X    if (indx > windex)
  1224. X        (*argc)++;
  1225. X    (void) cut(line);
  1226. X    tmpargv = mkargv(line, *argc);
  1227. X    for (i=0; tmpargv[i]; i++)
  1228. X        savestr((char **)&argv[i], (char *)tmpargv[i]);
  1229. X    argv[i] = NIL;
  1230. X    str_scr("\r\n");
  1231. X    nocbreak();
  1232. X    return;
  1233. X}
  1234. X
  1235. Xchar *
  1236. Xpathcomplete(path, c_list, iscomplete)
  1237. Xchar *path;
  1238. Xstruct list *c_list;
  1239. Xint *iscomplete;
  1240. X
  1241. X{
  1242. X    static char delta[LONG_BUF];
  1243. X    char buf[LONG_BUF], *dir, *file;
  1244. X    int n_matched, d_len, i;
  1245. X
  1246. X    delta[0] = '\0';
  1247. X    (void) strcpy(buf, path);
  1248. X
  1249. X    /*
  1250. X     * If we can't chdir to the directory prefix of the path, then
  1251. X     * we hack away parts of the path until we can or it's all
  1252. X     * gone.  Note that this depends on the fact within UNIX "\0"
  1253. X     * is synonymous with "."
  1254. X     */
  1255. X    dir = dirof(buf);
  1256. X    if (chdir(dir) == -1) {
  1257. X        diraxe(buf);
  1258. X        while (*buf && chdir(buf) == -1)
  1259. X            diraxe(buf);
  1260. X        (void) getwd(buf);
  1261. X        /*
  1262. X         * If this isn't the root directory, add a slash
  1263. X         */
  1264. X        if (!eq(buf, "/"))
  1265. X            (void) strcat(buf, "/");
  1266. X        /* note changes and store necessary cursor motions */
  1267. X        n_matched = nmatch(path, buf);
  1268. X        d_len = strlen(path);
  1269. X        for (i=d_len; i > n_matched; i--)
  1270. X            (void) strcat(delta, BACKSPACE);
  1271. X        (void) strcat(delta, buf+n_matched);
  1272. X        (void) strcpy(path, buf);
  1273. X        (void) chdir(Working_Directory);
  1274. X        *iscomplete = 0;
  1275. X        return delta;
  1276. X    }
  1277. X    /*
  1278. X     * Chdir'ed successfully so now we make the completion list.
  1279. X     */
  1280. X    freelist(c_list);
  1281. X    dirscan(".", c_list);
  1282. X    /* do completion of the file suffix of the path */
  1283. X    file = fileof(path);
  1284. X    (void) complete(file, c_list, iscomplete);
  1285. X    /*
  1286. X     * Shed . , .. and symlinks
  1287. X     */
  1288. X    (void) getwd(buf);
  1289. X    /*
  1290. X     * If this isn't the root directory, add a slash
  1291. X     */
  1292. X    if (!eq(buf, "/"))
  1293. X        (void) strcat(buf, "/");
  1294. X    /*
  1295. X     * Add the result of file name completion
  1296. X     */
  1297. X    (void) strcat(buf, file);
  1298. X    /*
  1299. X     * Now note the difference between the what was passed in and
  1300. X     * what we have now and put the necessary cursor movements into
  1301. X     * delta[]
  1302. X     */
  1303. X    n_matched = nmatch(path, buf);
  1304. X    d_len = strlen(path);
  1305. X    for (i=d_len; i > n_matched; i--)
  1306. X        (void) strcat(delta, BACKSPACE);
  1307. X    (void) strcat(delta, buf+n_matched);
  1308. X    /* get rid of side effects */
  1309. X    (void) chdir(Working_Directory);
  1310. X    /* save results */
  1311. X    (void) strcpy(path, buf);
  1312. X    /*
  1313. X     * If the filename completed was in fact a directory then we append
  1314. X     * a slash to the file name and note that the completion didn't
  1315. X     * result in a regular file name (*iscomplete = 0).
  1316. X     */
  1317. X    if (*iscomplete) {
  1318. X        d_len = strlen(path) - 1;
  1319. X        path[d_len] = '/';
  1320. X        if (isdir(path)) {
  1321. X            delta[strlen(delta)-1] = '/';
  1322. X            *iscomplete = 0;
  1323. X            return delta;
  1324. X        }
  1325. X        path[d_len] = ' ';
  1326. X    }
  1327. X    return delta;
  1328. X}
  1329. X
  1330. Xchar *
  1331. Xdirof(path)
  1332. Xchar *path;
  1333. X
  1334. X{
  1335. X    register char *cp;
  1336. X    static char buf[LONG_BUF];
  1337. X
  1338. X    (void) strcpy(buf, path);
  1339. X    cp = rindex(buf, '/');
  1340. X    if (cp)
  1341. X        *++cp = '\0';
  1342. X    else
  1343. X        *buf = '\0';
  1344. X    return buf;
  1345. X}
  1346. X
  1347. Xchar *
  1348. Xfileof(path)
  1349. Xchar *path;
  1350. X
  1351. X{
  1352. X    register char *cp;
  1353. X    static char buf[LONG_BUF];
  1354. X
  1355. X    (void) strcpy(buf, path);
  1356. X    cp = rindex(buf, '/');
  1357. X    return cp ? cp+1 : buf;
  1358. X}
  1359. X
  1360. X/*
  1361. X * Hack off the last directory in a path.  The path should end with '/'
  1362. X * for this to work properly.
  1363. X */
  1364. Xdiraxe(path)
  1365. Xchar *path;
  1366. X
  1367. X{
  1368. X    register char *cp;
  1369. X
  1370. X    cp = rindex(path, '/');
  1371. X    if (cp)
  1372. X        *cp = '\0';
  1373. X    else {
  1374. X        *path = '\0';
  1375. X        return;
  1376. X    }
  1377. X    cp = rindex(path, '/');
  1378. X    if (cp)
  1379. X        *++cp = '\0';
  1380. X    else
  1381. X        *path = '\0';
  1382. X    return;
  1383. X}
  1384. @//E*O*F src/complete.c//
  1385. if test 29213 -ne "`wc -c <'src/complete.c'`"; then
  1386.     echo shar: error transmitting "'src/complete.c'" '(should have been 29213 characters)'
  1387. fi
  1388. fi # end of overwriting check
  1389. echo shar: extracting "'src/describe.c'" '(14405 characters)'
  1390. if test -f 'src/describe.c' ; then 
  1391.   echo shar: will not over-write existing file "'src/describe.c'"
  1392. else
  1393. sed 's/^X//' >src/describe.c <<'@//E*O*F src/describe.c//'
  1394. X#include <stdio.h>
  1395. X#include <strings.h>
  1396. X#include <sys/types.h>
  1397. X#include <sys/file.h>
  1398. X#include <sys/stat.h>
  1399. X#include <lastlog.h>
  1400. X#include "sysdep.h"
  1401. X#include "macros.h"
  1402. X#include "mem.h"
  1403. X#include "lists.h"
  1404. X#include "job.h"
  1405. X#include "account.h"
  1406. X#ifdef SENDMAIL
  1407. X#include "alias.h"
  1408. X#endif
  1409. X#include "class.h"
  1410. X#include "groupmap.h"
  1411. X#include "range.h"
  1412. X#include "sig.h"
  1413. X#include "save.h"
  1414. X#include "sort.h"
  1415. X
  1416. X#ifdef BSD4_3
  1417. Xtime_t    time();
  1418. X#endif
  1419. X#ifdef HELPDIR
  1420. Xextern    struct list AllCommands, Terms;
  1421. X#endif
  1422. X#ifdef SENDMAIL
  1423. Xextern    struct list AliasList;
  1424. X#endif
  1425. Xextern    struct list AccountList, Jobs, RangeList;
  1426. Xextern    int ModBits;
  1427. X
  1428. Xchar    *when(), *sprintf();
  1429. X
  1430. X#ifdef SENDMAIL
  1431. Xdesalias(c, v)
  1432. Xint c;
  1433. Xaddr *v;
  1434. X
  1435. X{
  1436. X    struct alias *al;
  1437. X    static char *allv[2] = { ".*", 0 };
  1438. X
  1439. X    if (c > 2) {
  1440. X        err1("%s: too many arguments", (char *)v[0]);
  1441. X        return;
  1442. X    }
  1443. X    if (c < 2) {
  1444. X        err1("usage: %s <alias>", (char *)v[0]);
  1445. X        return;
  1446. X    }
  1447. X    al = getalnam((char *)v[1]);
  1448. X    if (!al) {
  1449. X        err1("%s: no such alias", (char *)v[1]);
  1450. X        return;
  1451. X    }
  1452. X
  1453. X    (void) printf("Name: %s\n", al->al_name);
  1454. X    if (al->al_groups.l_count) {
  1455. X        (void) printf("Bound to group%-2s: ",
  1456. X            S(al->al_groups.l_count));
  1457. X        listlist(&al->al_groups);
  1458. X    }
  1459. X    if (al->al_classes.l_count) {
  1460. X        (void) printf("Bound to class%-2s: ", ES(al->al_classes.l_count));
  1461. X        listlist(&al->al_classes);
  1462. X    }
  1463. X    if (al->al_sigs.l_count) {
  1464. X        (void) printf("Bound to sig%-4s: ",
  1465. X            S(al->al_sigs.l_count));
  1466. X        listlist(&al->al_sigs);
  1467. X    }
  1468. X    if (al->al_addresses.l_count) {
  1469. X        puts("\t- Addressees -");
  1470. X        (void) showlist(&al->al_addresses, (addr *)allv);
  1471. X        (void) printf("%d addressee%s\n", al->al_addresses.l_count,
  1472. X            S(al->al_addresses.l_count));
  1473. X    }
  1474. X    else
  1475. X        puts("No addressees.");
  1476. X    return;
  1477. X}
  1478. X#endif
  1479. X
  1480. Xdeschanges(c, v)
  1481. Xint c;
  1482. Xaddr *v;
  1483. X
  1484. X{
  1485. X    struct job *jb;
  1486. X    char errmsg[LONG_BUF];
  1487. X    int first, indx;
  1488. X
  1489. X    first = (c > 1 ? Jobs.l_count - atoi((char *)v[1]) : 0);
  1490. X    for (indx=first; indx < Jobs.l_count; indx++) {
  1491. X        jb = (struct job *) Jobs.l_list[indx];
  1492. X        (void) printf("%3d ", indx+1);
  1493. X        switch (jb->jb_todo) {
  1494. X        case JB_LASTLOG:
  1495. X            (void) printf("update lastlog entry for uid %d\n",
  1496. X                jb->jb_uid);
  1497. X            break;
  1498. X        case JB_MKDIR:
  1499. X            (void) printf("mkdir %s\n", jb->jb_name);
  1500. X            break;
  1501. X        case JB_MV:
  1502. X            (void) printf("rename %s to %s\n", jb->jb_oldname,
  1503. X                jb->jb_name);
  1504. X            break;
  1505. X        case JB_RMMAIL:
  1506. X            (void) printf("remove mail for user \"%s\"\n",
  1507. X                jb->jb_name);
  1508. X            break;
  1509. X        case JB_OMNICHOWN:
  1510. X            (void) printf("omnichown uid %d's files to uid %d\n",
  1511. X                jb->jb_olduid, jb->jb_uid);
  1512. X            break;
  1513. X        case JB_RMDIR:
  1514. X            (void) printf("remove directory %s\n", jb->jb_name);
  1515. X            break;
  1516. X        default:
  1517. X            (void) sprintf(errmsg,
  1518. X                "internal error: unknown todo (%d)\n",
  1519. X                jb->jb_todo);
  1520. X            err(errmsg);
  1521. X            break;
  1522. X        }
  1523. X    }
  1524. X    if (ModBits) {
  1525. X        fputs("Files modified:", stdout);
  1526. X        (ModBits&AC) && fputs(" account", stdout);
  1527. X#ifdef SENDMAIL
  1528. X        (ModBits&AL) && fputs(" alias", stdout);
  1529. X#endif
  1530. X        (ModBits&CS) && fputs(" class", stdout);
  1531. X        (ModBits&GR) && fputs(" group", stdout);
  1532. X        (ModBits&PW) && fputs(" passwd", stdout);
  1533. X        (ModBits&RG) && fputs(" range", stdout);
  1534. X        (ModBits&SG) && fputs(" sig", stdout);
  1535. X        (ModBits&VG) && fputs(" vig", stdout);
  1536. X        puts("");
  1537. X    }
  1538. X}
  1539. X
  1540. Xdesclass(c, v)
  1541. Xint c;
  1542. Xaddr *v;
  1543. X
  1544. X{
  1545. X    struct class *cs;
  1546. X    struct account *ac;
  1547. X    int indx, members = 0;
  1548. X
  1549. X    if ( c > 2 ) {
  1550. X        err1("%s: too many arguments", (char *)v[0]);
  1551. X        return;
  1552. X    }
  1553. X    if ( c != 2 ) {
  1554. X        err1("usage: %s <class>", (char *)v[0]);
  1555. X        return;
  1556. X    }
  1557. X    cs = getcsnam((char *)v[1]);
  1558. X    if (!cs) {
  1559. X        err1("%s: no such class", (char *)v[1]);
  1560. X        return;
  1561. X    }
  1562. X    (void) printf("Class: %s\n", cs->cs_name);
  1563. X    if (cs->cs_exptime) (void) printf("Ends: %s\n", when(cs->cs_exptime));
  1564. X#ifdef SENDMAIL
  1565. X    if (cs->cs_aliases.l_count) {
  1566. X        (void) printf("Bound to alias%s: ", ES(cs->cs_aliases.l_count));
  1567. X        listlist(&cs->cs_aliases);
  1568. X    }
  1569. X#endif
  1570. X    puts((char *)cs->cs_desc);
  1571. X    for (indx=0; indx < AccountList.l_count; indx++) {
  1572. X        ac = (struct account *) AccountList.l_list[indx];
  1573. X        if (!instrlist(&ac->ac_classes, cs->cs_name))
  1574. X            continue;
  1575. X        (void) printf("%-10s%-40s%3d",    ac->ac_name,
  1576. X                    ac->ac_realname,
  1577. X                    ac->ac_uid);
  1578. X        if (ac->ac_ll.ll_time)
  1579. X            puts("   *");
  1580. X        else
  1581. X            puts("");
  1582. X        members++;
  1583. X    }
  1584. X    if (members)
  1585. X        (void) printf("\n%d member%s.\n", members, S(members));
  1586. X    else
  1587. X        puts("No current members.");
  1588. X    return;
  1589. X}
  1590. X
  1591. Xdescryos(c, v)
  1592. Xint c;
  1593. Xchar **v;
  1594. X
  1595. X{
  1596. X    struct account *ac;
  1597. X    int indx, cryos = 0;
  1598. X
  1599. X    if ( c > 1 ) {
  1600. X        err1("%s: too many arguments", (char *)v[0]);
  1601. X        return;
  1602. X    }
  1603. X    for (indx=0; indx < AccountList.l_count; indx++) {
  1604. X        ac = (struct account *) AccountList.l_list[indx];
  1605. X        if (!eq(ac->ac_shell, FREEZE_SH))
  1606. X            continue;
  1607. X        (void) printf("%-10s%-40s%3d",    ac->ac_name,
  1608. X                    ac->ac_realname,
  1609. X                    ac->ac_uid);
  1610. X        if (ac->ac_ll.ll_time)
  1611. X            puts("   *");
  1612. X        else
  1613. X            puts("");
  1614. X        cryos++;
  1615. X    }
  1616. X    if (cryos)
  1617. X        (void) printf("\n%d cryo%s.\n", cryos, S(cryos));
  1618. X    else
  1619. X        puts("No cryos.");
  1620. X    return;
  1621. X}
  1622. X
  1623. X#ifdef HELPDIR
  1624. Xchar    *getenv();
  1625. X
  1626. Xdescommand(c, v)
  1627. Xint c;
  1628. Xaddr *v;
  1629. X
  1630. X{
  1631. X    char *pager = getenv("PAGER");
  1632. X    char *pname, helpfile[MEDIUM_BUF];
  1633. X    char *av[4];
  1634. X    struct stat statbuf;
  1635. X
  1636. X    if ( c > 2 ) {
  1637. X        err1("%s: too many arguments", (char *)v[0]);
  1638. X        return;
  1639. X    }
  1640. X    if ( c < 2 ) {
  1641. X        err1("usage: %s <mcp command>", (char *)v[0]);
  1642. X        return;
  1643. X    }
  1644. X    if (!instrlist(&AllCommands, (char *)v[1])) {
  1645. X        err2("%s: %s is not an mcp command",
  1646. X            (char *)v[0], (char *)v[1]);
  1647. X        return;
  1648. X    }
  1649. X    if (chdir(HELPDIR) == -1) {
  1650. X        perr(HELPDIR);
  1651. X        return;
  1652. X    }
  1653. X    (void) strcpy(helpfile, (char *)v[1]);
  1654. X    (void) strcat(helpfile, ".k");
  1655. X    if (stat(helpfile, &statbuf) == -1) {
  1656. X        err1("No help available for \"%s\"", (char *)v[1]);
  1657. X        return;
  1658. X    }
  1659. X    if (statbuf.st_size == 0) {
  1660. X        err1("Help file for \"%s\" is empty, (oh, well)",
  1661. X            (char *)v[1]);
  1662. X        return;
  1663. X    }
  1664. X    if (!pager)
  1665. X        pager = DEF_PAGER;
  1666. X    pname = rindex(pager, '/') + 1;
  1667. X    pname = (pname ? pname : pager);
  1668. X
  1669. X    av[0] = "shell-escape";        /* not really necessary */
  1670. X    av[1] = pager;
  1671. X    av[2] = helpfile;
  1672. X    av[3] = (char *)0;
  1673. X    (void) shellescape(3, (addr *)av);
  1674. X    return;
  1675. X}
  1676. X#endif
  1677. X
  1678. Xdesdeadbeats(c, v)
  1679. Xint c;
  1680. Xchar **v;
  1681. X
  1682. X{
  1683. X    struct account *ac;
  1684. X    struct groupmap *gm;
  1685. X    int indx, deadbeats = 0;
  1686. X    char errmsg[LONG_BUF];
  1687. X
  1688. X    if ( c > 1 ) {
  1689. X        err1("%s: too many arguments", (char *)v[0]);
  1690. X        return;
  1691. X    }
  1692. X    for (indx=0; indx < AccountList.l_count; indx++) {
  1693. X        ac = (struct account *) AccountList.l_list[indx];
  1694. X        if (ac->ac_classes.l_count)
  1695. X            continue;
  1696. X        if (ac->ac_sigs.l_count)
  1697. X            continue;
  1698. X        /*
  1699. X         * Cryos are not deadbeats.
  1700. X         */
  1701. X        if (eq(ac->ac_shell, FREEZE_SH))
  1702. X            continue;
  1703. X        gm = getgmgid(ac->ac_gid);
  1704. X        if (!gm) {
  1705. X            (void) sprintf(errmsg,
  1706. X                    "no group for gid %d!",
  1707. X                    ac->ac_gid);
  1708. X            err(errmsg);
  1709. X            return;
  1710. X        }
  1711. X        if (vigexists(gm->gm_name))
  1712. X            continue;
  1713. X        (void) printf("%-10s%-40s%3d",    ac->ac_name,
  1714. X                    ac->ac_realname,
  1715. X                    ac->ac_uid);
  1716. X        if (ac->ac_ll.ll_time)
  1717. X            puts("   *");
  1718. X        else
  1719. X            puts("");
  1720. X        deadbeats++;
  1721. X    }
  1722. X    if (deadbeats)
  1723. X        (void) printf("\n%d deadbeat%s.\n",
  1724. X                deadbeats, S(deadbeats));
  1725. X    else
  1726. X        puts("No deadbeats.");
  1727. X    return;
  1728. X}
  1729. X
  1730. Xdesgroup(c, v)
  1731. Xint c;
  1732. Xaddr *v;
  1733. X
  1734. X{
  1735. X    struct groupmap *gm;
  1736. X    struct range *rg;
  1737. X    struct account *ac;
  1738. X    static struct list members;
  1739. X    static char *allv[2] = { ".*", 0 };
  1740. X    int indx, vig = 0;
  1741. X
  1742. X    if ( c > 2 ) {
  1743. X        err1("%s: too many arguments", (char *)v[0]);
  1744. X        return;
  1745. X    }
  1746. X    if (c != 2) {
  1747. X        err1("usage: %s <group>", (char *)v[0]);
  1748. X        return;
  1749. X    }
  1750. X    gm = getgmnam((char *)v[1]);
  1751. X    if (!gm) {
  1752. X        err1("%s: no such group", (char *)v[1]);
  1753. X        return;
  1754. X    }
  1755. X    rg = getrgnam((char *)v[1]);
  1756. X    if (vigexists((char *)v[1]))
  1757. X        vig++;
  1758. X    zerolist(&members);
  1759. X    tmplistadd(&members);
  1760. X    for (indx=0; indx < AccountList.l_count; indx++) {
  1761. X        ac = (struct account *) AccountList.l_list[indx];
  1762. X        if (ac->ac_gid == gm->gm_gid)
  1763. X            strlistadd(&members, (char *)ac->ac_name);
  1764. X    }
  1765. X    (void) printf("Group: %s (%u)%s\n", gm->gm_name, gm->gm_gid, 
  1766. X            vig?" VIG":"");
  1767. X    if (rg) {
  1768. X        (void) printf("uid range: %d-%d  ", rg->rg_from,
  1769. X            rg->rg_to);
  1770. X        puts(rg->rg_mode == RG_SHARED ? "shared" : "exclusive");
  1771. X    }
  1772. X#ifdef SENDMAIL
  1773. X    if (gm->gm_aliases.l_count) {
  1774. X        (void) printf("Bound to alias%s: ", ES(gm->gm_aliases.l_count));
  1775. X        listlist(&gm->gm_aliases);
  1776. X    }
  1777. X#endif
  1778. X    if (members.l_count) {
  1779. X        puts("\t- Members -");
  1780. X        sort_list(&members, pstrcmp);
  1781. X        (void) showlist(&members, (addr *)allv);
  1782. X    }
  1783. X    if (gm->gm_mem.l_count) {
  1784. X        puts("\t- Groupies -");
  1785. X        (void) showlist(&gm->gm_mem, (addr *)allv);
  1786. X    }
  1787. X    if (!members.l_count && !gm->gm_mem.l_count)
  1788. X        puts("No current members or groupies.");
  1789. X    else {
  1790. X        if (members.l_count)
  1791. X            (void) printf("%d member%s", members.l_count,
  1792. X                S(members.l_count));
  1793. X        else
  1794. X            (void) printf("No members");
  1795. X        if (gm->gm_mem.l_count)
  1796. X            (void) printf(", %d groupie%s\n", gm->gm_mem.l_count,
  1797. X                S(gm->gm_mem.l_count));
  1798. X        else
  1799. X            puts(", no groupies.");
  1800. X    }
  1801. X    freelist(&members);
  1802. X    return;
  1803. X}
  1804. X
  1805. Xdesinactives(c, v)
  1806. Xint c;
  1807. Xaddr *v;
  1808. X
  1809. X{
  1810. X    struct account *ac;
  1811. X    struct groupmap *gm;
  1812. X    int indx, inactives = 0, days;
  1813. X    time_t now;
  1814. X    long toolong;
  1815. X    char errmsg[LONG_BUF];
  1816. X
  1817. X    if ( c > 2 ) {
  1818. X        err1("%s: too many arguments", (char *)v[0]);
  1819. X        return;
  1820. X    }
  1821. X    if ( c < 2 ) {
  1822. X        err1("usage: %s <days>", (char *)v[0]);
  1823. X        return;
  1824. X    }
  1825. X    if (!validint((char *)v[1])) {
  1826. X        err2("%s: %s doesn't make sense as a number", (char *)v[0],
  1827. X            (char *)v[1]);
  1828. X        return;
  1829. X    }
  1830. X    now = time((time_t *)0);
  1831. X    days = atoi((char *)v[1]);
  1832. X    toolong = days * 86400;
  1833. X
  1834. X    for (indx=0; indx < AccountList.l_count; indx++) {
  1835. X        ac = (struct account *) AccountList.l_list[indx];
  1836. X        if ((long)(now - ac->ac_ll.ll_time) < toolong)
  1837. X            continue;
  1838. X        /*
  1839. X         * Cryos are not inactive.
  1840. X         */
  1841. X        if (eq(ac->ac_shell, FREEZE_SH))
  1842. X            continue;
  1843. X        /*
  1844. X         * Vig members are not inactive.
  1845. X         */
  1846. X        gm = getgmgid(ac->ac_gid);
  1847. X        if (!gm) {
  1848. X            (void) sprintf(errmsg,
  1849. X                    "no group for gid %d!",
  1850. X                    ac->ac_gid);
  1851. X            err(errmsg);
  1852. X            return;
  1853. X        }
  1854. X        if (vigexists(gm->gm_name))
  1855. X            continue;
  1856. X        (void) printf("%-10s%-40s%3d",    ac->ac_name,
  1857. X                    ac->ac_realname,
  1858. X                    ac->ac_uid);
  1859. X        if (ac->ac_ll.ll_time)
  1860. X            puts("   *");
  1861. X        else
  1862. X            puts("");
  1863. X        inactives++;
  1864. X    }
  1865. X    if (inactives)
  1866. X        (void) printf("\n%d user%s inactive for at least %d day%s.\n",
  1867. X            inactives, S(inactives),
  1868. X            days, S(days));
  1869. X    else
  1870. X        (void) printf("No users inactive for %d day%s.\n",
  1871. X            days, S(days));
  1872. X    return;
  1873. X}
  1874. X
  1875. Xdesrange(c, v)
  1876. Xint c;
  1877. Xaddr *v;
  1878. X
  1879. X{
  1880. X    struct range *rg;
  1881. X
  1882. X    if ( c > 2 ) {
  1883. X        err1("%s: too many arguments", (char *)v[0]);
  1884. X        return;
  1885. X    }
  1886. X    if (c != 2) {
  1887. X        err1("usage: %s <range name>", (char *)v[0]);
  1888. X        return;
  1889. X    }
  1890. X    rg = getrgnam((char *)v[1]);
  1891. X    if (!rg) {
  1892. X        err1("%s: no such range", (char *)v[1]);
  1893. X        return;
  1894. X    }
  1895. X    (void) printf("%-16s %d to %d    %s\n", rg->rg_name, rg->rg_from,
  1896. X        rg->rg_to,
  1897. X        (rg->rg_mode == RG_SHARED ? "shared" : "exclusive"));
  1898. X    return;
  1899. X}
  1900. X
  1901. Xdessig(c, v)
  1902. Xint c;
  1903. Xaddr *v;
  1904. X
  1905. X{
  1906. X    struct sig *sg;
  1907. X    struct account *ac;
  1908. X    int indx, members = 0;
  1909. X
  1910. X    if ( c > 2 ) {
  1911. X        err1("%s: too many arguments", (char *)v[0]);
  1912. X        return;
  1913. X    }
  1914. X    if ( c != 2 ) {
  1915. X        err1("usage: %s <sig>", (char *)v[0]);
  1916. X        return;
  1917. X    }
  1918. X    sg = getsgnam((char *)v[1]);
  1919. X    if (!sg) {
  1920. X        err1("%s: no such sig", (char *)v[1]);
  1921. X        return;
  1922. X    }
  1923. X    (void) printf("Sig: %s\n", sg->sg_name);
  1924. X    if (sg->sg_exptime) (void) printf("Expires: %s\n",
  1925. X                        when(sg->sg_exptime));
  1926. X#ifdef SENDMAIL
  1927. X    if (sg->sg_aliases.l_count) {
  1928. X        (void) printf("Bound to alias%s:", ES(sg->sg_aliases.l_count));
  1929. X        listlist(&sg->sg_aliases);
  1930. X    }
  1931. X#endif
  1932. X    puts((char *)sg->sg_desc);
  1933. X    for (indx=0; indx < AccountList.l_count; indx++) {
  1934. X        ac = (struct account *) AccountList.l_list[indx];
  1935. X        if (!instrlist(&ac->ac_sigs, sg->sg_name))
  1936. X            continue;
  1937. X        (void) printf("%-10s%-40s%3d",    ac->ac_name,
  1938. X                    ac->ac_realname,
  1939. X                    ac->ac_uid);
  1940. X        if (ac->ac_ll.ll_time)
  1941. X            puts("   *");
  1942. X        else
  1943. X            puts("");
  1944. X        members++;
  1945. X    }
  1946. X    if (members)
  1947. X        (void) printf("\n%d member%s.\n", members, S(members));
  1948. X    else
  1949. X        puts("No current members.");
  1950. X    return;
  1951. X}
  1952. X
  1953. Xdesuser(c, v)
  1954. Xint c;
  1955. Xaddr *v;
  1956. X
  1957. X{
  1958. X#ifdef SENDMAIL
  1959. X    static struct list mal;
  1960. X    struct alias *al;
  1961. X    register int j;
  1962. X#endif
  1963. X    struct account *ac;
  1964. X    struct groupmap *gm;
  1965. X    char *shell, errmsg[LONG_BUF];
  1966. X
  1967. X    if ( c > 2 ) {
  1968. X        err1("%s: too many arguments", (char *)v[0]);
  1969. X        return;
  1970. X    }
  1971. X    if ( c != 2 ) {
  1972. X        err1("usage: %s <user>", (char *)v[0]);
  1973. X        return;
  1974. X    }
  1975. X
  1976. X    ac = getacnam((char *)v[1]);
  1977. X    if (!ac) {
  1978. X        err1("%s: no such user", (char *)v[1]);
  1979. X        return;
  1980. X    }
  1981. X    gm = getgmgid(ac->ac_gid);
  1982. X    if (!gm) {
  1983. X        (void) sprintf(errmsg, "no group name for gid %d!",
  1984. X                ac->ac_gid);
  1985. X        err(errmsg);
  1986. X        return;
  1987. X    }
  1988. X    if (strlen((char *)ac->ac_shell) == 0)
  1989. X        shell = "/bin/sh";
  1990. X    else
  1991. X        shell = (char *)ac->ac_shell;
  1992. X#ifdef SENDMAIL
  1993. X    /*
  1994. X     * Get the names of the aliases this user is in for later use
  1995. X     */
  1996. X    zerolist(&mal);
  1997. X    tmplistadd(&mal);
  1998. X    for (j=0; j < AliasList.l_count; j++) {
  1999. X        al = (struct alias *) AliasList.l_list[j];
  2000. X        if (instrlist(&al->al_addresses, (char *)ac->ac_name))
  2001. X        strlistadd(&mal, (char *)al->al_name);
  2002. X    }
  2003. X#endif
  2004. X    (void) printf("Login:   %s (%d)\n", ac->ac_name, ac->ac_uid);
  2005. X    (void) printf("Name:    %s (%s)\n", ac->ac_realname, ac->ac_gecos);
  2006. X    (void) printf("Id:      %s\n", ac->ac_id);
  2007. X    (void) printf("Groups:  %s ", gm->gm_name);
  2008. X    listlist(&ac->ac_groups);
  2009. X    fputs("Classes: ", stdout);
  2010. X    listlist(&ac->ac_classes);
  2011. X    fputs("Sigs:    ", stdout);
  2012. X    listlist(&ac->ac_sigs);
  2013. X#ifdef SENDMAIL
  2014. X    fputs("Aliases: ", stdout);
  2015. X    listlist(&mal);
  2016. X#endif
  2017. X    (void) printf("Home:    %s\n", ac->ac_dir);
  2018. X    (void) printf("Shell:   %s\n", shell);
  2019. X    if (ac->ac_ll.ll_time) {
  2020. X        (void) printf("Last login %s on %s", when(ac->ac_ll.ll_time),
  2021. X            ac->ac_ll.ll_line);
  2022. X        if (ac->ac_ll.ll_host[0] != '\0')
  2023. X            (void) printf(" from %s", ac->ac_ll.ll_host);
  2024. X        puts("");
  2025. X    }
  2026. X    else
  2027. X        puts("Never logged in.");
  2028. X    return;
  2029. X}
  2030. X
  2031. X#ifdef HELPDIR
  2032. Xwhatis(c, v)
  2033. Xint c;
  2034. Xaddr *v;
  2035. X
  2036. X{
  2037. X    char *pager = getenv("PAGER");
  2038. X    char *pname, helpfile[MEDIUM_BUF];
  2039. X    char *av[4];
  2040. X    struct stat statbuf;
  2041. X
  2042. X    if ( c > 2 ) {
  2043. X        err1("%s: too many arguments", (char *)v[0]);
  2044. X        return;
  2045. X    }
  2046. X    if ( c != 2 ) {
  2047. X        err1("usage: %s <mcp term>", (char *)v[0]);
  2048. X        return;
  2049. X    }
  2050. X    if (!instrlist(&Terms, (char *)v[1])) {
  2051. X        err2("%s: %s is not an mcp term",
  2052. X            (char *)v[0], (char *)v[1]);
  2053. X        return;
  2054. X    }
  2055. X
  2056. X    if (chdir(HELPDIR) == -1) {
  2057. X        perr(HELPDIR);
  2058. X        return;
  2059. X    }
  2060. X    (void) strcpy(helpfile, (char *)v[1]);
  2061. X    (void) strcat(helpfile, ".k");
  2062. X    if (stat(helpfile, &statbuf) == -1) {
  2063. X        err1("No definition file for \"%s\"", (char *)v[1]);
  2064. X        return;
  2065. X    }
  2066. X    if (statbuf.st_size == 0) {
  2067. X        err1("Definition file for \"%s\" is empty, (alas and alack!)",
  2068. X            (char *)v[1]);
  2069. X        return;
  2070. X    }
  2071. X    if (!pager)
  2072. X        pager = DEF_PAGER;
  2073. X    pname = rindex(pager, '/') + 1;
  2074. X    pname = (pname ? pname : pager);
  2075. X
  2076. X    av[0] = "shell-escape";        /* not really necessary */
  2077. X    av[1] = pager;
  2078. X    av[2] = helpfile;
  2079. X    av[3] = (char *)0;
  2080. X    (void) shellescape(3, (addr *)av);
  2081. X    return;
  2082. X}
  2083. X#endif
  2084. @//E*O*F src/describe.c//
  2085. if test 14405 -ne "`wc -c <'src/describe.c'`"; then
  2086.     echo shar: error transmitting "'src/describe.c'" '(should have been 14405 characters)'
  2087. fi
  2088. fi # end of overwriting check
  2089. echo shar: extracting "'src/lastlog.h'" '(116 characters)'
  2090. if test -f 'src/lastlog.h' ; then 
  2091.   echo shar: will not over-write existing file "'src/lastlog.h'"
  2092. else
  2093. sed 's/^X//' >src/lastlog.h <<'@//E*O*F src/lastlog.h//'
  2094. Xstruct lastlog {
  2095. X    time_t    ll_time;
  2096. X    char    ll_line[8];
  2097. X    char    ll_host[16];
  2098. X};
  2099. X
  2100. Xstruct lastlog *getlluid(), *getllent();
  2101. @//E*O*F src/lastlog.h//
  2102. if test 116 -ne "`wc -c <'src/lastlog.h'`"; then
  2103.     echo shar: error transmitting "'src/lastlog.h'" '(should have been 116 characters)'
  2104. fi
  2105. fi # end of overwriting check
  2106. echo shar: extracting "'src/lists.h'" '(294 characters)'
  2107. if test -f 'src/lists.h' ; then 
  2108.   echo shar: will not over-write existing file "'src/lists.h'"
  2109. else
  2110. sed 's/^X//' >src/lists.h <<'@//E*O*F src/lists.h//'
  2111. X/* if l_spacefor == 0 malloc for this times sizeof (int *) */
  2112. X#define    STARTSIZE    8
  2113. X
  2114. Xstruct list {
  2115. X    int    l_count;    /* number of elements in list */
  2116. X    int    l_spacefor;    /* how many elements there are room for */
  2117. X    addr    *l_list;    /* array of pointers to elements */
  2118. X};
  2119. X
  2120. Xaddr    *mkargv(), glob(), listpop();
  2121. @//E*O*F src/lists.h//
  2122. if test 294 -ne "`wc -c <'src/lists.h'`"; then
  2123.     echo shar: error transmitting "'src/lists.h'" '(should have been 294 characters)'
  2124. fi
  2125. fi # end of overwriting check
  2126. echo shar: extracting "'src/pause.c'" '(25 characters)'
  2127. if test -f 'src/pause.c' ; then 
  2128.   echo shar: will not over-write existing file "'src/pause.c'"
  2129. else
  2130. sed 's/^X//' >src/pause.c <<'@//E*O*F src/pause.c//'
  2131. Xpausemcp()
  2132. X
  2133. X{
  2134. X    tstp();
  2135. X}
  2136. @//E*O*F src/pause.c//
  2137. if test 25 -ne "`wc -c <'src/pause.c'`"; then
  2138.     echo shar: error transmitting "'src/pause.c'" '(should have been 25 characters)'
  2139. fi
  2140. fi # end of overwriting check
  2141. echo shar: "End of archive 5 (of 8)."
  2142. cp /dev/null ark5isdone
  2143. DONE=true
  2144. for I in 1 2 3 4 5 6 7 8; do
  2145.     if test -! f ark${I}isdone; then
  2146.         echo "You still need to run archive ${I}."
  2147.         DONE=false
  2148.     fi
  2149. done
  2150. case $DONE in
  2151.     true)
  2152.         echo "You have run all 8 archives."
  2153.         echo 'See the README file'
  2154.         ;;
  2155. esac
  2156. ##  End of shell archive.
  2157. exit 0
  2158.