home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume24 / rcs / part04 < prev    next >
Encoding:
Internet Message Format  |  1991-03-05  |  53.3 KB

  1. Subject:  v24i004:  RCS source control system, Part04/12
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 2397c1e8 b47b14fe de8867e1 de8b4ea9
  5.  
  6. Submitted-by: Adam Hammer <hammer@cs.purdue.edu>
  7. Posting-number: Volume 24, Issue 4
  8. Archive-name: rcs/part04
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # Contents:  src/rcsutil.c src/rlog.c
  17. # Wrapped by rsalz@litchi.bbn.com on Thu Feb 21 14:36:57 1991
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. echo If this archive is complete, you will see the following message:
  20. echo '          "shar: End of archive 4 (of 12)."'
  21. if test -f 'src/rcsutil.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'src/rcsutil.c'\"
  23. else
  24.   echo shar: Extracting \"'src/rcsutil.c'\" \(17610 characters\)
  25.   sed "s/^X//" >'src/rcsutil.c' <<'END_OF_FILE'
  26. X/*
  27. X *                     RCS utilities
  28. X */
  29. X
  30. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  31. X   Copyright 1990 by Paul Eggert
  32. X   Distributed under license by the Free Software Foundation, Inc.
  33. X
  34. XThis file is part of RCS.
  35. X
  36. XRCS is free software; you can redistribute it and/or modify
  37. Xit under the terms of the GNU General Public License as published by
  38. Xthe Free Software Foundation; either version 1, or (at your option)
  39. Xany later version.
  40. X
  41. XRCS is distributed in the hope that it will be useful,
  42. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  43. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  44. XGNU General Public License for more details.
  45. X
  46. XYou should have received a copy of the GNU General Public License
  47. Xalong with RCS; see the file COPYING.  If not, write to
  48. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  49. X
  50. XReport problems and direct all questions to:
  51. X
  52. X    rcs-bugs@cs.purdue.edu
  53. X
  54. X*/
  55. X
  56. X
  57. X
  58. X
  59. X/* $Log: rcsutil.c,v $
  60. X * Revision 5.5  1990/12/04  05:18:49  eggert
  61. X * Don't output a blank line after a signal diagnostic.
  62. X * Use -I for prompts and -q for diagnostics.
  63. X *
  64. X * Revision 5.4  1990/11/01  05:03:53  eggert
  65. X * Remove unneeded setid check.  Add awrite(), fremember().
  66. X *
  67. X * Revision 5.3  1990/10/06  00:16:45  eggert
  68. X * Don't fread F if feof(F).
  69. X *
  70. X * Revision 5.2  1990/09/04  08:02:31  eggert
  71. X * Store fread()'s result in an fread_type object.
  72. X *
  73. X * Revision 5.1  1990/08/29  07:14:07  eggert
  74. X * Declare getpwuid() more carefully.
  75. X *
  76. X * Revision 5.0  1990/08/22  08:13:46  eggert
  77. X * Add setuid support.  Permit multiple locks per user.
  78. X * Remove compile-time limits; use malloc instead.
  79. X * Switch to GMT.  Permit dates past 1999/12/31.
  80. X * Add -V.  Remove snooping.  Ansify and Posixate.
  81. X * Tune.  Some USG hosts define NSIG but not sys_siglist.
  82. X * Don't run /bin/sh if it's hopeless.
  83. X * Don't leave garbage behind if the output is an empty pipe.
  84. X * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
  85. X *
  86. X * Revision 4.6  89/05/01  15:13:40  narten
  87. X * changed copyright header to reflect current distribution rules
  88. X * 
  89. X * Revision 4.5  88/11/08  16:01:02  narten
  90. X * corrected use of varargs routines
  91. X * 
  92. X * Revision 4.4  88/08/09  19:13:24  eggert
  93. X * Check for memory exhaustion.
  94. X * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
  95. X * Use execv(), not system(); yield exit status like diff(1)'s.
  96. X * 
  97. X * Revision 4.3  87/10/18  10:40:22  narten
  98. X * Updating version numbers. Changes relative to 1.1 actually
  99. X * relative to 4.1
  100. X * 
  101. X * Revision 1.3  87/09/24  14:01:01  narten
  102. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  103. X * warnings)
  104. X * 
  105. X * Revision 1.2  87/03/27  14:22:43  jenkins
  106. X * Port to suns
  107. X * 
  108. X * Revision 4.1  83/05/10  15:53:13  wft
  109. X * Added getcaller() and findlock().
  110. X * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
  111. X * (needed for background jobs in older shells). Added restoreints().
  112. X * Removed printing of full RCS path from logcommand().
  113. X * 
  114. X * Revision 3.8  83/02/15  15:41:49  wft
  115. X * Added routine fastcopy() to copy remainder of a file in blocks.
  116. X *
  117. X * Revision 3.7  82/12/24  15:25:19  wft
  118. X * added catchints(), ignoreints() for catching and ingnoring interrupts;
  119. X * fixed catchsig().
  120. X *
  121. X * Revision 3.6  82/12/08  21:52:05  wft
  122. X * Using DATEFORM to format dates.
  123. X *
  124. X * Revision 3.5  82/12/04  18:20:49  wft
  125. X * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
  126. X * lockedby-field.
  127. X *
  128. X * Revision 3.4  82/12/03  17:17:43  wft
  129. X * Added check to addlock() ensuring only one lock per person.
  130. X * Addlock also returns a pointer to the lock created. Deleted fancydate().
  131. X *
  132. X * Revision 3.3  82/11/27  12:24:37  wft
  133. X * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
  134. X * Introduced macro SNOOP so that snoop can be placed in directory other than
  135. X * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
  136. X *
  137. X * Revision 3.2  82/10/18  21:15:11  wft
  138. X * added function getfullRCSname().
  139. X *
  140. X * Revision 3.1  82/10/13  16:17:37  wft
  141. X * Cleanup message is now suppressed in quiet mode.
  142. X */
  143. X
  144. X
  145. X
  146. X
  147. X#include "rcsbase.h"
  148. X
  149. X#if !MAKEDEPEND && defined(declare_getpwuid)
  150. X#    include <pwd.h>
  151. X    declare_getpwuid
  152. X#endif
  153. X
  154. XlibId(utilId, "$Id: rcsutil.c,v 5.5 1990/12/04 05:18:49 eggert Exp $")
  155. X
  156. X#if lint
  157. X    malloc_type lintalloc;
  158. X#endif
  159. X
  160. X#if has_getuid
  161. X    uid_t ruid;
  162. X#endif
  163. X#if SETID
  164. X    static uid_t euid;
  165. X    static gid_t egid, rgid;
  166. X#endif
  167. X
  168. X/*
  169. X * list of blocks allocated with ftestalloc()
  170. X * These blocks can be freed by ffree when we're done with the current file.
  171. X * We could put the free block inside struct alloclist, rather than a pointer
  172. X * to the free block, but that would be less portable.
  173. X */
  174. Xstruct alloclist {
  175. X    malloc_type alloc;
  176. X    struct alloclist *nextalloc;
  177. X};
  178. Xstatic struct alloclist *alloced;
  179. X
  180. X
  181. X    static malloc_type
  182. Xokalloc(p)
  183. X    malloc_type p;
  184. X{
  185. X    if (!p)
  186. X        faterror("out of memory");
  187. X    return p;
  188. X}
  189. X
  190. X    malloc_type
  191. Xtestalloc(size)
  192. X    size_t size;
  193. X/* Allocate a block, testing that the allocation succeeded.  */
  194. X{
  195. X    return okalloc(malloc(size));
  196. X}
  197. X
  198. X    malloc_type
  199. Xtestrealloc(ptr, size)
  200. X    malloc_type ptr;
  201. X    size_t size;
  202. X/* Reallocate a block, testing that the allocation succeeded.  */
  203. X{
  204. X    return okalloc(realloc(ptr, size));
  205. X}
  206. X
  207. X    malloc_type
  208. Xfremember(ptr)
  209. X    malloc_type ptr;
  210. X/* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
  211. X{
  212. X    register struct alloclist *q = talloc(struct alloclist);
  213. X    q->nextalloc = alloced;
  214. X    alloced = q;
  215. X    return q->alloc = ptr;
  216. X}
  217. X
  218. X    malloc_type
  219. Xftestalloc(size)
  220. X    size_t size;
  221. X/* Allocate a block, putting it in 'alloced' so it can be freed later. */
  222. X{
  223. X    return fremember(testalloc(size));
  224. X}
  225. X
  226. X    void
  227. Xffree()
  228. X/* Free all blocks allocated with ftestalloc().  */
  229. X{
  230. X    register struct alloclist *p, *q;
  231. X    for (p = alloced;  p;  p = q) {
  232. X        q = p->nextalloc;
  233. X        tfree(p->alloc);
  234. X        tfree(p);
  235. X    }
  236. X    alloced = nil;
  237. X}
  238. X
  239. X    void
  240. Xffree1(f)
  241. X    register const char *f;
  242. X/* Free the block f, which was allocated by ftestalloc.  */
  243. X{
  244. X    register struct alloclist *p, **a = &alloced;
  245. X
  246. X    while ((p = *a)->alloc  !=  f)
  247. X        a = &p->nextalloc;
  248. X    *a = p->nextalloc;
  249. X    tfree(p->alloc);
  250. X    tfree(p);
  251. X}
  252. X
  253. X    const char *
  254. Xstrsave(s)
  255. X    const char *s;
  256. X/* Save s in permanently allocated storage. */
  257. X{
  258. X    return strcpy(tnalloc(char, strlen(s)+1), s);
  259. X}
  260. X
  261. X    const char *
  262. Xfstrsave(s)
  263. X    const char *s;
  264. X/* Save s in storage that will be deallocated when we're done with this file. */
  265. X{
  266. X    return strcpy(ftnalloc(char, strlen(s)+1), s);
  267. X}
  268. X
  269. X    char *
  270. Xcgetenv(name)
  271. X    const char *name;
  272. X/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
  273. X{
  274. X    register char *p;
  275. X
  276. X    return (p=getenv(name)) ? strsave(p) : p;
  277. X}
  278. X
  279. X
  280. X    const char *
  281. Xgetcaller()
  282. X/* Function: gets the caller's login.
  283. X */
  284. X{
  285. X    static char *name;
  286. X
  287. X    if (!name) {
  288. X        if (!(
  289. X            /* Use getenv() if we're trustworthy; it's much faster.  */
  290. X#if SETID
  291. X            euid==ruid && egid==rgid &&
  292. X#endif
  293. X            (
  294. X                (name = cgetenv("LOGNAME"))
  295. X            ||    (name = cgetenv("USER"))
  296. X            )
  297. X
  298. X            /* Follow a procedure recommended by Posix 1003.1-1988.  */
  299. X            ||    (name = getlogin())
  300. X        )) {
  301. X#if has_getuid & defined(declare_getpwuid)
  302. X            const struct passwd *pw = getpwuid(ruid);
  303. X            if (!pw)
  304. X                faterror("no password entry for userid %lu",
  305. X                     (unsigned long)ruid
  306. X                );
  307. X            name = pw->pw_name;
  308. X#else
  309. X            faterror("Who are you?  Please set LOGNAME.");
  310. X#endif
  311. X        }
  312. X        checksid(name);
  313. X    }
  314. X    return name;
  315. X}
  316. X
  317. X
  318. X
  319. X    int
  320. Xfindlock(delete, target)
  321. X    int delete;
  322. X    struct hshentry **target;
  323. X/* Finds the first lock held by caller and returns a pointer
  324. X * to the locked delta; also removes the lock if delete is set.
  325. X * Returns 0 for no locks, 1 for one, 2 for two or more.
  326. X * If one lock, puts it into *target.
  327. X */
  328. X{
  329. X    register struct lock *next, **trail, **found = nil;
  330. X
  331. X    for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
  332. X        if (strcmp(getcaller(), next->login)  ==  0) {
  333. X            if (found) {
  334. X                error("multiple revisions locked by %s; please specify one", getcaller());
  335. X                return 2;
  336. X            }
  337. X            found = trail;
  338. X        }
  339. X    if (!found)
  340. X        return 0;
  341. X    next = *found;
  342. X    *target = next->delta;
  343. X    if (delete) {
  344. X        next->delta->lockedby = nil;
  345. X        *found = next->nextlock;
  346. X    }
  347. X    return 1;
  348. X}
  349. X
  350. X
  351. X
  352. X
  353. X
  354. X
  355. X
  356. X    int
  357. Xaddlock(delta)
  358. Xstruct hshentry * delta;
  359. X/* Add a lock held by caller to delta and yield 1 if successful.
  360. X * Print an error message and yield -1 if no lock is added because
  361. X * the delta is locked by somebody other than caller.
  362. X * Yield 0 if the caller already holds the lock.  */
  363. X{
  364. X        struct lock * next;
  365. X
  366. X        next=Locks;
  367. X        while (next!=nil) {
  368. X                if (cmpnum(delta->num,next->delta->num)==0) {
  369. X            if (strcmp(getcaller(),next->login)==0)
  370. X                return 0;
  371. X                        else {
  372. X                                error("revision %s already locked by %s",
  373. X                                      delta->num, next->login);
  374. X                return -1;
  375. X                        }
  376. X                }
  377. X        next = next->nextlock;
  378. X    }
  379. X        /* set up new lockblock */
  380. X    next = ftalloc(struct lock);
  381. X    delta->lockedby=next->login=getcaller();
  382. X        next->delta= delta;
  383. X        next->nextlock=Locks;
  384. X        Locks=next;
  385. X    return 1;
  386. X}
  387. X
  388. X
  389. X
  390. X    int
  391. Xaddsymbol(num, name, rebind)
  392. X    const char *num, *name;
  393. X    int rebind;
  394. X/* Function: adds a new symbolic name and associates it with revision num.
  395. X * If name already exists and rebind is true, the name is associated
  396. X * with the new num; otherwise, an error message is printed and
  397. X * false returned. Returns true it successful.
  398. X */
  399. X{       register struct assoc * next;
  400. X        next=Symbols;
  401. X        while (next!=nil) {
  402. X                if (strcmp(name,next->symbol)==0) {
  403. X                        if (rebind) {
  404. X                next->num = num;
  405. X                                return true;
  406. X                        } else {
  407. X                                error("symbolic name %s already bound to %s",
  408. X                    name, next->num);
  409. X                                return false;
  410. X                        }
  411. X                } else  next = next->nextassoc;
  412. X        }
  413. X        /* not found; insert new pair. */
  414. X    next = ftalloc(struct assoc);
  415. X        next->symbol=name;
  416. X    next->num = num;
  417. X        next->nextassoc=Symbols;
  418. X        Symbols = next;
  419. X        return true;
  420. X}
  421. X
  422. X
  423. X
  424. X
  425. Xint checkaccesslist()
  426. X/* function: Returns true if caller is the superuser, the owner of the
  427. X * file, the access list is empty, or caller is on the access list.
  428. X * Prints an error message and returns false otherwise.
  429. X */
  430. X{
  431. X    register const struct access *next;
  432. X
  433. X    if (!AccessList || strcmp(getcaller(),"root")==0)
  434. X                return true;
  435. X
  436. X        next=AccessList;
  437. X        do {
  438. X        if (strcmp(getcaller(),next->login)==0)
  439. X                        return true;
  440. X                next=next->nextaccess;
  441. X        } while (next!=nil);
  442. X
  443. X#if has_getuid
  444. X    {
  445. X        struct stat statbuf;
  446. X        VOID fstat(fileno(finptr),&statbuf);  /* get owner of file */
  447. X        if (myself(statbuf.st_uid)) return true;
  448. X    }
  449. X#endif
  450. X
  451. X    error("user %s not on the access list", getcaller());
  452. X        return false;
  453. X}
  454. X
  455. X
  456. X/*
  457. X *     Signal handling
  458. X *
  459. X * ANSI C places too many restrictions on signal handlers.
  460. X * We obey as many of them as we can.
  461. X * Posix places fewer restrictions, and we are Posix-compatible here.
  462. X */
  463. X
  464. Xstatic volatile sig_atomic_t heldsignal, holdlevel;
  465. X
  466. X    static signal_type
  467. Xcatchsig(s)
  468. X    int s;
  469. X{
  470. X    const char *sname;
  471. X    char buf[BUFSIZ];
  472. X
  473. X#if sig_zaps_handler
  474. X    /* If a signal arrives before we reset the signal handler, we lose. */
  475. X    VOID signal(s, SIG_IGN);
  476. X#endif
  477. X    if (holdlevel) {
  478. X        heldsignal = s;
  479. X        return;
  480. X    }
  481. X    ignoreints();
  482. X    setrid();
  483. X    if (!quietflag) {
  484. X        sname = nil;
  485. X#if has_sys_siglist & defined(NSIG)
  486. X        if ((unsigned)s < NSIG) {
  487. X#        ifndef sys_siglist
  488. X            extern const char *sys_siglist[];
  489. X#        endif
  490. X        sname = sys_siglist[s];
  491. X        }
  492. X#else
  493. X        switch (s) {
  494. X#ifdef SIGHUP
  495. X        case SIGHUP:    sname = "Hangup";  break;
  496. X#endif
  497. X#ifdef SIGINT
  498. X        case SIGINT:    sname = "Interrupt";  break;
  499. X#endif
  500. X#ifdef SIGPIPE
  501. X        case SIGPIPE:    sname = "Broken pipe";  break;
  502. X#endif
  503. X#ifdef SIGQUIT
  504. X        case SIGQUIT:    sname = "Quit";  break;
  505. X#endif
  506. X#ifdef SIGTERM
  507. X        case SIGTERM:    sname = "Terminated";  break;
  508. X#endif
  509. X#ifdef SIGXCPU
  510. X        case SIGXCPU:    sname = "Cputime limit exceeded";  break;
  511. X#endif
  512. X#ifdef SIGXFSZ
  513. X        case SIGXFSZ:    sname = "Filesize limit exceeded";  break;
  514. X#endif
  515. X        }
  516. X#endif
  517. X        if (sname)
  518. X        VOID sprintf(buf, "\nRCS: %s.  Cleaning up.\n", sname);
  519. X        else
  520. X        VOID sprintf(buf, "\nRCS: Signal %d.  Cleaning up.\n", s);
  521. X        VOID write(STDERR_FILENO, buf, strlen(buf));
  522. X    }
  523. X    exiterr();
  524. X}
  525. X
  526. X    void
  527. Xignoreints()
  528. X{
  529. X    ++holdlevel;
  530. X}
  531. X
  532. X    void
  533. Xrestoreints()
  534. X{
  535. X    if (!--holdlevel && heldsignal)
  536. X        VOID catchsig(heldsignal);
  537. X}
  538. X
  539. X
  540. Xstatic const sig[] = {
  541. X#ifdef SIGHUP
  542. X    SIGHUP,
  543. X#endif
  544. X#ifdef SIGINT
  545. X    SIGINT,
  546. X#endif
  547. X#ifdef SIGPIPE
  548. X    SIGPIPE,
  549. X#endif
  550. X#ifdef SIGQUIT
  551. X    SIGQUIT,
  552. X#endif
  553. X#ifdef SIGTERM
  554. X    SIGTERM,
  555. X#endif
  556. X#ifdef SIGXCPU
  557. X    SIGXCPU,
  558. X#endif
  559. X#ifdef SIGXFSZ
  560. X    SIGXFSZ,
  561. X#endif
  562. X};
  563. X#define SIGS (sizeof(sig)/sizeof(*sig))
  564. X
  565. X
  566. X#if has_sigaction
  567. X
  568. X    static void
  569. X  checksig(r)
  570. X    int r;
  571. X  {
  572. X    if (r < 0)
  573. X        efaterror("signal");
  574. X  }
  575. X
  576. X    void
  577. X  catchints()
  578. X  {
  579. X    register int i;
  580. X    sigset_t blocked;
  581. X    struct sigaction act;
  582. X
  583. X    checksig(sigemptyset(&blocked));
  584. X    for (i=SIGS; 0<=--i; )
  585. X        checksig(sigaddset(&blocked, sig[i]));
  586. X    for (i=SIGS; 0<=--i; ) {
  587. X        checksig(sigaction(sig[i], (struct sigaction*)nil, &act));
  588. X        if (act.sa_handler != SIG_IGN) {
  589. X            act.sa_handler = catchsig;
  590. X            act.sa_mask = blocked;
  591. X            checksig(sigaction(sig[i], &act, (struct sigaction*)nil));
  592. X        }
  593. X    }
  594. X  }
  595. X
  596. X#else
  597. X#if has_sigblock
  598. X
  599. X  void catchints()
  600. X  {
  601. X    register int i;
  602. X    int mask;
  603. X
  604. X    mask = 0;
  605. X    for (i=SIGS; 0<=--i; )
  606. X        mask |= sigmask(sig[i]);
  607. X    mask = sigblock(mask);
  608. X    for (i=SIGS; 0<=--i; )
  609. X        if (signal(sig[i], catchsig) == SIG_IGN)
  610. X            VOID signal(sig[i], SIG_IGN);
  611. X    VOID sigsetmask(mask);
  612. X  }
  613. X
  614. X#else
  615. X
  616. X  void catchints()
  617. X  {
  618. X    register i;
  619. X    for (i=SIGS; 0<=--i; )
  620. X        if (signal(sig[i], SIG_IGN) != SIG_IGN)
  621. X            VOID signal(sig[i], catchsig);
  622. X  }
  623. X
  624. X#endif
  625. X#endif
  626. X
  627. X
  628. X    void
  629. Xfastcopy(inf,outf)
  630. XFILE * inf, * outf;
  631. X/* Function: copies the remainder of file inf to outf.
  632. X */
  633. X{       char buf[BUFSIZ];
  634. X    register fread_type rcount;
  635. X
  636. X        /*now read the rest of the file in blocks*/
  637. X    while (!feof(inf)  &&  (rcount = fread(buf,sizeof(char),BUFSIZ,inf))) {
  638. X        awrite(buf, rcount, outf);
  639. X        }
  640. X}
  641. X
  642. X    void
  643. Xawrite(buf, chars, f)
  644. X    const char *buf;
  645. X    fread_type chars;
  646. X    FILE *f;
  647. X{
  648. X    if (fwrite(buf, sizeof(char), chars, f) != chars)
  649. X        IOerror();
  650. X}
  651. X
  652. X
  653. X
  654. X
  655. X
  656. X/*
  657. X* Print RCS format date and time in user-readable format.
  658. X*/
  659. X    void
  660. Xprintdate(f, date, separator)
  661. X    register FILE *f;
  662. X    const char *date, *separator;
  663. X{
  664. X    register const char *p = date;
  665. X
  666. X    while (*p++ != '.')
  667. X        ;
  668. X    aprintf(f, "%s%.*s/%.2s/%.2s%s%.2s:%.2s:%s",
  669. X        date[2]=='.' && VERSION(5)<=RCSversion  ?  "19"  :  "",
  670. X        p-date-1, date,
  671. X        p, p+3, separator, p+6, p+9, p+12
  672. X    );
  673. X}
  674. X
  675. X
  676. X
  677. X
  678. Xstatic int fdreopen(fd, file, flags, mode)
  679. X    int fd;
  680. X    const char *file;
  681. X    int flags;
  682. X    mode_t mode;
  683. X{
  684. X    int newfd;
  685. X    VOID close(fd);
  686. X    newfd =
  687. X#if !open_can_creat
  688. X        flags&O_CREAT ? creat(file,mode) :
  689. X#endif
  690. X        open(file,flags,mode);
  691. X    if (newfd < 0  ||  newfd == fd)
  692. X        return newfd;
  693. X    fd = dup2(newfd, fd);
  694. X    VOID close(newfd);
  695. X    return fd;
  696. X}
  697. X
  698. Xstatic void tryopen(fd,file,flags)
  699. X    int fd, flags;
  700. X    const char *file;
  701. X{
  702. X    if (file  &&  fdreopen(fd,file,flags,S_IRUSR|S_IWUSR) != fd) {
  703. X        VOID write(STDERR_FILENO, file, strlen(file));
  704. X        VOID write(STDERR_FILENO, ": can't open\n", 13);
  705. X        _exit(EXIT_TROUBLE);
  706. X    }
  707. X}
  708. X
  709. X/*
  710. X* Run a command specified by the strings in 'inoutargs'.
  711. X* inoutargs[0], if nonnil, is the name of the input file.
  712. X* inoutargs[1], if nonnil, is the name of the output file.
  713. X* inoutargs[2..] form the command to be run.
  714. X*/
  715. X    int
  716. Xrunv(inoutargs)
  717. X    const char **inoutargs;
  718. X{
  719. X    int pid;
  720. X    int wstatus, w;
  721. X    register const char **p;
  722. X    oflush();
  723. X    eflush();
  724. X    if (!(pid = vfork())) {
  725. X        p = inoutargs;
  726. X        tryopen(STDIN_FILENO, *p++, O_RDONLY);
  727. X        tryopen(STDOUT_FILENO, *p++, O_CREAT|O_TRUNC|O_WRONLY);
  728. X        VOID EXECRCS(*p, p);
  729. X        if (errno == ENOEXEC) {
  730. X            *--p = "/bin/sh";
  731. X            VOID execv(*p, p);
  732. X        }
  733. X        VOID write(STDERR_FILENO, *p, strlen(*p));
  734. X        VOID write(STDERR_FILENO, ": not found\n", 12);
  735. X        _exit(EXIT_TROUBLE);
  736. X    }
  737. X    if (pid < 0)
  738. X        return pid;
  739. X    do {
  740. X        if ((w = wait(&wstatus)) < 0)
  741. X            return w;
  742. X    } while (w != pid);
  743. X    return wstatus;
  744. X}
  745. X
  746. X#define CARGSMAX 20
  747. X/*
  748. X* Run a command.
  749. X* The first two arguments are the input and output files (if nonnil);
  750. X* the rest specify the command and its arguments.
  751. X*/
  752. X    int
  753. X#if has_prototypes
  754. Xrun(const char *infile, const char *outfile, ...)
  755. X#else
  756. X    /*VARARGS2*/
  757. Xrun(infile, outfile, va_alist)
  758. X    const char *infile;
  759. X    const char *outfile;
  760. X    va_dcl
  761. X#endif
  762. X{
  763. X    va_list ap;
  764. X    const char *rgargs[CARGSMAX];
  765. X    register i = 0;
  766. X    rgargs[0] = infile;
  767. X    rgargs[1] = outfile;
  768. X    vararg_start(ap, outfile);
  769. X    for (i = 2;  (rgargs[i++] = va_arg(ap, const char*));  )
  770. X        if (CARGSMAX <= i)
  771. X            faterror("too many command arguments");
  772. X    va_end(ap);
  773. X    return runv(rgargs);
  774. X}
  775. X
  776. X
  777. Xint RCSversion;
  778. X
  779. X    void
  780. XsetRCSversion(str)
  781. X    const char *str;
  782. X{
  783. X    static const char *oldversion;
  784. X
  785. X    register const char *s = str + 2;
  786. X    int v = VERSION_DEFAULT;
  787. X
  788. X    if (oldversion)
  789. X        redefined('V');
  790. X    oldversion = str;
  791. X
  792. X    if (*s) {
  793. X        v = 0;
  794. X        while (isdigit(*s))
  795. X            v  =  10*v + *s++ - '0';
  796. X        if (*s)
  797. X            faterror("%s isn't a number", str);
  798. X        if (v < VERSION_MIN  ||  VERSION_MAX < v)
  799. X            faterror("%s out of range %d..%d", str, VERSION_MIN, VERSION_MAX);
  800. X    }
  801. X
  802. X    RCSversion = VERSION(v);
  803. X}
  804. X
  805. X    void
  806. Xinitid()
  807. X{
  808. X#if SETID
  809. X    egid = getegid();
  810. X    euid = geteuid();
  811. X    rgid = getgid();
  812. X#endif
  813. X#if has_getuid
  814. X    ruid = getuid();
  815. X#endif
  816. X    setrid();
  817. X}
  818. X
  819. X
  820. X#if SETID
  821. X    void
  822. Xseteid()
  823. X/* Become effective user and group.  */
  824. X{
  825. X    if (euid!=ruid && seteuid(euid)<0  ||  egid!=rgid && setegid(egid)<0)
  826. X        efaterror("seteid");
  827. X}
  828. X
  829. X    void
  830. Xsetrid()
  831. X/* Become real user and group.  */
  832. X{
  833. X    if (euid!=ruid && seteuid(ruid)<0  ||  egid!=rgid && setegid(rgid)<0)
  834. X        efaterror("setrid");
  835. X}
  836. X#endif
  837. END_OF_FILE
  838.   if test 17610 -ne `wc -c <'src/rcsutil.c'`; then
  839.     echo shar: \"'src/rcsutil.c'\" unpacked with wrong size!
  840.   fi
  841.   # end of 'src/rcsutil.c'
  842. fi
  843. if test -f 'src/rlog.c' -a "${1}" != "-c" ; then 
  844.   echo shar: Will not clobber existing file \"'src/rlog.c'\"
  845. else
  846.   echo shar: Extracting \"'src/rlog.c'\" \(32911 characters\)
  847.   sed "s/^X//" >'src/rlog.c' <<'END_OF_FILE'
  848. X/*
  849. X *                       RLOG    operation
  850. X */
  851. X/*****************************************************************************
  852. X *                       print contents of RCS files
  853. X *****************************************************************************
  854. X */
  855. X
  856. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  857. X   Copyright 1990 by Paul Eggert
  858. X   Distributed under license by the Free Software Foundation, Inc.
  859. X
  860. XThis file is part of RCS.
  861. X
  862. XRCS is free software; you can redistribute it and/or modify
  863. Xit under the terms of the GNU General Public License as published by
  864. Xthe Free Software Foundation; either version 1, or (at your option)
  865. Xany later version.
  866. X
  867. XRCS is distributed in the hope that it will be useful,
  868. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  869. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  870. XGNU General Public License for more details.
  871. X
  872. XYou should have received a copy of the GNU General Public License
  873. Xalong with RCS; see the file COPYING.  If not, write to
  874. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  875. X
  876. XReport problems and direct all questions to:
  877. X
  878. X    rcs-bugs@cs.purdue.edu
  879. X
  880. X*/
  881. X
  882. X
  883. X
  884. X
  885. X/* $Log: rlog.c,v $
  886. X * Revision 5.5  1990/11/01  05:03:55  eggert
  887. X * Permit arbitrary data in logs and comment leaders.
  888. X *
  889. X * Revision 5.4  1990/10/04  06:30:22  eggert
  890. X * Accumulate exit status across files.
  891. X *
  892. X * Revision 5.3  1990/09/11  02:41:16  eggert
  893. X * Plug memory leak.
  894. X *
  895. X * Revision 5.2  1990/09/04  08:02:33  eggert
  896. X * Count RCS lines better.
  897. X *
  898. X * Revision 5.0  1990/08/22  08:13:48  eggert
  899. X * Remove compile-time limits; use malloc instead.  Add setuid support.
  900. X * Switch to GMT.
  901. X * Report dates in long form, to warn about dates past 1999/12/31.
  902. X * Change "added/del" message to make room for the longer dates.
  903. X * Don't generate trailing white space.  Add -V.  Ansify and Posixate.
  904. X *
  905. X * Revision 4.7  89/05/01  15:13:48  narten
  906. X * changed copyright header to reflect current distribution rules
  907. X * 
  908. X * Revision 4.6  88/08/09  19:13:28  eggert
  909. X * Check for memory exhaustion; don't access freed storage.
  910. X * Shrink stdio code size; remove lint.
  911. X * 
  912. X * Revision 4.5  87/12/18  11:46:38  narten
  913. X * more lint cleanups (Guy Harris)
  914. X * 
  915. X * Revision 4.4  87/10/18  10:41:12  narten
  916. X * Updating version numbers
  917. X * Changes relative to 1.1 actually relative to 4.2
  918. X * 
  919. X * Revision 1.3  87/09/24  14:01:10  narten
  920. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  921. X * warnings)
  922. X * 
  923. X * Revision 1.2  87/03/27  14:22:45  jenkins
  924. X * Port to suns
  925. X * 
  926. X * Revision 4.2  83/12/05  09:18:09  wft
  927. X * changed rewriteflag to external.
  928. X * 
  929. X * Revision 4.1  83/05/11  16:16:55  wft
  930. X * Added -b, updated getnumericrev() accordingly.
  931. X * Replaced getpwuid() with getcaller().
  932. X * 
  933. X * Revision 3.7  83/05/11  14:24:13  wft
  934. X * Added options -L and -R;
  935. X * Fixed selection bug with -l on multiple files.
  936. X * Fixed error on dates of the form -d'>date' (rewrote getdatepair()).
  937. X * 
  938. X * Revision 3.6  82/12/24  15:57:53  wft
  939. X * shortened output format.
  940. X *
  941. X * Revision 3.5  82/12/08  21:45:26  wft
  942. X * removed call to checkaccesslist(); used DATEFORM to format all dates;
  943. X * removed unused variables.
  944. X *
  945. X * Revision 3.4  82/12/04  13:26:25  wft
  946. X * Replaced getdelta() with gettree(); removed updating of field lockedby.
  947. X *
  948. X * Revision 3.3  82/12/03  14:08:20  wft
  949. X * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE.
  950. X * Fixed printing of nil, removed printing of Suffix,
  951. X * added shortcut if no revisions are printed, disambiguated struct members.
  952. X *
  953. X * Revision 3.2  82/10/18  21:09:06  wft
  954. X * call to curdir replaced with getfullRCSname(),
  955. X * fixed call to getlogin(), cosmetic changes on output,
  956. X * changed conflicting long identifiers.
  957. X *
  958. X * Revision 3.1  82/10/13  16:07:56  wft
  959. X * fixed type of variables receiving from getc() (char -> int).
  960. X */
  961. X
  962. X
  963. X
  964. X#include "rcsbase.h"
  965. X
  966. Xstruct  lockers {                     /* lockers in locker option; stored   */
  967. X     const char         * login;      /* lockerlist                         */
  968. X     struct     lockers * lockerlink;
  969. X     }  ;
  970. X
  971. Xstruct  stateattri {                  /* states in state option; stored in  */
  972. X     const char         * status;     /* statelist                          */
  973. X     struct  stateattri * nextstate;
  974. X     }  ;
  975. X
  976. Xstruct  authors {                     /* login names in author option;      */
  977. X     const char         * login;      /* stored in authorlist               */
  978. X     struct     authors * nextauthor;
  979. X     }  ;
  980. X
  981. Xstruct Revpairs{                      /* revision or branch range in -r     */
  982. X     unsigned          numfld;     /* option; stored in revlist        */
  983. X     const char         * strtrev;
  984. X     const char         * endrev;
  985. X     struct  Revpairs   * rnext;
  986. X     } ;
  987. X
  988. Xstruct Datepairs{                     /* date range in -d option; stored in */
  989. X     char               strtdate[datesize];   /* duelst and datelist      */
  990. X     char               enddate[datesize];
  991. X     struct  Datepairs  * dnext;
  992. X     };
  993. X
  994. Xstatic char extractdelta P((const struct hshentry*));
  995. Xstatic int checkrevpair P((const char*,const char*));
  996. Xstatic int readdeltalog P((void));
  997. Xstatic void cleanup P((void));
  998. Xstatic void extdate P((struct hshentry*));
  999. Xstatic void exttree P((struct hshentry*));
  1000. Xstatic void getauthor P((char*));
  1001. Xstatic void getdatepair P((char*));
  1002. Xstatic void getlocker P((char*));
  1003. Xstatic void getnumericrev P((void));
  1004. Xstatic void getrevpairs P((char*));
  1005. Xstatic void getscript P((struct hshentry*));
  1006. Xstatic void getstate P((char*));
  1007. Xstatic void putabranch P((const struct hshentry*));
  1008. Xstatic void putadelta P((const struct hshentry*,const struct hshentry*,int));
  1009. Xstatic void putforest P((const struct branchhead*));
  1010. Xstatic void putree P((const struct hshentry*));
  1011. Xstatic void putrunk P((void));
  1012. Xstatic void recentdate P((const struct hshentry*,struct Datepairs*));
  1013. Xstatic void trunclocks P((void));
  1014. X
  1015. Xstatic const char *insDelFormat;
  1016. Xstatic int branchflag;    /*set on -b */
  1017. Xstatic int exitstatus;
  1018. Xstatic int lockflag;
  1019. Xstatic int revno;    /* number of revision chosen */
  1020. Xstatic struct Datepairs *datelist, *duelst;
  1021. Xstatic struct Revpairs *revlist, *Revlst;
  1022. Xstatic struct authors *authorlist;
  1023. Xstatic struct lockers *lockerlist;
  1024. Xstatic struct stateattri *statelist;
  1025. X
  1026. X
  1027. XmainProg(rlogId, "rlog", "$Id: rlog.c,v 5.5 1990/11/01 05:03:55 eggert Exp $")
  1028. X{
  1029. X    static const char cmdusage[] =
  1030. X        "\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ...";
  1031. X
  1032. X    struct Datepairs *currdate;
  1033. X    const char *accessListString, *accessFormat, *commentFormat;
  1034. X    const char *headFormat, *symbolFormat;
  1035. X    const struct access *curaccess;
  1036. X    const struct assoc *curassoc;
  1037. X    const struct lock *currlock;
  1038. X    int descflag, selectflag;
  1039. X    int onlylockflag;  /* print only files with locks */
  1040. X    int selectop;  /* print only some revisions */
  1041. X    int onlyRCSflag;  /* print only RCS file name */
  1042. X
  1043. X    initid();
  1044. X
  1045. X        descflag = selectflag = true;
  1046. X    onlylockflag = selectop = onlyRCSflag = false;
  1047. X
  1048. X        while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  1049. X                switch ((*argv)[1]) {
  1050. X
  1051. X        case 'L':
  1052. X            onlylockflag = true;
  1053. X            break;
  1054. X
  1055. X        case 'R':
  1056. X            onlyRCSflag =true;
  1057. X            break;
  1058. X
  1059. X                case 'l':
  1060. X                        selectop = true;
  1061. X                        lockflag = true;
  1062. X                        getlocker( (*argv)+2 );
  1063. X                        break;
  1064. X
  1065. X                case 'b':
  1066. X                        selectop = true;
  1067. X                        branchflag = true;
  1068. X                        break;
  1069. X
  1070. X                case 'r':
  1071. X                        selectop = true;
  1072. X                        getrevpairs( (*argv)+2 );
  1073. X                        break;
  1074. X
  1075. X                case 'd':
  1076. X                        selectop = true;
  1077. X                        getdatepair( (*argv)+2 );
  1078. X                        break;
  1079. X
  1080. X                case 's':
  1081. X                        selectop = true;
  1082. X                        getstate( (*argv)+2);
  1083. X                        break;
  1084. X
  1085. X                case 'w':
  1086. X                        selectop = true;
  1087. X                        getauthor( (*argv)+2);
  1088. X                        break;
  1089. X
  1090. X                case 'h':
  1091. X                        if ( ! selectflag ) warn("-t overrides -h.");
  1092. X                        else    descflag = false;
  1093. X                        break;
  1094. X
  1095. X                case 't':
  1096. X                        selectflag = false;
  1097. X                        if ( ! descflag ) warn("-t overrides -h.");
  1098. X                        descflag = true;
  1099. X                        break;
  1100. X
  1101. X        case 'V':
  1102. X            setRCSversion(*argv);
  1103. X            break;
  1104. X
  1105. X                default:
  1106. X            faterror("unknown option: %s%s", *argv, cmdusage);
  1107. X
  1108. X                };
  1109. X        } /* end of option processing */
  1110. X
  1111. X    if (argc<1) faterror("no input file%s", cmdusage);
  1112. X
  1113. X    if (RCSversion < VERSION(5)) {
  1114. X        accessListString = "\naccess list:   ";
  1115. X        accessFormat = "  %s";
  1116. X        commentFormat = "\ncomment leader:  \"";
  1117. X        headFormat = "\nRCS file:        %s;   Working file:    %s\nhead:           %s%s\nbranch:         %s%s\nlocks:         ";
  1118. X        insDelFormat = "  lines added/del: %lu/%lu";
  1119. X        symbolFormat = "  %s: %s;";
  1120. X    } else {
  1121. X        accessListString = "\naccess list:";
  1122. X        accessFormat = "\n\t%s";
  1123. X        commentFormat = "\ncomment leader: \"";
  1124. X        headFormat = "\nRCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s";
  1125. X        insDelFormat = "  lines: +%lu -%lu";
  1126. X        symbolFormat = "\n\t%s: %s";
  1127. X    }
  1128. X
  1129. X        /* now handle all filenames */
  1130. X        do {
  1131. X        finptr = NULL;
  1132. X        ffree();
  1133. X
  1134. X        if (!pairfilenames(argc, argv, rcsreadopen, true, false))
  1135. X        continue;
  1136. X
  1137. X            /* now RCSfilename contains the name of the RCS file, and finptr
  1138. X             * the file descriptor. Workfilename contains the name of the
  1139. X             * working file.
  1140. X             */
  1141. X
  1142. X        /* Keep only those locks given by -l.  */
  1143. X        if (lockflag)
  1144. X        trunclocks();
  1145. X
  1146. X            /* do nothing if -L is given and there are no locks*/
  1147. X        if (onlylockflag && !Locks)
  1148. X        continue;
  1149. X
  1150. X        if ( onlyRCSflag ) {
  1151. X        aprintf(stdout, "%s\n", RCSfilename);
  1152. X        continue;
  1153. X        }
  1154. X            /*   print RCS filename , working filename and optional
  1155. X                 administrative information                         */
  1156. X            /* could use getfullRCSname() here, but that is very slow */
  1157. X        aprintf(stdout, headFormat, RCSfilename, workfilename,
  1158. X            Head ? " " : "",  Head ? Head->num : "",
  1159. X            Dbranch ? " " : "",  Dbranch ? Dbranch : "",
  1160. X            StrictLocks ? " strict" : ""
  1161. X        );
  1162. X            currlock = Locks;
  1163. X            while( currlock ) {
  1164. X        aprintf(stdout, symbolFormat, currlock->login,
  1165. X                                currlock->delta->num);
  1166. X                currlock = currlock->nextlock;
  1167. X            }
  1168. X            if (StrictLocks && RCSversion<VERSION(5))
  1169. X        aputs("  strict", stdout);
  1170. X
  1171. X        aputs(accessListString, stdout);      /*  print access list  */
  1172. X            curaccess = AccessList;
  1173. X            while(curaccess) {
  1174. X        aprintf(stdout, accessFormat, curaccess->login);
  1175. X                curaccess = curaccess->nextaccess;
  1176. X            }
  1177. X
  1178. X        aputs("\nsymbolic names:", stdout);   /*  print symbolic names   */
  1179. X        for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc)
  1180. X        aprintf(stdout, symbolFormat, curassoc->symbol, curassoc->num);
  1181. X        aputs(commentFormat, stdout);
  1182. X        awrite(Comment.string, Comment.size, stdout);
  1183. X        aputs("\"\n", stdout);
  1184. X        if (VERSION(5)<=RCSversion  ||  Expand != KEYVAL_EXPAND)
  1185. X        aprintf(stdout, "keyword substitution: %s\n",
  1186. X            expand_names[Expand]
  1187. X        );
  1188. X
  1189. X            gettree();
  1190. X
  1191. X        aprintf(stdout, "total revisions: %d", TotalDeltas);
  1192. X
  1193. X            if ( Head == nil || !selectflag || !descflag) {
  1194. X        afputc('\n',stdout);
  1195. X        if (descflag) aputs("description:\n", stdout);
  1196. X                getdesc(descflag);
  1197. X        goto rlogend;
  1198. X            }
  1199. X
  1200. X
  1201. X            getnumericrev();    /* get numeric revision or branch names */
  1202. X            revno = 0;
  1203. X
  1204. X            exttree(Head);
  1205. X
  1206. X            /*  get most recently date of the dates pointed by duelst  */
  1207. X            currdate = duelst;
  1208. X            while( currdate) {
  1209. X                recentdate(Head, currdate);
  1210. X                currdate = currdate->dnext;
  1211. X        }
  1212. X
  1213. X            extdate(Head);
  1214. X
  1215. X            /*  reinitialize the date specification list   */
  1216. X            currdate = duelst;
  1217. X            while(currdate) {
  1218. X                VOID sprintf(currdate->strtdate,DATEFORM,0,0,0,0,0,0);
  1219. X                currdate = currdate->dnext;
  1220. X            }
  1221. X
  1222. X            if ( selectop || ( selectflag && descflag) )
  1223. X        aprintf(stdout, ";\tselected revisions: %d", revno);
  1224. X        afputc('\n', stdout);
  1225. X        if (descflag) aputs("description:\n", stdout);
  1226. X            getdesc(descflag);
  1227. X            if (selectflag && descflag && revno) {
  1228. X        while (readdeltalog())
  1229. X            ;
  1230. X                putrunk();
  1231. X                putree(Head);
  1232. X        if (nexttok != EOFILE)
  1233. X            fatserror("expecting EOF");
  1234. X            }
  1235. X    rlogend:
  1236. X        aputs("=============================================================================\n",stdout);
  1237. X    } while (cleanup(),
  1238. X         ++argv, --argc >= 1);
  1239. X    exitmain(exitstatus);
  1240. X}
  1241. X
  1242. X    static void
  1243. Xcleanup()
  1244. X{
  1245. X    if (nerror) exitstatus = EXIT_FAILURE;
  1246. X    if (finptr) ffclose(finptr);
  1247. X}
  1248. X
  1249. X#if lint
  1250. X#    define exiterr rlogExit
  1251. X#endif
  1252. X    exiting void
  1253. Xexiterr()
  1254. X{
  1255. X    _exit(EXIT_FAILURE);
  1256. X}
  1257. X
  1258. X
  1259. X
  1260. X    static void
  1261. Xputrunk()
  1262. X/*  function:  print revisions chosen, which are in trunk      */
  1263. X
  1264. X{
  1265. X    register const struct hshentry *ptr;
  1266. X
  1267. X    for (ptr = Head;  ptr;  ptr = ptr->next)
  1268. X        putadelta(ptr, ptr->next, true);
  1269. X}
  1270. X
  1271. X
  1272. X
  1273. X    static void
  1274. Xputree(root)
  1275. X    const struct hshentry *root;
  1276. X/*   function: print delta tree (not including trunk) in reverse
  1277. X               order on each branch                                        */
  1278. X
  1279. X{
  1280. X        if ( root == nil ) return;
  1281. X
  1282. X        putree(root->next);
  1283. X
  1284. X        putforest(root->branches);
  1285. X}
  1286. X
  1287. X
  1288. X
  1289. X
  1290. X    static void
  1291. Xputforest(branchroot)
  1292. X    const struct branchhead *branchroot;
  1293. X/*   function:  print branches that has the same direct ancestor    */
  1294. X{
  1295. X
  1296. X        if ( branchroot == nil ) return;
  1297. X
  1298. X        putforest(branchroot->nextbranch);
  1299. X
  1300. X        putabranch(branchroot->hsh);
  1301. X        putree(branchroot->hsh);
  1302. X}
  1303. X
  1304. X
  1305. X
  1306. X
  1307. X    static void
  1308. Xputabranch(root)
  1309. X    const struct hshentry *root;
  1310. X/*   function  :  print one branch     */
  1311. X
  1312. X{
  1313. X
  1314. X        if ( root == nil) return;
  1315. X
  1316. X        putabranch(root->next);
  1317. X
  1318. X        putadelta(root, root, false);
  1319. X}
  1320. X
  1321. X
  1322. X
  1323. X
  1324. X
  1325. X    static void
  1326. Xputadelta(node,editscript,trunk)
  1327. X    register const struct hshentry *node, *editscript;
  1328. X    int trunk;
  1329. X/*  function: Print delta node if node->selector is set.        */
  1330. X/*      editscript indicates where the editscript is stored     */
  1331. X/*      trunk indicated whether this node is in trunk           */
  1332. X{
  1333. X    const struct branchhead *newbranch;
  1334. X    struct buf branchnum;
  1335. X
  1336. X    if (!node->selector)
  1337. X            return;
  1338. X
  1339. X    aprintf(stdout,
  1340. X        "----------------------------\nrevision %s", node->num
  1341. X    );
  1342. X        if ( node->lockedby )
  1343. X       aprintf(stdout, "\tlocked by: %s;", node->lockedby);
  1344. X
  1345. X    aputs("\ndate: ",stdout);
  1346. X    printdate(stdout, node->date, " ");
  1347. X    aprintf(stdout, ";  author: %s;  state: %s;",
  1348. X        node->author, node->state
  1349. X    );
  1350. X
  1351. X        if ( editscript )
  1352. X           if(trunk)
  1353. X          aprintf(stdout, insDelFormat,
  1354. X                             editscript->deletelns, editscript->insertlns);
  1355. X           else
  1356. X          aprintf(stdout, insDelFormat,
  1357. X                             editscript->insertlns, editscript->deletelns);
  1358. X
  1359. X        newbranch = node->branches;
  1360. X        if ( newbranch ) {
  1361. X       bufautobegin(&branchnum);
  1362. X       aputs("\nbranches:", stdout);
  1363. X           while( newbranch ) {
  1364. X        getbranchno(newbranch->hsh->num, &branchnum);
  1365. X        aprintf(stdout, "  %s;", branchnum.string);
  1366. X                newbranch = newbranch->nextbranch;
  1367. X           }
  1368. X       bufautoend(&branchnum);
  1369. X        }
  1370. X
  1371. X    afputc('\n', stdout);
  1372. X    awrite(node->log.string, node->log.size, stdout);
  1373. X}
  1374. X
  1375. X
  1376. X
  1377. X
  1378. X
  1379. X    static int
  1380. Xreaddeltalog()
  1381. X/*  Function : get the log message and skip the text of a deltatext node.
  1382. X *             Return false if current block does not start with a number.
  1383. X *             Assumes the current lexeme is not yet in nexttok; does not
  1384. X *             advance nexttok.
  1385. X */
  1386. X{
  1387. X        register struct  hshentry  * Delta;
  1388. X    struct buf logbuf;
  1389. X
  1390. X        nextlex();
  1391. X        if ( !(Delta = getnum() )) return(false);
  1392. X    getkeystring(Klog);
  1393. X    bufautobegin(&logbuf);
  1394. X    Delta->log = savestring(&logbuf);
  1395. X    /*
  1396. X     * Do the following instead of bufautoend(&logbuf),
  1397. X     * because the buffer must survive until we are done with the file.
  1398. X     */
  1399. X    Delta->log.string = (char *)fremember(testrealloc(
  1400. X        (malloc_type)logbuf.string,
  1401. X        Delta->log.size
  1402. X    ));
  1403. X
  1404. X        nextlex();
  1405. X    while (nexttok==ID && strcmp(NextString,Ktext)!=0)
  1406. X        ignorephrase();
  1407. X    getkeystring(Ktext);
  1408. X        Delta->insertlns = Delta->deletelns = 0;
  1409. X        if ( Delta != Head)
  1410. X                getscript(Delta);
  1411. X        else
  1412. X                readstring();
  1413. X        return true;
  1414. X}
  1415. X
  1416. X
  1417. X
  1418. X    static void
  1419. Xgetscript(Delta)
  1420. Xstruct    hshentry   * Delta;
  1421. X/*   function:  read edit script of Delta and count how many lines added  */
  1422. X/*              and deleted in the script                                 */
  1423. X
  1424. X{
  1425. X        int ed;   /*  editor command  */
  1426. X    register FILE * fin;
  1427. X        register  int   c;
  1428. X    register unsigned long i;
  1429. X    struct diffcmd dc;
  1430. X
  1431. X    fin = finptr;
  1432. X    initdiffcmd(&dc);
  1433. X    while (0  <=  (ed = getdiffcmd(fin,SDELIM,(FILE *)0,&dc)))
  1434. X        if (!ed)
  1435. X                 Delta->deletelns += dc.nlines;
  1436. X        else {
  1437. X                 /*  skip scripted lines  */
  1438. X         i = dc.nlines;
  1439. X         Delta->insertlns += i;
  1440. X         do {
  1441. X             while ((c=getc(fin)) != '\n')
  1442. X            if (c==EOF  ||  c==SDELIM && (c=getc(fin))!=SDELIM) {
  1443. X                if (c==EOF || i!=1)
  1444. X                fatserror("unexpected end to edit script");
  1445. X                nextc = c;
  1446. X                return;
  1447. X            }
  1448. X             ++rcsline;
  1449. X         } while (--i);
  1450. X            }
  1451. X    nextc = getc(fin);
  1452. X}
  1453. X
  1454. X
  1455. X
  1456. X
  1457. X
  1458. X
  1459. X
  1460. X    static void
  1461. Xexttree(root)
  1462. Xstruct hshentry  *root;
  1463. X/*  function: select revisions , starting with root             */
  1464. X
  1465. X{
  1466. X    const struct branchhead *newbranch;
  1467. X
  1468. X        if (root == nil) return;
  1469. X
  1470. X    root->selector = extractdelta(root);
  1471. X        exttree(root->next);
  1472. X
  1473. X        newbranch = root->branches;
  1474. X        while( newbranch ) {
  1475. X            exttree(newbranch->hsh);
  1476. X            newbranch = newbranch->nextbranch;
  1477. X        }
  1478. X}
  1479. X
  1480. X
  1481. X
  1482. X
  1483. X    static void
  1484. Xgetlocker(argv)
  1485. Xchar    * argv;
  1486. X/*   function : get the login names of lockers from command line   */
  1487. X/*              and store in lockerlist.                           */
  1488. X
  1489. X{
  1490. X        register char c;
  1491. X        struct   lockers   * newlocker;
  1492. X        argv--;
  1493. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  1494. X                 c == '\n' || c == ';')  ;
  1495. X        if (  c == '\0') {
  1496. X            lockerlist=nil;
  1497. X            return;
  1498. X        }
  1499. X
  1500. X        while( c != '\0' ) {
  1501. X        newlocker = talloc(struct lockers);
  1502. X            newlocker->lockerlink = lockerlist;
  1503. X            newlocker->login = argv;
  1504. X            lockerlist = newlocker;
  1505. X            while ( ( c = (*++argv)) != ',' && c != '\0' && c != ' '
  1506. X                       && c != '\t' && c != '\n' && c != ';') ;
  1507. X            *argv = '\0';
  1508. X            if ( c == '\0' ) return;
  1509. X            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  1510. X                     c == '\n' || c == ';')  ;
  1511. X        }
  1512. X}
  1513. X
  1514. X
  1515. X
  1516. X    static void
  1517. Xgetauthor(argv)
  1518. Xchar   *argv;
  1519. X/*   function:  get the author's name from command line   */
  1520. X/*              and store in authorlist                   */
  1521. X
  1522. X{
  1523. X        register    c;
  1524. X        struct     authors  * newauthor;
  1525. X
  1526. X        argv--;
  1527. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  1528. X                 c == '\n' || c == ';')  ;
  1529. X        if ( c == '\0' ) {
  1530. X        authorlist = talloc(struct authors);
  1531. X        authorlist->login = getcaller();
  1532. X            authorlist->nextauthor  = nil;
  1533. X            return;
  1534. X        }
  1535. X
  1536. X        while( c != '\0' ) {
  1537. X        newauthor = talloc(struct authors);
  1538. X            newauthor->nextauthor = authorlist;
  1539. X            newauthor->login = argv;
  1540. X            authorlist = newauthor;
  1541. X            while( ( c = *++argv) != ',' && c != '\0' && c != ' '
  1542. X                     && c != '\t' && c != '\n' && c != ';') ;
  1543. X            * argv = '\0';
  1544. X            if ( c == '\0') return;
  1545. X            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  1546. X                     c == '\n' || c == ';')  ;
  1547. X        }
  1548. X}
  1549. X
  1550. X
  1551. X
  1552. X
  1553. X    static void
  1554. Xgetstate(argv)
  1555. Xchar   * argv;
  1556. X/*   function :  get the states of revisions from command line  */
  1557. X/*               and store in statelist                         */
  1558. X
  1559. X{
  1560. X        register  char  c;
  1561. X        struct    stateattri    *newstate;
  1562. X
  1563. X        argv--;
  1564. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  1565. X                 c == '\n' || c == ';')  ;
  1566. X        if ( c == '\0'){
  1567. X        warn("missing state attributes after -s options");
  1568. X            return;
  1569. X        }
  1570. X
  1571. X        while( c != '\0' ) {
  1572. X        newstate = talloc(struct stateattri);
  1573. X            newstate->nextstate = statelist;
  1574. X            newstate->status = argv;
  1575. X            statelist = newstate;
  1576. X            while( (c = (*++argv)) != ',' && c != '\0' && c != ' '
  1577. X                    && c != '\t' && c != '\n' && c != ';')  ;
  1578. X            *argv = '\0';
  1579. X            if ( c == '\0' ) return;
  1580. X            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  1581. X                     c == '\n' || c == ';')  ;
  1582. X        }
  1583. X}
  1584. X
  1585. X
  1586. X
  1587. X    static void
  1588. Xtrunclocks()
  1589. X/*  Function:  Truncate the list of locks to those that are held by the  */
  1590. X/*             id's on lockerlist. Do not truncate if lockerlist empty.  */
  1591. X
  1592. X{
  1593. X    const struct lockers *plocker;
  1594. X        struct lock     * plocked,  * nextlocked;
  1595. X
  1596. X        if ( (lockerlist == nil) || (Locks == nil)) return;
  1597. X
  1598. X        /* shorten Locks to those contained in lockerlist */
  1599. X        plocked = Locks;
  1600. X        Locks = nil;
  1601. X        while( plocked != nil) {
  1602. X            plocker = lockerlist;
  1603. X            while((plocker != nil) && ( strcmp(plocker->login, plocked->login)!=0))
  1604. X                plocker = plocker->lockerlink;
  1605. X            nextlocked = plocked->nextlock;
  1606. X            if ( plocker != nil) {
  1607. X                plocked->nextlock = Locks;
  1608. X                Locks = plocked;
  1609. X            }
  1610. X            plocked = nextlocked;
  1611. X        }
  1612. X}
  1613. X
  1614. X
  1615. X
  1616. X    static void
  1617. Xrecentdate(root, pd)
  1618. X    const struct hshentry *root;
  1619. X    struct Datepairs *pd;
  1620. X/*  function:  Finds the delta that is closest to the cutoff date given by   */
  1621. X/*             pd among the revisions selected by exttree.                   */
  1622. X/*             Successively narrows down the interval given by pd,           */
  1623. X/*             and sets the strtdate of pd to the date of the selected delta */
  1624. X{
  1625. X    const struct branchhead *newbranch;
  1626. X
  1627. X    if ( root == nil) return;
  1628. X    if (root->selector) {
  1629. X             if ( cmpnum(root->date, pd->strtdate) >= 0 &&
  1630. X                  cmpnum(root->date, pd->enddate) <= 0)
  1631. X        VOID strcpy(pd->strtdate, root->date);
  1632. X        }
  1633. X
  1634. X        recentdate(root->next, pd);
  1635. X        newbranch = root->branches;
  1636. X        while( newbranch) {
  1637. X           recentdate(newbranch->hsh, pd);
  1638. X           newbranch = newbranch->nextbranch;
  1639. X    }
  1640. X}
  1641. X
  1642. X
  1643. X
  1644. X
  1645. X
  1646. X
  1647. X    static void
  1648. Xextdate(root)
  1649. Xstruct  hshentry        * root;
  1650. X/*  function:  select revisions which are in the date range specified     */
  1651. X/*             in duelst  and datelist, start at root                     */
  1652. X
  1653. X{
  1654. X    const struct branchhead *newbranch;
  1655. X    const struct Datepairs *pdate;
  1656. X
  1657. X        if ( root == nil) return;
  1658. X
  1659. X        if ( datelist || duelst) {
  1660. X            pdate = datelist;
  1661. X            while( pdate ) {
  1662. X                if ( (pdate->strtdate)[0] == '\0' || cmpnum(root->date,pdate->strtdate) >= 0){
  1663. X                   if ((pdate->enddate)[0] == '\0' || cmpnum(pdate->enddate,root->date) >= 0)
  1664. X                        break;
  1665. X                }
  1666. X                pdate = pdate->dnext;
  1667. X            }
  1668. X            if ( pdate == nil) {
  1669. X                pdate = duelst;
  1670. X        for (;;) {
  1671. X           if (!pdate) {
  1672. X            root->selector = false;
  1673. X            break;
  1674. X           }
  1675. X                   if ( cmpnum(root->date, pdate->strtdate) == 0)
  1676. X                      break;
  1677. X                   pdate = pdate->dnext;
  1678. X                }
  1679. X            }
  1680. X        }
  1681. X    if (root->selector)
  1682. X        ++revno;
  1683. X
  1684. X        extdate(root->next);
  1685. X
  1686. X        newbranch = root->branches;
  1687. X        while( newbranch ) {
  1688. X           extdate(newbranch->hsh);
  1689. X           newbranch = newbranch->nextbranch;
  1690. X        }
  1691. X}
  1692. X
  1693. X
  1694. X
  1695. X    static char
  1696. Xextractdelta(pdelta)
  1697. X    const struct hshentry *pdelta;
  1698. X/*  function:  compare information of pdelta to the authorlist, lockerlist,*/
  1699. X/*             statelist, revlist and yield true if pdelta is selected.    */
  1700. X
  1701. X{
  1702. X    const struct lock *plock;
  1703. X    const struct stateattri *pstate;
  1704. X    const struct authors *pauthor;
  1705. X    const struct Revpairs *prevision;
  1706. X    unsigned length;
  1707. X
  1708. X    if ((pauthor = authorlist)) /* only certain authors wanted */
  1709. X        while (strcmp(pauthor->login, pdelta->author) != 0)
  1710. X        if (!(pauthor = pauthor->nextauthor))
  1711. X            return false;
  1712. X    if ((pstate = statelist)) /* only certain states wanted */
  1713. X        while (strcmp(pstate->status, pdelta->state) != 0)
  1714. X        if (!(pstate = pstate->nextstate))
  1715. X            return false;
  1716. X    if (lockflag) /* only locked revisions wanted */
  1717. X        for (plock = Locks;  ;  plock = plock->nextlock)
  1718. X        if (!plock)
  1719. X            return false;
  1720. X        else if (plock->delta == pdelta)
  1721. X            break;
  1722. X    if ((prevision = Revlst)) /* only certain revs or branches wanted */
  1723. X        for (;;) {
  1724. X                length = prevision->numfld;
  1725. X        if (
  1726. X            countnumflds(pdelta->num) == length+(length&1) &&
  1727. X            0 <= compartial(pdelta->num, prevision->strtrev, length) &&
  1728. X            0 <= compartial(prevision->endrev, pdelta->num, length)
  1729. X        )
  1730. X             break;
  1731. X        if (!(prevision = prevision->rnext))
  1732. X            return false;
  1733. X            }
  1734. X    return true;
  1735. X}
  1736. X
  1737. X
  1738. X
  1739. X    static void
  1740. Xgetdatepair(argv)
  1741. X   char   * argv;
  1742. X/*  function:  get time range from command line and store in datelist if    */
  1743. X/*             a time range specified or in duelst if a time spot specified */
  1744. X
  1745. X{
  1746. X        register   char         c;
  1747. X        struct     Datepairs    * nextdate;
  1748. X    const char        * rawdate;
  1749. X    int                     switchflag;
  1750. X
  1751. X        argv--;
  1752. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  1753. X                 c == '\n' || c == ';')  ;
  1754. X        if ( c == '\0' ) {
  1755. X        warn("missing date/time after -d");
  1756. X            return;
  1757. X        }
  1758. X
  1759. X        while( c != '\0' )  {
  1760. X        switchflag = false;
  1761. X        nextdate = talloc(struct Datepairs);
  1762. X            if ( c == '<' ) {   /*   case: -d <date   */
  1763. X                c = *++argv;
  1764. X                (nextdate->strtdate)[0] = '\0';
  1765. X        } else if (c == '>') { /* case: -d'>date' */
  1766. X        c = *++argv;
  1767. X        (nextdate->enddate)[0] = '\0';
  1768. X        switchflag = true;
  1769. X        } else {
  1770. X                rawdate = argv;
  1771. X        while( c != '<' && c != '>' && c != ';' && c != '\0')
  1772. X             c = *++argv;
  1773. X                *argv = '\0';
  1774. X        if ( c == '>' ) switchflag=true;
  1775. X        str2date(rawdate,
  1776. X             switchflag ? nextdate->enddate : nextdate->strtdate);
  1777. X        if ( c == ';' || c == '\0') {  /*  case: -d date  */
  1778. X            VOID strcpy(nextdate->enddate,nextdate->strtdate);
  1779. X            VOID sprintf(nextdate->strtdate,DATEFORM,0,0,0,0,0,0);
  1780. X                    nextdate->dnext = duelst;
  1781. X                    duelst = nextdate;
  1782. X            goto end;
  1783. X        } else {
  1784. X            /*   case:   -d date<  or -d  date>; see switchflag */
  1785. X            while ( (c= *++argv) == ' ' || c=='\t' || c=='\n');
  1786. X            if ( c == ';' || c == '\0') {
  1787. X            /* second date missing */
  1788. X            if (switchflag)
  1789. X                *nextdate->strtdate= '\0';
  1790. X            else
  1791. X                *nextdate->enddate= '\0';
  1792. X            nextdate->dnext = datelist;
  1793. X            datelist = nextdate;
  1794. X            goto end;
  1795. X            }
  1796. X                }
  1797. X            }
  1798. X            rawdate = argv;
  1799. X        while( c != '>' && c != '<' && c != ';' && c != '\0')
  1800. X         c = *++argv;
  1801. X            *argv = '\0';
  1802. X        str2date(rawdate,
  1803. X             switchflag ? nextdate->strtdate : nextdate->enddate);
  1804. X            nextdate->dnext = datelist;
  1805. X        datelist = nextdate;
  1806. X     end:
  1807. X        if ( c == '\0')  return;
  1808. X            while( (c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n');
  1809. X        }
  1810. X}
  1811. X
  1812. X
  1813. X
  1814. X    static void
  1815. Xgetnumericrev()
  1816. X/*  function:  get the numeric name of revisions which stored in revlist  */
  1817. X/*             and then stored the numeric names in Revlst                */
  1818. X/*             if branchflag, also add default branch                     */
  1819. X
  1820. X{
  1821. X        struct  Revpairs        * ptr, *pt;
  1822. X    unsigned n;
  1823. X    struct buf s, e;
  1824. X    const struct buf *rstart, *rend;
  1825. X
  1826. X        Revlst = nil;
  1827. X        ptr = revlist;
  1828. X    bufautobegin(&s);
  1829. X    bufautobegin(&e);
  1830. X        while( ptr ) {
  1831. X        n = 0;
  1832. X        rstart = &s;
  1833. X        rend = &e;
  1834. X
  1835. X        switch (ptr->numfld) {
  1836. X
  1837. X          case 1: /* -r rev */
  1838. X        if (expandsym(ptr->strtrev, &s)) {
  1839. X            rend = &s;
  1840. X            n = countnumflds(s.string);
  1841. X                }
  1842. X        break;
  1843. X
  1844. X          case 2: /* -r rev- */
  1845. X        if (expandsym(ptr->strtrev, &s)) {
  1846. X            bufscpy(&e, s.string);
  1847. X            n = countnumflds(s.string);
  1848. X            (n<2 ? e.string : strrchr(e.string,'.'))[0]  =  0;
  1849. X                }
  1850. X        break;
  1851. X
  1852. X          case 3: /* -r -rev */
  1853. X        if (expandsym(ptr->endrev, &e)) {
  1854. X            if ((n = countnumflds(e.string)) < 2)
  1855. X            bufscpy(&s, ".1");
  1856. X            else {
  1857. X            bufscpy(&s, e.string);
  1858. X            VOID strcpy(strrchr(s.string,'.'), ".1");
  1859. X            }
  1860. X                }
  1861. X        break;
  1862. X
  1863. X          default: /* -r rev1-rev2 */
  1864. X        if (
  1865. X            expandsym(ptr->strtrev, &s)
  1866. X            &&    expandsym(ptr->endrev, &e)
  1867. X            &&    checkrevpair(s.string, e.string)
  1868. X        ) {
  1869. X            n = countnumflds(s.string);
  1870. X            /* Swap if out of order.  */
  1871. X            if (compartial(s.string,e.string,n) > 0) {
  1872. X            rstart = &e;
  1873. X            rend = &s;
  1874. X            }
  1875. X        }
  1876. X        break;
  1877. X        }
  1878. X
  1879. X        if (n) {
  1880. X        pt = ftalloc(struct Revpairs);
  1881. X        pt->numfld = n;
  1882. X        pt->strtrev = fstrsave(rstart->string);
  1883. X        pt->endrev = fstrsave(rend->string);
  1884. X                pt->rnext = Revlst;
  1885. X                Revlst = pt;
  1886. X        }
  1887. X        ptr = ptr->rnext;
  1888. X        }
  1889. X        /* Now take care of branchflag */
  1890. X    if (branchflag && (Dbranch||Head)) {
  1891. X        pt = ftalloc(struct Revpairs);
  1892. X        pt->strtrev = pt->endrev =
  1893. X        Dbranch ? Dbranch : fstrsave(partialno(&s,Head->num,1));
  1894. X        pt->rnext=Revlst; Revlst=pt;
  1895. X        pt->numfld = countnumflds(pt->strtrev);
  1896. X        }
  1897. X    bufautoend(&s);
  1898. X    bufautoend(&e);
  1899. X}
  1900. X
  1901. X
  1902. X
  1903. X    static int
  1904. Xcheckrevpair(num1,num2)
  1905. X    const char *num1, *num2;
  1906. X/*  function:  check whether num1, num2 are legal pair,i.e.
  1907. X    only the last field are different and have same number of
  1908. X    fields( if length <= 2, may be different if first field)   */
  1909. X
  1910. X{
  1911. X    unsigned length = countnumflds(num1);
  1912. X
  1913. X    if (
  1914. X            countnumflds(num2) != length
  1915. X        ||    2 < length  &&  compartial(num1, num2, length-1) != 0
  1916. X    ) {
  1917. X        error("invalid branch or revision pair %s : %s", num1, num2);
  1918. X            return false;
  1919. X        }
  1920. X
  1921. X        return true;
  1922. X}
  1923. X
  1924. X
  1925. X
  1926. X    static void
  1927. Xgetrevpairs(argv)
  1928. Xregister     char    * argv;
  1929. X/*  function:  get revision or branch range from command line, and   */
  1930. X/*             store in revlist                                      */
  1931. X
  1932. X{
  1933. X        register    char    c;
  1934. X        struct      Revpairs  * nextrevpair;
  1935. X        int         flag;
  1936. X
  1937. X        argv--;
  1938. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  1939. X                 c == '\n' || c == ';')  ;
  1940. X        if ( c == '\0' ) {
  1941. X        warn("missing revision or branch number after -r");
  1942. X            return;
  1943. X        }
  1944. X
  1945. X        while( c != '\0') {
  1946. X            while(  c  == ',' || c == ' ' || c == '\t' ||
  1947. X                     c == '\n' || c == ';') c = *++argv;
  1948. X            if (c == '\0')  return;
  1949. X        nextrevpair = talloc(struct Revpairs);
  1950. X            nextrevpair->rnext = revlist;
  1951. X            revlist = nextrevpair;
  1952. X        nextrevpair->numfld = 0;
  1953. X            nextrevpair->strtrev = nil;
  1954. X            nextrevpair->endrev  = nil;
  1955. X            flag = false;
  1956. X            if (  c == '<' || c == '-' ) {  /*  case: -r -rev  or -r <rev  */
  1957. X                flag = true;
  1958. X                while( (c =(*++argv)) == ' ' || c == '\t' || c =='\n') ;
  1959. X            }
  1960. X            else {
  1961. X                nextrevpair->strtrev = argv;
  1962. X                /*   get a revision or branch name  */
  1963. X                while( c != ',' && c != ';' && c != ' ' && c != '\0' && c != '-'
  1964. X                        && c != '\t' && c != '\n' && c != '<') c = *++argv;
  1965. X
  1966. X                *argv = '\0';
  1967. X
  1968. X                if ( c != '<' && c != '-') {    /*  case: rev  */
  1969. X                    nextrevpair->numfld = 1;
  1970. X                    continue;
  1971. X                }
  1972. X
  1973. X                if ( (c =(*++argv)) == ',' || c == '\0' || c == ' '
  1974. X                      || c == '\t' || c == '\n' || c == ';') {/*  case: rev_  */
  1975. X                    nextrevpair->numfld = 2;
  1976. X                    continue;
  1977. X                }
  1978. X            }
  1979. X            nextrevpair->endrev = argv;
  1980. X            while( c != ',' && c != ' ' && c != '\0' && c != '\t' && c != '<'
  1981. X                   && c != '\n' && c != '-' && c != ';')  c = *++argv;
  1982. X
  1983. X            * argv = '\0';
  1984. X            if ( c == '<'){
  1985. X        error("separator expected near %s", nextrevpair->endrev);
  1986. X                while( (c = *++argv) != ',' && c != ' ' && c != '\0' &&
  1987. X                        c != '\t' && c != '\n' && c != ';' ) ;
  1988. X                revlist = nextrevpair->rnext;
  1989. X                continue;
  1990. X            }
  1991. X            else  {
  1992. X                if (flag)   /*  case:  -rev   */
  1993. X                    nextrevpair->numfld  = 3;
  1994. X
  1995. X                else     /*   rev1-rev2  appears  */
  1996. X                    nextrevpair->numfld = 4;
  1997. X            }
  1998. X        }
  1999. X}
  2000. END_OF_FILE
  2001.   if test 32911 -ne `wc -c <'src/rlog.c'`; then
  2002.     echo shar: \"'src/rlog.c'\" unpacked with wrong size!
  2003.   fi
  2004.   # end of 'src/rlog.c'
  2005. fi
  2006. echo shar: End of archive 4 \(of 12\).
  2007. cp /dev/null ark4isdone
  2008. MISSING=""
  2009. for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
  2010.     if test ! -f ark${I}isdone ; then
  2011.     MISSING="${MISSING} ${I}"
  2012.     fi
  2013. done
  2014. if test "${MISSING}" = "" ; then
  2015.     echo You have unpacked all 12 archives.
  2016.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2017. else
  2018.     echo You still must unpack the following archives:
  2019.     echo "        " ${MISSING}
  2020. fi
  2021. exit 0
  2022. exit 0 # Just in case...
  2023.