home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume7 / pt < prev    next >
Encoding:
Text File  |  1989-07-27  |  17.8 KB  |  785 lines

  1. Newsgroups: comp.sources.misc
  2. organization: Florida State University
  3. subject: v07i106: pt - show process family tree
  4. from: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  5. Reply-To: bauer@loligo.cc.fsu.edu (Jeff Bauer)
  6.  
  7. Posting-number: Volume 7, Issue 106
  8. Submitted-by: bauer@loligo.cc.fsu.edu (Jeff Bauer)
  9. Archive-name: pt
  10.  
  11. Here's a simple utility that displays the hierarchy of the process
  12. structure on a UNIX system.  I must admit, however, that "ps" output
  13. on System V is cleaner in terms of white space between columns
  14. than bsd systems and "pt" does a better job there.
  15.  
  16. Just delete everything above and include the "cut here" line and
  17. run it through "sh".
  18.  
  19.                 -- Jeff Bauer
  20.  
  21. -- cut here -- cut here -- cut here -- cut here -- cut here --
  22. #!/bin/sh
  23. # shar:    Shell Archiver
  24. #    Run the following text with /bin/sh to create:
  25. #    Makefile
  26. #    pt.1
  27. #    pt.1.cat
  28. #    pt.c
  29. sed 's/^X//' << 'SHAR_EOF' > Makefile
  30. X#
  31. X# Comment out the next line if you are building for System V
  32. X#
  33. XDEFINES=-DBSD
  34. XCFLAGS=-O
  35. X
  36. Xpt:    pt.c
  37. X    $(CC) $@.c -o $@ $(CFLAGS) $(DEFINES)
  38. SHAR_EOF
  39. sed 's/^X//' << 'SHAR_EOF' > pt.1
  40. X.TH PT 1
  41. X.SH NAME
  42. X\fBpt\fR \- display process family tree
  43. X.SH USAGE
  44. X.B pt [-a] [user]
  45. X.SH DESCRIPTION
  46. XThe 
  47. X.I pt
  48. Xcommand is a convenient alternative to using ps(1) to display a list
  49. Xof user processes.  Instead of showing a list of processes in the order
  50. Xthat they appear in the system process table,
  51. X.I pt
  52. Xdisplays processes in order of their family hierarchy.  The PID of
  53. Xchildren processes are indented to show the relationship between a
  54. Xparent and child process.
  55. X.sp
  56. XWith no parameters specified,
  57. X.I pt
  58. Xwill display only those processes not owned by "root".  If
  59. X.I \-a
  60. Xis specified,
  61. X.I pt
  62. Xwill display all processes.  If
  63. X.I user
  64. Xis specified,
  65. X.I pt
  66. Xwill display only processes owned by the user name specified.
  67. X.sp
  68. XThe column headings are somewhat self-explanatory; for further
  69. Xdetails see ps(1).
  70. X.SH EXAMPLE
  71. X.sp
  72. X$ pt carr
  73. X.br
  74. XPID                  User     Time    Pages  S  Command
  75. X.br
  76. X 8618                carr     0:00        8 (S) -ksh
  77. X.br
  78. X  8724               carr     0:00        8 (S) -ksh
  79. X.br
  80. X   8756              carr    16:13      479 (R) vlad6
  81. X.SH CAVEATS
  82. XWhen a user name is specified on the command line sometimes
  83. X.I pt
  84. Xcannot determine the correct lineage of a process.  Any of these
  85. Xorphan processes will appear under the heading of "Orphans" on
  86. Xthe output.
  87. X.sp
  88. XSince
  89. X.I pt
  90. Xreally is a filter from a forked-off ps(1) sometimes the parsing of
  91. Xthe output from ps(1) fails and the fields get garbled.
  92. X.SH SEE ALSO
  93. X.BR ps(1).
  94. SHAR_EOF
  95. sed 's/^X//' << 'SHAR_EOF' > pt.1.cat
  96. XPT(1)                     USER COMMANDS                     PT(1)
  97. X
  98. X
  99. X
  100. XNAME
  101. X     pt - display process family tree
  102. X
  103. XUSAGE
  104. X     pt [-a] [user]
  105. X
  106. XDESCRIPTION
  107. X     The _p_t command is a convenient alternative to using ps(1) to
  108. X     display a list of user processes.  Instead of showing a list
  109. X     of processes in the order that they  appear  in  the  system
  110. X     process  table, _p_t displays processes in order of their fam-
  111. X     ily hierarchy.  The PID of children processes  are  indented
  112. X     to show the relationship between a parent and child process.
  113. X
  114. X     With no parameters specified, _p_t  will  display  only  those
  115. X     processes  not owned by "root".  If -_a is specified, _p_t will
  116. X     display all  processes.   If  _u_s_e_r  is  specified,  _p_t  will
  117. X     display only processes owned by the user name specified.
  118. X
  119. X     The  column  headings  are  somewhat  self-explanatory;  for
  120. X     further details see ps(1).
  121. X
  122. XEXAMPLE
  123. X     $ pt carr
  124. X     PID                  User     Time    Pages  S  Command
  125. X      8618                carr     0:00        8 (S) -ksh
  126. X       8724               carr     0:00        8 (S) -ksh
  127. X        8756              carr    16:13      479 (R) vlad6
  128. X
  129. XCAVEATS
  130. X     When a user name is specified on the command line  sometimes
  131. X     _p_t  cannot  determine the correct lineage of a process.  Any
  132. X     of these orphan processes will appear under the  heading  of
  133. X     "Orphans" on the output.
  134. X
  135. X     Since _p_t really is a filter from a  forked-off  ps(1)  some-
  136. X     times  the  parsing  of  the output from ps(1) fails and the
  137. X     fields get garbled.
  138. X
  139. XSEE ALSO
  140. X     ps(1).
  141. SHAR_EOF
  142. sed 's/^X//' << 'SHAR_EOF' > pt.c
  143. X/*
  144. X    pt.c    - show process family tree
  145. X
  146. X    pt [-a] [user]
  147. X
  148. X    Default compilation is for System V UNIX.
  149. X    To compile for a bsd system try :
  150. X
  151. X    cc pt.c -o pt -O -DBSD
  152. X                            */
  153. X
  154. X#include <stdio.h>
  155. X#include <fcntl.h>
  156. X#include <string.h>
  157. X#ifdef    BSD
  158. X#include <pwd.h>
  159. X#include <ctype.h>
  160. X#endif    BSD
  161. X
  162. X#define    MAXLINE    256
  163. X#define    MYBUFSZ    256
  164. X#define    STDIN    0
  165. X#define    STDOUT    1
  166. X#define    STDERR    2
  167. X
  168. Xchar line[MAXLINE], token[MAXLINE];
  169. Xint firstr, cur_char, cur_row, cur_col;
  170. X
  171. X#define    TRUE    1
  172. X#define    FALSE    0
  173. X#define    UNDEF    2
  174. X
  175. Xstruct psl {
  176. X    char state[8];        /* S */
  177. X    char user[9];        /* UID */
  178. X    unsigned int pid;    /* PID */
  179. X    unsigned int ppid;    /* PPID */
  180. X    unsigned int pages;    /* SZ */
  181. X    char time[12];        /* TIME */
  182. X    char verb[32];        /* COMD */
  183. X};
  184. X
  185. Xstruct plist {
  186. X    struct psl cp;
  187. X    struct plist *s;        /* sibling */
  188. X    struct plist *c;        /* child */
  189. X};
  190. X
  191. Xstruct psl cp;
  192. Xstruct plist *proot;
  193. Xstruct plist *orphans;
  194. Xint orphcnt;
  195. Xint showroot = FALSE;
  196. X#ifdef    BSD
  197. Xstruct passwd *pe;
  198. Xextern int errno;
  199. X#endif    BSD
  200. X
  201. Xextern char *malloc();
  202. Xvoid strcpy2(), cp_plist(), pprint();
  203. Xint pinsert();
  204. Xstruct plist *pfind();
  205. X
  206. Xvoid main(argc, argv)
  207. Xint argc;
  208. Xchar **argv;
  209. X{
  210. X    int gls;
  211. X    int pfds[2];
  212. X    char who[9];
  213. X    int slen, wsp;
  214. X    char tbuf[32], tbuf2[32];
  215. X#ifdef    BSD
  216. X    char d1[32], d2[32], d3[32], d4[32], d5[32], d6[32], d7[32];
  217. X#endif
  218. X    struct plist *child;
  219. X    int fostered;
  220. X    int offset;
  221. X    int adopted;
  222. X    int i, j;
  223. X
  224. X    if (argc == 1) who[0] = '\0';
  225. X
  226. X    while (argc--) {
  227. X      if (argc != 0) {
  228. X        if (strcmp(argv[argc], "-a") == 0)
  229. X          showroot = TRUE;
  230. X        else
  231. X          strcpy2(who, argv[argc], 8);
  232. X      }
  233. X    }
  234. X    if (argc > 3) {
  235. X      fprintf(stderr,"Usage : %s [-a] [user]\n", argv[0]);
  236. X      exit(1);
  237. X    }
  238. X    if (strcmp(who, "root") == 0)
  239. X      showroot = TRUE;
  240. X
  241. X    orphcnt = 0;
  242. X
  243. X    if (pipe(pfds) < 0) {
  244. X        perror("pipe");
  245. X        exit(1);
  246. X    }
  247. X
  248. X    if (fork() == 0) {        /* BEGIN child processing */
  249. X      if (close(STDOUT) < 0) {    /* release fd 1 */
  250. X        perror("close1");
  251. X        exit(1);
  252. X      }
  253. X      if (dup(pfds[1]) < 0) {    /* re-use fd 1 */
  254. X        perror("dup1");
  255. X        exit(1);
  256. X      }
  257. X      if (close(pfds[1]) < 0) {
  258. X        perror("close2");
  259. X        exit(1);
  260. X      }
  261. X      if (close(pfds[0]) < 0) {
  262. X        perror("close3");
  263. X        exit(1);
  264. X      }
  265. X      if (who[0] == '\0')
  266. X#ifdef    BSD
  267. X        execl("/bin/ps","ps","-axlw",0);
  268. X#else
  269. X        execl("/bin/ps","ps","-elf",0);
  270. X#endif    BSD
  271. X      else 
  272. X#ifdef    BSD
  273. X        execl("/bin/ps","ps","-axlw",0);    /* no single-user filter on bsd "ps" */
  274. X#else
  275. X        execl("/bin/ps","ps","-u",who,"-lf",0);
  276. X#endif    BSD
  277. X    }                /* END child processing */
  278. X
  279. X    if (close(STDIN) < 0) {        /* release fd 0 */
  280. X      perror("close4");
  281. X      exit(1);
  282. X    }
  283. X    if (dup(pfds[0]) < 0) {        /* re-use fd 0 */
  284. X      perror("dup2");
  285. X      exit(1);
  286. X    }
  287. X    if (close(pfds[0]) < 0) {
  288. X      perror("close5");
  289. X      exit(1);
  290. X    }
  291. X    if (close(pfds[1]) < 0) {
  292. X      perror("close6");
  293. X      exit(1);
  294. X    }
  295. X
  296. X    firstr = TRUE;
  297. X    cur_row = 0;
  298. X    while ((gls = getline(STDIN, line, MAXLINE)) >= 0) {
  299. X      if (gls == -2) break;
  300. X      cur_char = 0;
  301. X      cur_col = 0;
  302. X      offset = FALSE;
  303. X#ifdef    BSD
  304. X      d7[0] = '\0';
  305. X#endif    BSD
  306. X      while (get_token(line, token, MAXLINE, &wsp) >= 0) {
  307. X        if (cur_row > 0) {        /* skip column headings */
  308. X          switch (cur_col) {
  309. X#ifdef    BSD
  310. X        case 0 :
  311. X          if (((wsp > 0) && (strlen(token) > 5)) ||
  312. X            ((wsp == 0) && (strlen(token) > 10))) {
  313. X            j = 0;
  314. X            for (i = (7 - wsp); i < strlen(token); i++)
  315. X              tbuf[j++] = token[i];
  316. X            tbuf[j+1] = '\0';
  317. X            strcpy(token, tbuf);
  318. X            cur_col += 1;
  319. X            sscanf(token, "%d", &i);
  320. X            if ((pe = getpwuid(i)) != NULL)
  321. X              strcpy2(cp.user, pe->pw_name, 8);
  322. X            else {
  323. X              strcpy2(cp.user, "(bogus)", 8);
  324. X              fprintf(stderr,"1: Checking UID %d : ",i);
  325. X              perror("getpwuid");
  326. X            }
  327. X          }
  328. X          else if ((wsp == 6) && (strlen(token) > 1)) {
  329. X            cur_col += 1;
  330. X            sscanf(token, "%d", &i);
  331. X            if ((pe = getpwuid(i)) != NULL)
  332. X              strcpy2(cp.user, pe->pw_name, 8);
  333. X            else {
  334. X              strcpy2(cp.user, "(bogus)", 8);
  335. X              fprintf(stderr,"2: Checking UID %d : ",i);
  336. X              perror("getpwuid");
  337. X            }
  338. X          }
  339. X          break;      
  340. X        case 1 :
  341. X          sscanf(token, "%d", &i);
  342. X          if ((pe = getpwuid(i)) != NULL)
  343. X            strcpy2(cp.user, pe->pw_name, 8);
  344. X          else {
  345. X            strcpy2(cp.user, "(bogus)", 8);
  346. X            fprintf(stderr,"3: Checking UID %d : ",i);
  347. X            perror("getpwuid");
  348. X          }        
  349. X          break;
  350. X        case 2 :
  351. X          sscanf(token, "%d", &cp.pid);
  352. X          break;
  353. X        case 3 :
  354. X          sscanf(token, "%d", &cp.ppid);
  355. X          break;
  356. X        case 7 :
  357. X          if (strlen(token) > 4)
  358. X            offset = TRUE;
  359. X          break;
  360. X        case 8 :
  361. X          if (offset == TRUE) {
  362. X            strcpy2(d1, token, 32);
  363. X          }
  364. X          break;
  365. X        case 9 :
  366. X          if (offset == TRUE) {
  367. X            strcpy2(d2, token, 32);
  368. X          }
  369. X          else {
  370. X            strcpy2(d1, token, 32);
  371. X          }        
  372. X          break;
  373. X        case 10 :
  374. X          if (offset == TRUE) {
  375. X            strcpy2(d3,token, 32);
  376. X          }
  377. X          else {
  378. X            strcpy2(d2, token, 32);
  379. X          }        
  380. X          break;
  381. X        case 11 :
  382. X          if (offset == TRUE) {
  383. X            strcpy(d4, token, 32);
  384. X          }
  385. X          else {
  386. X            strcpy2(d3, token, 32);
  387. X          }
  388. X          break;
  389. X        case 12 :
  390. X          if (offset == TRUE) {
  391. X            strcpy2(d5, token, 32);
  392. X          }
  393. X          else {
  394. X            strcpy2(d4, token, 32);
  395. X          }
  396. X          break;
  397. X        case 13 :
  398. X          if (offset == TRUE) { 
  399. X            strcpy2(d6, token, 32);
  400. X          }
  401. X          else {
  402. X            strcpy2(d5, token, 32);
  403. X          }
  404. X          break;
  405. X        case 14 :
  406. X          if (offset == TRUE) {
  407. X            strcpy2(d7, token, 32);
  408. X          }
  409. X          else {
  410. X            strcpy2(d6, token, 32);
  411. X          }
  412. X          break;
  413. X#else
  414. X        case 1 :
  415. X          cp.state[0] = token[0];
  416. X          cp.state[1] = '\0';
  417. X          if (strlen(token) > 1) {
  418. X            strcpy2(cp.user, (token+1), 8);
  419. X            cur_col += 1;
  420. X          }
  421. X          if ((cp.state[0] == 'R') || (cp.state[0] == 'O'))
  422. X            offset = TRUE;    /* "WCHAN" is empty */
  423. X          break;
  424. X        case 2 :
  425. X          strcpy2(cp.user, token, 8);
  426. X          break;
  427. X        case 3 :
  428. X          sscanf(token, "%d", &cp.pid);
  429. X          break;
  430. X        case 4 :
  431. X          sscanf(token, "%d", &cp.ppid);
  432. X          break;
  433. X        case 9 :
  434. X          if (cp.state[0] == 'Z') {
  435. X            cp.pages = 0;
  436. X            break;
  437. X          }
  438. X          strcpy(tbuf,token);
  439. X          if ((slen = strlen(token)) > 9 ) {
  440. X            strcpy2(tbuf, token, (slen-9));
  441. X            cur_col += 1;
  442. X          }
  443. X          sscanf(tbuf, "%d", &cp.pages);
  444. X          break;
  445. X        case 12 :
  446. X          if (offset == TRUE) {
  447. X            strcpy2(cp.time, token, 12);
  448. X          }
  449. X          break;
  450. X        case 13 :
  451. X          if (offset == TRUE) {
  452. X            strcpy2(cp.verb, token, 32);
  453. X          }
  454. X          else {
  455. X            strcpy2(cp.time, token, 12);
  456. X          }
  457. X          strcpy2(tbuf2, token, 12);
  458. X          break;
  459. X        case 14 :
  460. X          if (offset == FALSE) {
  461. X            strcpy2(cp.verb, token, 32);
  462. X          }
  463. X          break;
  464. X#endif    BSD
  465. X          }
  466. X          cur_col += 1;
  467. X        }
  468. X      }
  469. X#ifdef    BSD
  470. X    
  471. X      /* Sigh... */
  472. X
  473. X      if (isupper(d2[0]) || (d2[0] == 'p')) {
  474. X        strcpy(cp.state, d2);
  475. X        strcpy(cp.time, d4);
  476. X        strcpy(cp.verb, d5);
  477. X      }
  478. X      else {
  479. X        strcpy(cp.state, d3);
  480. X        strcpy(cp.time, d5);
  481. X        strcpy(cp.verb, d6);
  482. X      }
  483. X
  484. X#endif    BSD
  485. X      if (cur_row > 0) {
  486. X#ifdef    BSD
  487. X        if (who[0] == '\0')
  488. X          pinsert(&cp, TRUE);
  489. X        else
  490. X          if (strcmp(cp.user, who) == 0)
  491. X            pinsert(&cp, TRUE);
  492. X#else
  493. X        pinsert(&cp, TRUE);
  494. X#endif    BSD
  495. X      }
  496. X      cur_row += 1;
  497. X    }
  498. X    wait(0);
  499. X
  500. X    /* Place orphans into their foster homes */
  501. X
  502. X    fostered = 0;
  503. X
  504. X    while (orphcnt != fostered) {
  505. X
  506. X      adopted = FALSE;
  507. X
  508. X      for (child = orphans; child != NULL; child = child->s) {
  509. X        if (strcmp(child->cp.state, "HOME") != 0) {
  510. X          if (pinsert(&child->cp, FALSE) == TRUE) {
  511. X        strcpy(child->cp.state, "HOME");
  512. X            fostered += 1;
  513. X        adopted = TRUE;
  514. X          }
  515. X        }
  516. X      }
  517. X      if (adopted == FALSE) {
  518. X        break;
  519. X      }
  520. X    }
  521. X    printf("PID\t\t\t    User     Time    Pages  S  Command\n");
  522. X    pprint(proot, 0, FALSE);
  523. X    if (orphcnt != fostered) {
  524. X      printf("Orphans :\n");
  525. X      printf("PID (PPID)\t\t    User     Time    Pages  S  Command\n");
  526. X      pprint(orphans, 0, TRUE);
  527. X    }
  528. X}
  529. X
  530. Xint pinsert(cp, adopt)        /* TRUE = info inserted; FALSE = info orphaned */
  531. Xstruct psl *cp;
  532. Xint adopt;            /* TRUE = add orphans to orphan list */
  533. X{
  534. X    struct plist *parent, *sibling;
  535. X    int iflag;
  536. X
  537. X    iflag = TRUE;
  538. X    if (proot == NULL) {    /* call this one root for now */
  539. X      if ((proot = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
  540. X        fprintf(stderr,"malloc failed\n");
  541. X        exit(1);
  542. X      }
  543. X      cp_plist(proot, cp);
  544. X    }
  545. X    else {            /* find parent; traverse sibling list & insert */
  546. X      if ((parent = pfind(proot, cp->ppid)) == NULL) {    /* no parent yet */
  547. X        iflag = FALSE;
  548. X        if (adopt == TRUE) {
  549. X          orphcnt += 1;
  550. X          if (orphans == NULL) {
  551. X            if ((orphans = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
  552. X              fprintf(stderr,"malloc failed\n");
  553. X              exit(1);
  554. X            }
  555. X            cp_plist(orphans, cp);
  556. X          }
  557. X          else {            /* chain orphans together (heartless!) */
  558. X            for (sibling = orphans; sibling->s != NULL; sibling = sibling->s);
  559. X            if ((sibling->s = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
  560. X              fprintf(stderr,"malloc failed\n");
  561. X              exit(1);
  562. X            }
  563. X            cp_plist(sibling->s, cp);
  564. X          }
  565. X        }
  566. X      }
  567. X      else {            /* parent exists */
  568. X        if (parent->c == NULL) {    /* first child */
  569. X          if ((parent->c = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
  570. X            fprintf(stderr,"malloc failed\n");
  571. X            exit(1);
  572. X          }
  573. X          cp_plist(parent->c, cp);
  574. X        }
  575. X        else {            /* siblings exist */
  576. X          sibling = parent->c;
  577. X          while (sibling->s != NULL) sibling = sibling->s;
  578. X          if ((sibling->s = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
  579. X            fprintf(stderr,"malloc failed\n");
  580. X            exit(1);
  581. X          }
  582. X          cp_plist(sibling->s, cp);
  583. X        }
  584. X      }
  585. X    }
  586. X    return(iflag);
  587. X}
  588. X
  589. Xstruct plist *pfind(cp, ppid)
  590. Xstruct plist *cp;
  591. Xint ppid;
  592. X{
  593. X    struct plist *p;
  594. X
  595. X    if (cp == NULL) return(NULL);
  596. X    else if (cp->cp.pid == ppid) return(cp);
  597. X    else {
  598. X      if ((p = pfind(cp->c, ppid)) == NULL)
  599. X        return(pfind(cp->s, ppid));
  600. X      else
  601. X        return(p);
  602. X    }
  603. X}
  604. X
  605. Xvoid pprint(cp, cnt, orphflg)
  606. Xstruct plist *cp;
  607. Xint cnt, orphflg;
  608. X{
  609. X    int i, j;
  610. X    char buf[16];
  611. X
  612. X    if (cp == NULL) return;
  613. X
  614. X    if (strcmp(cp->cp.state, "HOME") != 0) {
  615. X      if (!((showroot == FALSE) && (strcmp(cp->cp.user, "root") == 0))) {
  616. X        if (orphflg == TRUE) {
  617. X#ifdef    BSD
  618. X          sprintf(buf, "%d (%d)",cp->cp.pid, cp->cp.ppid);
  619. X          j = strlen(buf);
  620. X#else
  621. X          j = sprintf(buf, "%d (%d)",cp->cp.pid, cp->cp.ppid);
  622. X#endif    BSD
  623. X        }
  624. X        else {
  625. X#ifdef    BSD
  626. X          sprintf(buf, "%d", cp->cp.pid);
  627. X          j = strlen(buf);
  628. X#else
  629. X          j = sprintf(buf, "%d", cp->cp.pid);
  630. X#endif    BSD
  631. X        }
  632. X        for (i = 0; i < cnt; i++) printf(" ");
  633. X        printf(" %s", buf);
  634. X        for (i = 0; i < (3-(((cnt*1)+j+1)/8)); i++) printf("\t");
  635. X        printf("%8s %8s %8d (%s) %s\n", cp->cp.user, cp->cp.time,
  636. X          cp->cp.pages, cp->cp.state, cp->cp.verb);
  637. X      }
  638. X    }
  639. X
  640. X    pprint(cp->c, cnt+1, orphflg);
  641. X    pprint(cp->s, cnt, orphflg);
  642. X}
  643. X
  644. Xvoid cp_plist(pto, pfrom)
  645. Xstruct plist *pto;
  646. Xstruct psl *pfrom;
  647. X{
  648. X    strcpy(pto->cp.state, pfrom->state);
  649. X    strcpy(pto->cp.user, pfrom->user);
  650. X    pto->cp.pid = pfrom->pid;
  651. X    pto->cp.ppid = pfrom->ppid;
  652. X    pto->cp.pages = pfrom->pages;
  653. X    strcpy(pto->cp.time, pfrom->time);
  654. X    strcpy(pto->cp.verb, pfrom->verb);
  655. X    pto->c = pto->s = NULL;
  656. X}
  657. X
  658. Xint getline(fd,buf,max)
  659. Xint fd;
  660. Xchar *buf;
  661. Xint max;
  662. X
  663. X/*
  664. X    getline(fd, buffer, max)
  665. X
  666. X    Get a line from file *fd* up to *max* bytes into *buffer*.
  667. X    Return 0 if OK, -1 if hit EOF, -2 if first read is NULL,
  668. X    -3 if read failed.
  669. X                                */
  670. X
  671. X{
  672. X    static char mybuf[MYBUFSZ];    /* internal buffer */
  673. X    static int myend = 0;        /* # bytes in mybuf */
  674. X    static int mycnt = 0;        /* # bytes already scanned */
  675. X    static char *curline = NULL;    /* beginning of current line to get */
  676. X    char *p, lastc;
  677. X    int nbytes;
  678. X
  679. X    if (firstr == TRUE) curline = NULL;
  680. X
  681. X    if (curline == NULL) {    /* empty buffer */
  682. X      if ((myend = read(fd, mybuf, MYBUFSZ)) < 0) {
  683. X        perror("read");
  684. X        return(-3);
  685. X      }
  686. X      curline = mybuf;        /* new buffer filled */
  687. X      mycnt = 0;
  688. X    }
  689. X
  690. X    if ((myend == 0) && firstr) {    /* first read hit EOF (empty file) */
  691. X      *buf = '\0';
  692. X      return(-2);
  693. X    }
  694. X
  695. X    if (myend == 0) {        /* later read hit EOF */
  696. X      *buf = '\0';
  697. X      return(-1);
  698. X    }
  699. X
  700. X    firstr = FALSE;
  701. X
  702. X    p = curline;
  703. X    nbytes = 0;
  704. X
  705. X    read_loop:
  706. X
  707. X    while ((*p != '\n') && (mycnt < myend) && (nbytes <= max)) {
  708. X      *buf++ = *p++;
  709. X      mycnt += 1;
  710. X      nbytes += 1;
  711. X    }
  712. X    lastc = *p;
  713. X    p += 1;
  714. X    mycnt += 1;
  715. X
  716. X    if ((mycnt >= myend) && (lastc != '\n')) {
  717. X      if ((myend = read(fd, mybuf, MYBUFSZ)) < 0) {
  718. X        perror("read");
  719. X        return(-3);
  720. X      }
  721. X      p = curline = mybuf;
  722. X      lastc = *p;
  723. X      mycnt = 0;
  724. X    }
  725. X    if ((mycnt != myend) && (lastc != '\n')) goto read_loop;
  726. X
  727. X    *buf += 1;
  728. X    *buf = '\0';
  729. X    curline = p;    /* set for next *getline* call */
  730. X
  731. X    if (mycnt >= myend)
  732. X      curline = NULL;    /* reached end of buffer */
  733. X
  734. X    return(0);
  735. X}
  736. X
  737. Xint get_token(line, token, max, wsp)    /* return 0 = token, -1 = no tokens left on line */
  738. Xchar *line, *token;
  739. Xint *wsp;                /* # of white spaces preceding token */
  740. Xint max;
  741. X{
  742. X    int i, j;
  743. X
  744. X    i = cur_char;
  745. X    j = 0;
  746. X    *wsp = 0;
  747. X
  748. X    if ((i >= (max-1)) || (line[i] == '\0')) {
  749. X      *token = '\0';
  750. X      return(-1);
  751. X    }
  752. X
  753. X    while((line[i] == ' ') || (line[i] == '\t')) {
  754. X      i++;
  755. X      *(wsp)++;
  756. X    }
  757. X
  758. X    while ((line[i] != ' ') && (line[i] != '\t') && (line[i] != '\n')
  759. X        && (line[i] != '\r') && (line[i] != '\0'))
  760. X      token[j++] = line[i++];
  761. X
  762. X    token[j] = '\0';
  763. X
  764. X    if (line[i] != '\0') cur_char = i + 1;
  765. X    else cur_char = i;
  766. X    return(0);
  767. X}
  768. X
  769. Xvoid strcpy2(dst, src, max)
  770. Xchar *dst, *src;
  771. Xint max;
  772. X{
  773. X    int i;
  774. X
  775. X    i = 0;
  776. X    while ((src[i] != ' ') && (i < max) && (src[i] != '\0')) {
  777. X      dst[i] = src[i];
  778. X      i += 1;
  779. X    }
  780. X    dst[i] = '\0';
  781. X}
  782. SHAR_EOF
  783. exit
  784.  
  785.