home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume24 / rcs / part07 < prev    next >
Text File  |  1991-03-05  |  54KB  |  1,805 lines

  1. Subject:  v24i007:  RCS source control system, Part07/12
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 5267d696 2667712a 5d7f1b27 d40cfeda
  5.  
  6. Submitted-by: Adam Hammer <hammer@cs.purdue.edu>
  7. Posting-number: Volume 24, Issue 7
  8. Archive-name: rcs/part07
  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/co.c src/rcsfcmp.c src/rcsrev.c
  17. # Wrapped by rsalz@litchi.bbn.com on Thu Feb 21 14:37:02 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 7 (of 12)."'
  21. if test -f 'src/co.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'src/co.c'\"
  23. else
  24.   echo shar: Extracting \"'src/co.c'\" \(21836 characters\)
  25.   sed "s/^X//" >'src/co.c' <<'END_OF_FILE'
  26. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  27. X   Copyright 1990 by Paul Eggert
  28. X   Distributed under license by the Free Software Foundation, Inc.
  29. X
  30. XThis file is part of RCS.
  31. X
  32. XRCS is free software; you can redistribute it and/or modify
  33. Xit under the terms of the GNU General Public License as published by
  34. Xthe Free Software Foundation; either version 1, or (at your option)
  35. Xany later version.
  36. X
  37. XRCS is distributed in the hope that it will be useful,
  38. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  39. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  40. XGNU General Public License for more details.
  41. X
  42. XYou should have received a copy of the GNU General Public License
  43. Xalong with RCS; see the file COPYING.  If not, write to
  44. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  45. X
  46. XReport problems and direct all questions to:
  47. X
  48. X    rcs-bugs@cs.purdue.edu
  49. X
  50. X*/
  51. X
  52. X/*
  53. X *                     RCS checkout operation
  54. X */
  55. X/*****************************************************************************
  56. X *                       check out revisions from RCS files
  57. X *****************************************************************************
  58. X */
  59. X
  60. X
  61. X/* $Log: co.c,v $
  62. X * Revision 5.6  1990/12/04  05:18:38  eggert
  63. X * Don't checkaccesslist() unless necessary.
  64. X * Use -I for prompts and -q for diagnostics.
  65. X *
  66. X * Revision 5.5  1990/11/01  05:03:26  eggert
  67. X * Fix -j.  Add -I.
  68. X *
  69. X * Revision 5.4  1990/10/04  06:30:11  eggert
  70. X * Accumulate exit status across files.
  71. X *
  72. X * Revision 5.3  1990/09/11  02:41:09  eggert
  73. X * co -kv yields a readonly working file.
  74. X *
  75. X * Revision 5.2  1990/09/04  08:02:13  eggert
  76. X * Standardize yes-or-no procedure.
  77. X *
  78. X * Revision 5.0  1990/08/22  08:10:02  eggert
  79. X * Permit multiple locks by same user.  Add setuid support.
  80. X * Remove compile-time limits; use malloc instead.
  81. X * Permit dates past 1999/12/31.  Switch to GMT.
  82. X * Make lock and temp files faster and safer.
  83. X * Ansify and Posixate.  Add -k, -V.  Remove snooping.  Tune.
  84. X *
  85. X * Revision 4.7  89/05/01  15:11:41  narten
  86. X * changed copyright header to reflect current distribution rules
  87. X * 
  88. X * Revision 4.6  88/08/09  19:12:15  eggert
  89. X * Fix "co -d" core dump; rawdate wasn't always initialized.
  90. X * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
  91. X * 
  92. X * Revision 4.5  87/12/18  11:35:40  narten
  93. X * lint cleanups (from Guy Harris)
  94. X * 
  95. X * Revision 4.4  87/10/18  10:20:53  narten
  96. X * Updating version numbers changes relative to 1.1, are actually
  97. X * relative to 4.2
  98. X * 
  99. X * Revision 1.3  87/09/24  13:58:30  narten
  100. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  101. X * warnings)
  102. X * 
  103. X * Revision 1.2  87/03/27  14:21:38  jenkins
  104. X * Port to suns
  105. X * 
  106. X * Revision 4.2  83/12/05  13:39:48  wft
  107. X * made rewriteflag external.
  108. X * 
  109. X * Revision 4.1  83/05/10  16:52:55  wft
  110. X * Added option -u and -f.
  111. X * Added handling of default branch.
  112. X * Replaced getpwuid() with getcaller().
  113. X * Removed calls to stat(); now done by pairfilenames().
  114. X * Changed and renamed rmoldfile() to rmworkfile().
  115. X * Replaced catchints() calls with restoreints(), unlink()--link() with rename();
  116. X * 
  117. X * Revision 3.7  83/02/15  15:27:07  wft
  118. X * Added call to fastcopy() to copy remainder of RCS file.
  119. X *
  120. X * Revision 3.6  83/01/15  14:37:50  wft
  121. X * Added ignoring of interrupts while RCS file is renamed; this avoids
  122. X * deletion of RCS files during the unlink/link window.
  123. X *
  124. X * Revision 3.5  82/12/08  21:40:11  wft
  125. X * changed processing of -d to use DATEFORM; removed actual from
  126. X * call to preparejoin; re-fixed printing of done at the end.
  127. X *
  128. X * Revision 3.4  82/12/04  18:40:00  wft
  129. X * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
  130. X * Fixed printing of "done".
  131. X *
  132. X * Revision 3.3  82/11/28  22:23:11  wft
  133. X * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
  134. X * %02d with %.2d, mode generation for working file with WORKMODE.
  135. X * Fixed nil printing. Fixed -j combined with -l and -p, and exit
  136. X * for non-existing revisions in preparejoin().
  137. X *
  138. X * Revision 3.2  82/10/18  20:47:21  wft
  139. X * Mode of working file is now maintained even for co -l, but write permission
  140. X * is removed.
  141. X * The working file inherits its mode from the RCS file, plus write permission
  142. X * for the owner. The write permission is not given if locking is strict and
  143. X * co does not lock.
  144. X * An existing working file without write permission is deleted automatically.
  145. X * Otherwise, co asks (empty answer: abort co).
  146. X * Call to getfullRCSname() added, check for write error added, call
  147. X * for getlogin() fixed.
  148. X *
  149. X * Revision 3.1  82/10/13  16:01:30  wft
  150. X * fixed type of variables receiving from getc() (char -> int).
  151. X * removed unused variables.
  152. X */
  153. X
  154. X
  155. X
  156. X
  157. X#include "rcsbase.h"
  158. X
  159. Xstatic const char *getancestor P((const char*,const char*));
  160. Xstatic int buildjoin P((const char*));
  161. Xstatic int creatempty P((void));
  162. Xstatic int fixworkmode P((const char*));
  163. Xstatic int preparejoin P((void));
  164. Xstatic int rmlock P((const struct hshentry*));
  165. Xstatic int rmworkfile P((void));
  166. Xstatic void cleanup P((void));
  167. X
  168. Xstatic const char quietarg[] = "-q";
  169. X
  170. Xstatic const char *join, *versionarg;
  171. Xstatic const char *joinlist[joinlength];/* revisions to be joined    */
  172. Xstatic int exitstatus;
  173. Xstatic int forceflag, tostdout;
  174. Xstatic int lastjoin;            /* index of last element in joinlist  */
  175. Xstatic int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */
  176. Xstatic struct hshentries *gendeltas;    /* deltas to be generated    */
  177. Xstatic struct hshentry *targetdelta;    /* final delta to be generated    */
  178. X
  179. XmainProg(coId, "co", "$Id: co.c,v 5.6 1990/12/04 05:18:38 eggert Exp $")
  180. X{
  181. X    static const char cmdusage[] =
  182. X        "\nco usage: co -{flpqru}[rev] -ddate -jjoinlist -sstate -w[login] -Vn file ...";
  183. X
  184. X    const char *author, *date, *rev, *state;
  185. X    const char *neworkfilename;
  186. X    int changelock;  /* 1 if a lock has been changed, -1 if error */
  187. X    int expmode, r;
  188. X    struct buf numericrev;    /* expanded revision number    */
  189. X    char finaldate[datesize];
  190. X
  191. X    initid();
  192. X    catchints();
  193. X    author = date = rev = state = nil;
  194. X    bufautobegin(&numericrev);
  195. X    expmode = -1;
  196. X    versionarg = nil;
  197. X
  198. X        while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  199. X                switch ((*argv)[1]) {
  200. X
  201. X                case 'r':
  202. X                revno:  if ((*argv)[2]!='\0') {
  203. X                if (rev) warn("redefinition of revision number");
  204. X                                rev = (*argv)+2;
  205. X                        }
  206. X                        break;
  207. X
  208. X        case 'f':
  209. X            forceflag=true;
  210. X            goto revno;
  211. X
  212. X                case 'l':
  213. X            if (lockflag < 0) {
  214. X                                warn("-l overrides -u.");
  215. X                        }
  216. X            lockflag = 1;
  217. X                        goto revno;
  218. X
  219. X                case 'u':
  220. X            if (0 < lockflag) {
  221. X                                warn("-l overrides -u.");
  222. X                        }
  223. X            lockflag = -1;
  224. X                        goto revno;
  225. X
  226. X                case 'p':
  227. X                        tostdout=true;
  228. X                        goto revno;
  229. X
  230. X        case 'I':
  231. X            interactiveflag = true;
  232. X            goto revno;
  233. X
  234. X                case 'q':
  235. X                        quietflag=true;
  236. X                        goto revno;
  237. X
  238. X                case 'd':
  239. X            if (date)
  240. X                redefined('d');
  241. X            str2date(*argv+2, finaldate);
  242. X                        date=finaldate;
  243. X                        break;
  244. X
  245. X                case 'j':
  246. X                        if ((*argv)[2]!='\0'){
  247. X                if (join) redefined('j');
  248. X                                join = (*argv)+2;
  249. X                        }
  250. X                        break;
  251. X
  252. X                case 's':
  253. X                        if ((*argv)[2]!='\0'){
  254. X                if (state) redefined('s');
  255. X                                state = (*argv)+2;
  256. X                        }
  257. X                        break;
  258. X
  259. X                case 'w':
  260. X            if (author) redefined('w');
  261. X                        if ((*argv)[2]!='\0')
  262. X                                author = (*argv)+2;
  263. X            else
  264. X                author = getcaller();
  265. X                        break;
  266. X
  267. X        case 'V':
  268. X            if (versionarg) redefined('V');
  269. X            versionarg = *argv;
  270. X            setRCSversion(versionarg);
  271. X            break;
  272. X
  273. X        case 'k':    /*  set keyword expand mode  */
  274. X            if (0 <= expmode) redefined('k');
  275. X            if (0 <= (expmode = str2expmode(*argv+2)))
  276. X                break;
  277. X            /* fall into */
  278. X                default:
  279. X            faterror("unknown option: %s%s", *argv, cmdusage);
  280. X
  281. X                };
  282. X        } /* end of option processing */
  283. X
  284. X    if (argc<1) faterror("no input file%s", cmdusage);
  285. X
  286. X        /* now handle all filenames */
  287. X        do {
  288. X        finptr=frewrite=NULL;
  289. X    fcopy = foutptr = NULL;
  290. X    ffree();
  291. X
  292. X    if (!pairfilenames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, tostdout))
  293. X        continue;
  294. X
  295. X        /* now RCSfilename contains the name of the RCS file, and finptr
  296. X         * the file descriptor. If tostdout is false, workfilename contains
  297. X         * the name of the working file, otherwise undefined (not nil!).
  298. X     * Also, RCSstat has been set.
  299. X         */
  300. X    diagnose("%s  -->  %s\n", RCSfilename,tostdout?"stdout":workfilename);
  301. X
  302. X    if (!tostdout) {
  303. X        if (!getworkstat()) continue; /* give up */
  304. X        if (!initeditfiles(workfilename)) {
  305. X            if (errno == EACCES)
  306. X                error("%s: parent directory isn't writable",
  307. X                    workfilename
  308. X                );
  309. X            else
  310. X                eerror(resultfile);
  311. X            continue;
  312. X        }
  313. X    }
  314. X    if (0 <= expmode)
  315. X        Expand = expmode;
  316. X    if (0 < lockflag  &&  Expand == VAL_EXPAND) {
  317. X        error("cannot combine -kv and -l");
  318. X        continue;
  319. X    }
  320. X
  321. X        gettree();  /* reads in the delta tree */
  322. X
  323. X        if (Head==nil) {
  324. X                /* no revisions; create empty file */
  325. X        diagnose("no revisions present; generating empty revision 0.0\n");
  326. X                if (!tostdout)
  327. X                        if (!creatempty()) continue;
  328. X                /* Can't reserve a delta, so don't call addlock */
  329. X        } else {
  330. X                if (rev!=nil) {
  331. X                        /* expand symbolic revision number */
  332. X            if (!expandsym(rev, &numericrev))
  333. X                                continue;
  334. X        } else
  335. X            switch (lockflag<0 ? findlock(false,&targetdelta) : 0) {
  336. X                default:
  337. X                continue;
  338. X                case 0:
  339. X                bufscpy(&numericrev, Dbranch?Dbranch:"");
  340. X                break;
  341. X                case 1:
  342. X                bufscpy(&numericrev, targetdelta->num);
  343. X                break;
  344. X            }
  345. X                /* get numbers of deltas to be generated */
  346. X        if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas)))
  347. X                        continue;
  348. X                /* check reservations */
  349. X        changelock = 0;
  350. X        if (lockflag) {
  351. X            changelock =
  352. X               lockflag<0 ? rmlock(targetdelta) : addlock(targetdelta);
  353. X            if (changelock) {
  354. X            if (changelock<0 || !checkaccesslist())
  355. X                continue;
  356. X            } else {
  357. X            ffclose(frewrite);  frewrite=NULL;
  358. X            seteid();
  359. X            ignoreints();
  360. X            r = unlink(newRCSfilename);
  361. X            keepdirtemp(newRCSfilename);
  362. X            restoreints();
  363. X            setrid();
  364. X            if (r != 0) {
  365. X                eerror(RCSfilename);
  366. X                continue;
  367. X            }
  368. X            }
  369. X        }
  370. X
  371. X                if (join && !preparejoin()) continue;
  372. X
  373. X        diagnose("revision %s%s\n",targetdelta->num,
  374. X             0<lockflag ? " (locked)" :
  375. X             lockflag<0 ? " (unlocked)" : "");
  376. X
  377. X                /* remove old working file if necessary */
  378. X                if (!tostdout)
  379. X                        if (!rmworkfile()) continue;
  380. X
  381. X                /* prepare for rewriting the RCS file */
  382. X        if (changelock) {
  383. X                        putadmin(frewrite);
  384. X                        puttree(Head,frewrite);
  385. X            aprintf(frewrite, "\n\n%s%c",Kdesc,nextc);
  386. X            foutptr = frewrite;
  387. X        }
  388. X
  389. X                /* skip description */
  390. X                getdesc(false); /* don't echo*/
  391. X
  392. X        locker_expansion = 0 < lockflag;
  393. X                if (!(neworkfilename=buildrevision(gendeltas,targetdelta,
  394. X                           tostdout,Expand!=OLD_EXPAND)))
  395. X                                continue;
  396. X
  397. X        if (changelock && !nerror) {
  398. X                        /* rewrite the rest of the RCSfile */
  399. X                        fastcopy(finptr,frewrite);
  400. X            ffclose(finptr); finptr=NULL; /*Help the file system.*/
  401. X                        ffclose(frewrite); frewrite=NULL;
  402. X            seteid();
  403. X            if ((r = chmod(newRCSfilename, RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH))) == 0) {
  404. X                ignoreints();
  405. X                r = re_name(newRCSfilename,RCSfilename);
  406. X                keepdirtemp(newRCSfilename);
  407. X                restoreints();
  408. X            }
  409. X            setrid();
  410. X            if (r != 0) {
  411. X                eerror(RCSfilename);
  412. X                error("saved in %s", newRCSfilename);
  413. X                dirtempunlink();
  414. X                                break;
  415. X                        }
  416. X                }
  417. X
  418. X                if (join) {
  419. X            if (!buildjoin(neworkfilename)) continue;
  420. X                }
  421. X                if (!tostdout) {
  422. X            if (!fixworkmode(neworkfilename))
  423. X                continue;
  424. X            ignoreints();
  425. X            r = re_name(neworkfilename,workfilename);
  426. X            keepdirtemp(neworkfilename);
  427. X            restoreints();
  428. X            if (r != 0) {
  429. X                eerror(workfilename);
  430. X                error("see %s", neworkfilename);
  431. X                                continue;
  432. X                        }
  433. X        }
  434. X        }
  435. X    if (!tostdout) diagnose("done\n");
  436. X        } while (cleanup(),
  437. X                 ++argv, --argc >=1);
  438. X
  439. X    tempunlink();
  440. X    exitmain(exitstatus);
  441. X
  442. X}       /* end of main (co) */
  443. X
  444. X    static void
  445. Xcleanup()
  446. X{
  447. X    if (nerror) exitstatus = EXIT_FAILURE;
  448. X    if (finptr) ffclose(finptr);
  449. X    if (frewrite) ffclose(frewrite);
  450. X    dirtempunlink();
  451. X}
  452. X
  453. X#if lint
  454. X#    define exiterr coExit
  455. X#endif
  456. X    exiting void
  457. Xexiterr()
  458. X{
  459. X    dirtempunlink();
  460. X    tempunlink();
  461. X    _exit(EXIT_FAILURE);
  462. X}
  463. X
  464. X
  465. X/*****************************************************************
  466. X * The following routines are auxiliary routines
  467. X *****************************************************************/
  468. X
  469. X    static int
  470. Xrmworkfile()
  471. X/* Function: prepares to remove workfilename, if it exists, and if
  472. X * it is read-only.
  473. X * Otherwise (file writable):
  474. X *   if !quietmode asks the user whether to really delete it (default: fail);
  475. X *   otherwise failure.
  476. X * Returns 0 on failure to get permission, -1 if there's nothing to remove,
  477. X * 1 if there is a file to remove.
  478. X */
  479. X{
  480. X    if (haveworkstat)      /* File doesn't exist; set by pairfilenames*/
  481. X        return -1;
  482. X
  483. X    if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) {
  484. X        /* File is writable */
  485. X        if (!yesorno(false, "writable %s exists; remove it? [ny](n): ",
  486. X            workfilename
  487. X        )) {
  488. X        error(!quietflag && ttystdin()
  489. X            ? "checkout aborted"
  490. X            : "writable %s exists; checkout aborted", workfilename);
  491. X        return 0;
  492. X            }
  493. X        }
  494. X    /* Actual unlink is done later by caller. */
  495. X    return 1;
  496. X}
  497. X
  498. X    static int
  499. Xfixworkmode(f)
  500. X    const char *f;
  501. X{
  502. X    if (
  503. X        chmod(f, WORKMODE(RCSstat.st_mode,
  504. X            !(Expand==VAL_EXPAND  ||  lockflag<=0 && StrictLocks)
  505. X        )) < 0
  506. X    ) {
  507. X        eerror(workfilename);
  508. X        return false;
  509. X    }
  510. X    return true;
  511. X}
  512. X
  513. X
  514. X    static int
  515. Xcreatempty()
  516. X/* Function: creates an empty working file.
  517. X * First, removes an existing working file with rmworkfile().
  518. X */
  519. X{
  520. X        int  fdesc;              /* file descriptor */
  521. X    int s;
  522. X
  523. X    if (!(s = rmworkfile()))
  524. X        return false;
  525. X    if (0 < s  &&  unlink(workfilename) != 0) {
  526. X        eerror(workfilename);
  527. X        return false;
  528. X    }
  529. X        fdesc=creat(workfilename,0);
  530. X    if (fdesc < 0)
  531. X        efaterror(workfilename);
  532. X    VOID close(fdesc); /* empty file */
  533. X    return fixworkmode(workfilename);
  534. X}
  535. X
  536. X
  537. X    static int
  538. Xrmlock(delta)
  539. X    const struct hshentry *delta;
  540. X/* Function: removes the lock held by caller on delta.
  541. X * Returns -1 if someone else holds the lock,
  542. X * 0 if there is no lock on delta,
  543. X * and 1 if a lock was found and removed.
  544. X */
  545. X{       register struct lock * next, * trail;
  546. X    const char *num;
  547. X        struct lock dummy;
  548. X        int whomatch, nummatch;
  549. X
  550. X        num=delta->num;
  551. X        dummy.nextlock=next=Locks;
  552. X        trail = &dummy;
  553. X        while (next!=nil) {
  554. X        whomatch = strcmp(getcaller(), next->login);
  555. X                nummatch=strcmp(num,next->delta->num);
  556. X                if ((whomatch==0) && (nummatch==0)) break;
  557. X            /*found a lock on delta by caller*/
  558. X                if ((whomatch!=0)&&(nummatch==0)) {
  559. X                    error("revision %s locked by %s; use co -r or rcs -u",num,next->login);
  560. X                    return -1;
  561. X                }
  562. X                trail=next;
  563. X                next=next->nextlock;
  564. X        }
  565. X        if (next!=nil) {
  566. X                /*found one; delete it */
  567. X                trail->nextlock=next->nextlock;
  568. X                Locks=dummy.nextlock;
  569. X                next->delta->lockedby=nil; /* reset locked-by */
  570. X                return 1; /*success*/
  571. X        } else  return 0; /*no lock on delta*/
  572. X}
  573. X
  574. X
  575. X
  576. X
  577. X/*****************************************************************
  578. X * The rest of the routines are for handling joins
  579. X *****************************************************************/
  580. X
  581. X
  582. X    static const char *
  583. Xaddjoin(joinrev)
  584. X    char *joinrev;
  585. X/* Add joinrev's number to joinlist, yielding address of char past joinrev,
  586. X * or nil if no such revision exists.
  587. X */
  588. X{
  589. X    register char *j;
  590. X    register const struct hshentry *d;
  591. X    char terminator;
  592. X    struct buf numrev;
  593. X    struct hshentries *joindeltas;
  594. X
  595. X    j = joinrev;
  596. X    for (;;) {
  597. X        switch (*j++) {
  598. X        default:
  599. X            continue;
  600. X        case 0:
  601. X        case ' ': case '\t': case '\n':
  602. X        case ':': case ',': case ';':
  603. X            break;
  604. X        }
  605. X        break;
  606. X    }
  607. X    terminator = *--j;
  608. X    *j = 0;
  609. X    bufautobegin(&numrev);
  610. X    d = 0;
  611. X    if (expandsym(joinrev, &numrev))
  612. X        d = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&joindeltas);
  613. X    bufautoend(&numrev);
  614. X    *j = terminator;
  615. X    if (d) {
  616. X        joinlist[++lastjoin] = d->num;
  617. X        return j;
  618. X    }
  619. X    return nil;
  620. X}
  621. X
  622. X    static int
  623. Xpreparejoin()
  624. X/* Function: Parses a join list pointed to by join and places pointers to the
  625. X * revision numbers into joinlist.
  626. X */
  627. X{
  628. X    register const char *j;
  629. X
  630. X        j=join;
  631. X        lastjoin= -1;
  632. X        for (;;) {
  633. X                while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
  634. X                if (*j=='\0') break;
  635. X                if (lastjoin>=joinlength-2) {
  636. X                        error("too many joins");
  637. X                        return(false);
  638. X                }
  639. X        if (!(j = addjoin(j))) return false;
  640. X                while ((*j==' ') || (*j=='\t')) j++;
  641. X                if (*j == ':') {
  642. X                        j++;
  643. X                        while((*j==' ') || (*j=='\t')) j++;
  644. X                        if (*j!='\0') {
  645. X                if (!(j = addjoin(j))) return false;
  646. X                        } else {
  647. X                                error("join pair incomplete");
  648. X                                return false;
  649. X                        }
  650. X                } else {
  651. X                        if (lastjoin==0) { /* first pair */
  652. X                                /* common ancestor missing */
  653. X                                joinlist[1]=joinlist[0];
  654. X                                lastjoin=1;
  655. X                                /*derive common ancestor*/
  656. X                if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1])))
  657. X                                       return false;
  658. X                        } else {
  659. X                                error("join pair incomplete");
  660. X                                return false;
  661. X                        }
  662. X                }
  663. X        }
  664. X        if (lastjoin<1) {
  665. X                error("empty join");
  666. X                return false;
  667. X        } else  return true;
  668. X}
  669. X
  670. X
  671. X
  672. X    static const char *
  673. Xgetancestor(r1, r2)
  674. X    const char *r1, *r2;
  675. X/* Yield the common ancestor of r1 and r2 if successful, nil otherwise.
  676. X * Work reliably only if r1 and r2 are not branch numbers.
  677. X */
  678. X{
  679. X    static struct buf t1, t2;
  680. X
  681. X    unsigned l1, l2, l3;
  682. X    const char *r;
  683. X
  684. X    l1 = countnumflds(r1);
  685. X    l2 = countnumflds(r2);
  686. X    if ((2<l1 || 2<l2)  &&  cmpnum(r1,r2)!=0) {
  687. X        /* not on main trunk or identical */
  688. X        l3 = 0;
  689. X        while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0)
  690. X        l3 += 2;
  691. X        /* This will terminate since r1 and r2 are not the same; see above. */
  692. X        if (l3==0) {
  693. X        /* no common prefix; common ancestor on main trunk */
  694. X        VOID partialno(&t1, r1, l1>2 ? (unsigned)2 : l1);
  695. X        VOID partialno(&t2, r2, l2>2 ? (unsigned)2 : l2);
  696. X        r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string;
  697. X        if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0)
  698. X            return r;
  699. X        } else if (cmpnumfld(r1, r2, l3+1)!=0)
  700. X            return partialno(&t1,r1,l3);
  701. X    }
  702. X    error("common ancestor of %s and %s undefined", r1, r2);
  703. X    return nil;
  704. X}
  705. X
  706. X
  707. X
  708. X    static int
  709. Xbuildjoin(initialfile)
  710. X    const char *initialfile;
  711. X/* Function: merge pairs of elements in joinlist into initialfile
  712. X * If tostdout is set, copy result to stdout.
  713. X * All unlinking of initialfile, rev2, and rev3 should be done by *tempunlink().
  714. X */
  715. X{
  716. X    struct buf commarg;
  717. X    struct buf subs;
  718. X    const char *rev2, *rev3;
  719. X        int i;
  720. X    int status;
  721. X    const char *cov[8], *mergev[12];
  722. X    const char **p;
  723. X
  724. X    bufautobegin(&commarg);
  725. X    bufautobegin(&subs);
  726. X    rev2 = maketemp(0);
  727. X    rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */
  728. X
  729. X    cov[0] = nil;
  730. X    /* cov[1] setup below */
  731. X    cov[2] = CO;
  732. X    /* cov[3] setup below */
  733. X    p = &cov[4];
  734. X    if (versionarg) *p++ = versionarg;
  735. X    *p++ = quietarg;
  736. X    *p++ = RCSfilename;
  737. X    *p = nil;
  738. X
  739. X    mergev[0] = nil;
  740. X    mergev[1] = nil;
  741. X    mergev[2] = MERGE;
  742. X    mergev[3] = mergev[5] = "-L";
  743. X    /* rest of mergev setup below */
  744. X
  745. X        i=0;
  746. X        while (i<lastjoin) {
  747. X                /*prepare marker for merge*/
  748. X                if (i==0)
  749. X            bufscpy(&subs, targetdelta->num);
  750. X        else {
  751. X            bufscat(&subs, ",");
  752. X            bufscat(&subs, joinlist[i-2]);
  753. X            bufscat(&subs, ":");
  754. X            bufscat(&subs, joinlist[i-1]);
  755. X        }
  756. X        diagnose("revision %s\n",joinlist[i]);
  757. X        bufscpy(&commarg, "-p");
  758. X        bufscat(&commarg, joinlist[i]);
  759. X        cov[1] = rev2;
  760. X        cov[3] = commarg.string;
  761. X        if (runv(cov))
  762. X            goto badmerge;
  763. X        diagnose("revision %s\n",joinlist[i+1]);
  764. X        bufscpy(&commarg, "-p");
  765. X        bufscat(&commarg, joinlist[i+1]);
  766. X        cov[1] = rev3;
  767. X        cov[3] = commarg.string;
  768. X        if (runv(cov))
  769. X            goto badmerge;
  770. X        diagnose("merging...\n");
  771. X        mergev[4] = subs.string;
  772. X        mergev[6] = joinlist[i+1];
  773. X        p = &mergev[7];
  774. X        if (quietflag) *p++ = quietarg;
  775. X        if (lastjoin<=i+2 && tostdout) *p++ = "-p";
  776. X        *p++ = initialfile;
  777. X        *p++ = rev2;
  778. X        *p++ = rev3;
  779. X        *p = nil;
  780. X        status = runv(mergev);
  781. X        if (!WIFEXITED(status) || 1<WEXITSTATUS(status))
  782. X            goto badmerge;
  783. X                i=i+2;
  784. X        }
  785. X    bufautoend(&commarg);
  786. X    bufautoend(&subs);
  787. X        return true;
  788. X
  789. X    badmerge:
  790. X    nerror++;
  791. X    bufautoend(&commarg);
  792. X    bufautoend(&subs);
  793. X    return false;
  794. X}
  795. END_OF_FILE
  796.   if test 21836 -ne `wc -c <'src/co.c'`; then
  797.     echo shar: \"'src/co.c'\" unpacked with wrong size!
  798.   fi
  799.   # end of 'src/co.c'
  800. fi
  801. if test -f 'src/rcsfcmp.c' -a "${1}" != "-c" ; then 
  802.   echo shar: Will not clobber existing file \"'src/rcsfcmp.c'\"
  803. else
  804.   echo shar: Extracting \"'src/rcsfcmp.c'\" \(7674 characters\)
  805.   sed "s/^X//" >'src/rcsfcmp.c' <<'END_OF_FILE'
  806. X/*
  807. X *                     RCS file comparison
  808. X */
  809. X/*****************************************************************************
  810. X *                       rcsfcmp()
  811. X *                       Testprogram: define FCMPTEST
  812. X *****************************************************************************
  813. X */
  814. X
  815. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  816. X   Copyright 1990 by Paul Eggert
  817. X   Distributed under license by the Free Software Foundation, Inc.
  818. X
  819. XThis file is part of RCS.
  820. X
  821. XRCS is free software; you can redistribute it and/or modify
  822. Xit under the terms of the GNU General Public License as published by
  823. Xthe Free Software Foundation; either version 1, or (at your option)
  824. Xany later version.
  825. X
  826. XRCS is distributed in the hope that it will be useful,
  827. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  828. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  829. XGNU General Public License for more details.
  830. X
  831. XYou should have received a copy of the GNU General Public License
  832. Xalong with RCS; see the file COPYING.  If not, write to
  833. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  834. X
  835. XReport problems and direct all questions to:
  836. X
  837. X    rcs-bugs@cs.purdue.edu
  838. X
  839. X*/
  840. X
  841. X
  842. X
  843. X
  844. X
  845. X/* $Log: rcsfcmp.c,v $
  846. X * Revision 5.5  1990/11/27  09:26:05  eggert
  847. X * Fix comment leader bug.
  848. X *
  849. X * Revision 5.4  1990/11/01  05:03:42  eggert
  850. X * Permit arbitrary data in logs and comment leaders.
  851. X *
  852. X * Revision 5.3  1990/09/11  02:41:15  eggert
  853. X * Don't ignore differences inside keyword strings if -ko is set.
  854. X *
  855. X * Revision 5.1  1990/08/29  07:13:58  eggert
  856. X * Clean old log messages too.
  857. X *
  858. X * Revision 5.0  1990/08/22  08:12:49  eggert
  859. X * Don't append "checked in with -k by " log to logs,
  860. X * so that checking in a program with -k doesn't change it.
  861. X * Ansify and Posixate.  Remove lint.
  862. X *
  863. X * Revision 4.5  89/05/01  15:12:42  narten
  864. X * changed copyright header to reflect current distribution rules
  865. X * 
  866. X * Revision 4.4  88/08/09  19:12:50  eggert
  867. X * Shrink stdio code size.
  868. X * 
  869. X * Revision 4.3  87/12/18  11:40:02  narten
  870. X * lint cleanups (Guy Harris)
  871. X * 
  872. X * Revision 4.2  87/10/18  10:33:06  narten
  873. X * updting version number. Changes relative to 1.1 actually relative to 
  874. X * 4.1
  875. X * 
  876. X * Revision 1.2  87/03/27  14:22:19  jenkins
  877. X * Port to suns
  878. X * 
  879. X * Revision 4.1  83/05/10  16:24:04  wft
  880. X * Marker matching now uses trymatch(). Marker pattern is now
  881. X * checked precisely.
  882. X * 
  883. X * Revision 3.1  82/12/04  13:21:40  wft
  884. X * Initial revision.
  885. X *
  886. X */
  887. X
  888. X/*
  889. X#define FCMPTEST
  890. X*/
  891. X/* Testprogram; prints out whether two files are identical,
  892. X * except for keywords
  893. X */
  894. X
  895. X#include  "rcsbase.h"
  896. X
  897. XlibId(fcmpId, "$Id: rcsfcmp.c,v 5.5 1990/11/27 09:26:05 eggert Exp $")
  898. X
  899. X
  900. X    int
  901. Xrcsfcmp(xfname,uxfname,delta)
  902. X    const char *xfname, *uxfname;
  903. X    const struct hshentry *delta;
  904. X/* Function: compares the files xfname and uxfname. Returns true
  905. X * if xfname has the same contents as uxfname, while disregarding
  906. X * keyword values. For the LOG-keyword, rcsfcmp skips the log message
  907. X * given by the parameter delta in xfname. Thus, rcsfcmp returns true
  908. X * if xfname contains the same as uxfname, with the keywords expanded.
  909. X * Implementation: character-by-character comparison until $ is found.
  910. X * If a $ is found, read in the marker keywords; if they are real keywords
  911. X * and identical, read in keyword value. If value is terminated properly,
  912. X * disregard it and optionally skip log message; otherwise, compare value.
  913. X */
  914. X{
  915. X    register int xc,uxc;
  916. X    char xkeyword[keylength+2],   uxkeyword[keylength+2];
  917. X    int eqkeyvals;
  918. X    register FILE * xfp, * uxfp;
  919. X    register int delimiter;
  920. X    register char * tp;
  921. X    register const char *sp;
  922. X    int result;
  923. X    enum markers match1,match2;
  924. X
  925. X    errno = 0;
  926. X    if (!(xfp=fopen(sp=xfname,"r")) || !(errno=0, uxfp=fopen(sp=uxfname,"r"))) {
  927. X       efaterror(sp);
  928. X    }
  929. X    result=false;
  930. X    delimiter = Expand==OLD_EXPAND ? EOF : KDELIM;
  931. X    xc=getc(xfp); uxc=getc(uxfp);
  932. X    while( xc == uxc) { /* comparison loop */
  933. X        if (xc==EOF) { /* finished; everything is the same*/
  934. X            result=true;
  935. X            break;
  936. X        }
  937. X    if (xc != delimiter) {
  938. X            /* get the next characters */
  939. X            xc=getc(xfp); uxc=getc(uxfp);
  940. X        } else {
  941. X            /* try to get both keywords */
  942. X            tp = xkeyword;
  943. X            while( (xc=getc(xfp))!=EOF && (tp< xkeyword+keylength) && (xc!='\n')
  944. X                   && (xc!=KDELIM) && (xc!=VDELIM))
  945. X                *tp++ = xc;
  946. X        *tp++ = xc;  /* add closing K/VDELIM */
  947. X            *tp='\0';
  948. X            tp = uxkeyword;
  949. X            while( (uxc=getc(uxfp))!=EOF && (tp< uxkeyword+keylength) && (uxc!='\n')
  950. X                   && (uxc!=KDELIM) && (uxc!=VDELIM))
  951. X                *tp++ = uxc;
  952. X        *tp++ = xc;  /* add closing K/VDELIM */
  953. X            *tp='\0';
  954. X        /* Now we have 2 keywords, or something that looks like it. */
  955. X        match1 = trymatch(xkeyword);
  956. X        match2 = trymatch(uxkeyword);
  957. X        if (match1 != match2) break; /* not identical */
  958. X#ifdef FCMPTEST
  959. X        VOID printf("found potential keywords %s and %s\n",xkeyword,uxkeyword);
  960. X#endif
  961. X
  962. X        if (match1 == Nomatch) {
  963. X        /* not a keyword pattern, but could still be identical */
  964. X        if (strcmp(xkeyword,uxkeyword)==0)
  965. X             continue;
  966. X        else break;
  967. X        }
  968. X#ifdef FCMPTEST
  969. X        VOID printf("found common keyword %s\n",xkeyword);
  970. X#endif
  971. X        eqkeyvals = 1;
  972. X        for (;;) {
  973. X        if (xc==uxc) {
  974. X            if (xc==KDELIM)
  975. X            break;
  976. X        } else {
  977. X            eqkeyvals = 0;
  978. X            if (xc==KDELIM) {
  979. X            while (uxc!=KDELIM && uxc!='\n' && uxc!=EOF)
  980. X                uxc = getc(uxfp);
  981. X            break;
  982. X            }
  983. X            if (uxc==KDELIM) {
  984. X            while (xc!=KDELIM && xc!='\n' && xc!=EOF)
  985. X                xc = getc(xfp);
  986. X            break;
  987. X            }
  988. X        }
  989. X        if (xc=='\n' || uxc=='\n' || xc==EOF || uxc==EOF)
  990. X            break;
  991. X        xc = getc(xfp);
  992. X        uxc = getc(uxfp);
  993. X        }
  994. X        if (xc!=uxc) break; /* not the same */
  995. X        if (xc==KDELIM) {
  996. X        xc=getc(xfp); uxc=getc(uxfp); /* skip closing KDELIM */
  997. X        /* if the keyword is LOG, also skip the log message in xfp*/
  998. X        if (match1==Log) {
  999. X            /* first, compute the number of line feeds in log msg */
  1000. X            unsigned lncnt;
  1001. X            size_t ls, ccnt;
  1002. X            lncnt = 3;
  1003. X            sp = delta->log.string;
  1004. X            ls = delta->log.size;
  1005. X            if (sizeof(ciklog)-1<=ls && !strncmp(sp,ciklog,sizeof(ciklog)-1))
  1006. X            continue; /* this log message wasn't inserted */
  1007. X            while (ls--) if (*sp++=='\n') lncnt++;
  1008. X            while(xc!=EOF) {
  1009. X            if (xc=='\n')
  1010. X                if(--lncnt==0) break;
  1011. X            xc=getc(xfp);
  1012. X            }
  1013. X            /* skip last comment leader */
  1014. X            /* Can't just skip another line here, because there may be */
  1015. X            /* additional characters on the line (after the Log....$)  */
  1016. X            for (ccnt=Comment.size; ccnt--; ) {
  1017. X            xc=getc(xfp);
  1018. X            if(xc=='\n') break;
  1019. X            /* reads to the end of the comment leader or '\n',     */
  1020. X            /* whatever comes first. This is because some editors  */
  1021. X            /* strip off trailing blanks from a leader like " * ". */
  1022. X            }
  1023. X        }
  1024. X        } else {
  1025. X        /* both end in the same character, but not a KDELIM */
  1026. X        /* must compare string values.*/
  1027. X#ifdef FCMPTEST
  1028. X        VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
  1029. X#endif
  1030. X        if (!eqkeyvals) break;
  1031. X            }
  1032. X        }
  1033. X    }
  1034. X    ffclose(xfp); ffclose(uxfp);
  1035. X    return result;
  1036. X}
  1037. X
  1038. X
  1039. X
  1040. X#ifdef FCMPTEST
  1041. X
  1042. Xconst char cmdid[] = "rcsfcmp";
  1043. X
  1044. Xmain(argc, argv)
  1045. Xint  argc; char  *argv[];
  1046. X/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
  1047. X * 4th: unexpanded file
  1048. X */
  1049. X{       struct hshentry delta;
  1050. X
  1051. X    Comment.string = argv[1];
  1052. X    Comment.size = strlen(argv[1]);
  1053. X    delta.log.string = argv[2];
  1054. X    delta.log.size = strlen(argv[2]);
  1055. X        if (rcsfcmp(argv[3],argv[4],&delta))
  1056. X                VOID printf("files are the same\n");
  1057. X        else    VOID printf("files are different\n");
  1058. X}
  1059. X#endif
  1060. END_OF_FILE
  1061.   if test 7674 -ne `wc -c <'src/rcsfcmp.c'`; then
  1062.     echo shar: \"'src/rcsfcmp.c'\" unpacked with wrong size!
  1063.   fi
  1064.   # end of 'src/rcsfcmp.c'
  1065. fi
  1066. if test -f 'src/rcsrev.c' -a "${1}" != "-c" ; then 
  1067.   echo shar: Will not clobber existing file \"'src/rcsrev.c'\"
  1068. else
  1069.   echo shar: Extracting \"'src/rcsrev.c'\" \(20500 characters\)
  1070.   sed "s/^X//" >'src/rcsrev.c' <<'END_OF_FILE'
  1071. X/*
  1072. X *                     RCS revision number handling
  1073. X */
  1074. X
  1075. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  1076. X   Copyright 1990 by Paul Eggert
  1077. X   Distributed under license by the Free Software Foundation, Inc.
  1078. X
  1079. XThis file is part of RCS.
  1080. X
  1081. XRCS is free software; you can redistribute it and/or modify
  1082. Xit under the terms of the GNU General Public License as published by
  1083. Xthe Free Software Foundation; either version 1, or (at your option)
  1084. Xany later version.
  1085. X
  1086. XRCS is distributed in the hope that it will be useful,
  1087. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  1088. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  1089. XGNU General Public License for more details.
  1090. X
  1091. XYou should have received a copy of the GNU General Public License
  1092. Xalong with RCS; see the file COPYING.  If not, write to
  1093. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  1094. X
  1095. XReport problems and direct all questions to:
  1096. X
  1097. X    rcs-bugs@cs.purdue.edu
  1098. X
  1099. X*/
  1100. X
  1101. X
  1102. X
  1103. X
  1104. X/* $Log: rcsrev.c,v $
  1105. X * Revision 5.0  1990/08/22  08:13:43  eggert
  1106. X * Remove compile-time limits; use malloc instead.
  1107. X * Ansify and Posixate.  Tune.
  1108. X * Remove possibility of an internal error.  Remove lint.
  1109. X *
  1110. X * Revision 4.5  89/05/01  15:13:22  narten
  1111. X * changed copyright header to reflect current distribution rules
  1112. X * 
  1113. X * Revision 4.4  87/12/18  11:45:22  narten
  1114. X * more lint cleanups. Also, the NOTREACHED comment is no longer necessary, 
  1115. X * since there's now a return value there with a value. (Guy Harris)
  1116. X * 
  1117. X * Revision 4.3  87/10/18  10:38:42  narten
  1118. X * Updating version numbers. Changes relative to version 1.1 actually 
  1119. X * relative to 4.1
  1120. X * 
  1121. X * Revision 1.3  87/09/24  14:00:37  narten
  1122. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  1123. X * warnings)
  1124. X * 
  1125. X * Revision 1.2  87/03/27  14:22:37  jenkins
  1126. X * Port to suns
  1127. X * 
  1128. X * Revision 4.1  83/03/25  21:10:45  wft
  1129. X * Only changed $Header to $Id.
  1130. X * 
  1131. X * Revision 3.4  82/12/04  13:24:08  wft
  1132. X * Replaced getdelta() with gettree().
  1133. X *
  1134. X * Revision 3.3  82/11/28  21:33:15  wft
  1135. X * fixed compartial() and compnum() for nil-parameters; fixed nils
  1136. X * in error messages. Testprogram output shortenend.
  1137. X *
  1138. X * Revision 3.2  82/10/18  21:19:47  wft
  1139. X * renamed compnum->cmpnum, compnumfld->cmpnumfld,
  1140. X * numericrevno->numricrevno.
  1141. X *
  1142. X * Revision 3.1  82/10/11  19:46:09  wft
  1143. X * changed expandsym() to check for source==nil; returns zero length string
  1144. X * in that case.
  1145. X */
  1146. X
  1147. X
  1148. X
  1149. X/*
  1150. X#define REVTEST
  1151. X*/
  1152. X/* version REVTEST is for testing the routines that generate a sequence
  1153. X * of delta numbers needed to regenerate a given delta.
  1154. X */
  1155. X
  1156. X#include "rcsbase.h"
  1157. X
  1158. XlibId(revId, "$Id: rcsrev.c,v 5.0 1990/08/22 08:13:43 eggert Exp $")
  1159. X
  1160. Xstatic struct hshentry *genbranch P((const struct hshentry*,const char*,unsigned,const char*,const char*,const char*,struct hshentries**));
  1161. X
  1162. X
  1163. X
  1164. X    unsigned
  1165. Xcountnumflds(s)
  1166. X    const char *s;
  1167. X/* Given a pointer s to a dotted number (date or revision number),
  1168. X * countnumflds returns the number of digitfields in s.
  1169. X */
  1170. X{
  1171. X    register const char *sp;
  1172. X    register unsigned count;
  1173. X        if ((sp=s)==nil) return(0);
  1174. X        if (*sp == '\0') return(0);
  1175. X        count = 1;
  1176. X    do {
  1177. X                if (*sp++ == '.') count++;
  1178. X    } while (*sp);
  1179. X        if (*(--sp) == '.') count--; /*trailing periods don't count*/
  1180. X        return(count);
  1181. X}
  1182. X
  1183. X    void
  1184. Xgetbranchno(revno,branchno)
  1185. X    const char *revno;
  1186. X    struct buf *branchno;
  1187. X/* Given a non-nil revision number revno, getbranchno copies the number of the branch
  1188. X * on which revno is into branchno. If revno itself is a branch number,
  1189. X * it is copied unchanged.
  1190. X */
  1191. X{
  1192. X    register unsigned numflds;
  1193. X    register char *tp;
  1194. X
  1195. X    bufscpy(branchno, revno);
  1196. X        numflds=countnumflds(revno);
  1197. X    if (!(numflds & 1)) {
  1198. X        tp = branchno->string;
  1199. X        while (--numflds)
  1200. X            while (*tp++ != '.')
  1201. X                ;
  1202. X                *(tp-1)='\0';
  1203. X        }
  1204. X}
  1205. X
  1206. X
  1207. X
  1208. Xint cmpnum(num1, num2)
  1209. X    const char *num1, *num2;
  1210. X/* compares the two dotted numbers num1 and num2 lexicographically
  1211. X * by field. Individual fields are compared numerically.
  1212. X * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp.
  1213. X * omitted fields are assumed to be higher than the existing ones.
  1214. X*/
  1215. X{
  1216. X    register const char *s1, *s2;
  1217. X        register int n1, n2;
  1218. X
  1219. X        s1=num1==nil?"":num1;
  1220. X        s2=num2==nil?"":num2;
  1221. X
  1222. X        do {
  1223. X                n1 = 0;
  1224. X        while (isdigit(*s1))
  1225. X            n1 = n1*10 + (*s1++ - '0');
  1226. X                /* skip '.' */
  1227. X                if (*s1=='.') s1++;
  1228. X
  1229. X                n2 = 0;
  1230. X        while (isdigit(*s2))
  1231. X            n2 = n2*10 + (*s2++ - '0');
  1232. X                /* skip '.' */
  1233. X                if (*s2=='.') s2++;
  1234. X
  1235. X        } while ((n1==n2) && (*s1!='\0') && (*s2!='\0'));
  1236. X
  1237. X        if (((*s1=='\0') && (*s2=='\0')) || (n1!=n2))
  1238. X                return (n1 - n2);
  1239. X        /*now n1==n2 and one of s1 or s2 is shorter*/
  1240. X        /*give precedence to shorter one*/
  1241. X        if (*s1=='\0') return 1;
  1242. X        else           return -1;
  1243. X
  1244. X}
  1245. X
  1246. X
  1247. X
  1248. Xint cmpnumfld(num1, num2, fld)
  1249. X    const char *num1, *num2;
  1250. X    unsigned fld;
  1251. X/* compares the two dotted numbers at field fld and returns
  1252. X * num1[fld]-num2[fld]. Assumes that num1 and num2 have at least fld fields.
  1253. X * fld must be positive.
  1254. X*/
  1255. X{
  1256. X    register const char *s1, *s2;
  1257. X    register unsigned n1, n2;
  1258. X
  1259. X    s1 = num1;
  1260. X    s2 = num2;
  1261. X        /* skip fld-1 fields */
  1262. X    for (n1 = fld;  (--n1);  ) {
  1263. X        while (*s1++ != '.')
  1264. X            ;
  1265. X        while (*s2++ != '.')
  1266. X            ;
  1267. X    }
  1268. X        /* Now s1 and s2 point to the beginning of the respective fields */
  1269. X        /* compute numerical value and compare */
  1270. X        n1 = 0;
  1271. X    while (isdigit(*s1))
  1272. X        n1 = n1*10 + (*s1++ - '0');
  1273. X        n2 = 0;
  1274. X    while (isdigit(*s2))
  1275. X        n2 = n2*10 + (*s2++ - '0');
  1276. X    return n1<n2 ? -1 : n1==n2 ? 0 : 1;
  1277. X}
  1278. X
  1279. X
  1280. X    int
  1281. Xcompartial(num1, num2, length)
  1282. X    const char *num1, *num2;
  1283. X    unsigned length;
  1284. X
  1285. X/*   compare the first "length" fields of two dot numbers;
  1286. X     the omitted field is considered to be larger than any number  */
  1287. X/*   restriction:  at least one number has length or more fields   */
  1288. X
  1289. X{
  1290. X    register const char *s1, *s2;
  1291. X        register        int     n1, n2;
  1292. X
  1293. X
  1294. X        s1 = num1;      s2 = num2;
  1295. X        if ( s1==nil || *s1 == '\0' ) return 1;
  1296. X        if ( s2==nil || *s2 == '\0' ) return -1;
  1297. X
  1298. X    for (;;) {
  1299. X            n1 = 0;
  1300. X        while (isdigit(*s1))
  1301. X        n1 = n1*10 + (*s1++ - '0');
  1302. X            if ( *s1 == '.' ) s1++;    /*  skip .   */
  1303. X
  1304. X            n2 = 0;
  1305. X        while (isdigit(*s2))
  1306. X        n2 = n2*10 + (*s2++ - '0');
  1307. X            if (*s2 == '.') s2++;
  1308. X
  1309. X        if ( n1 != n2 ) return n1-n2;
  1310. X        if ( --length == 0 ) return 0;
  1311. X        if ( *s1 == '\0' ) return 1;
  1312. X        if ( *s2 == '\0' ) return -1;
  1313. X    }
  1314. X}
  1315. X
  1316. X
  1317. Xchar * partialno(rev1,rev2,length)
  1318. X    struct buf *rev1;
  1319. X    const char *rev2;
  1320. X    register unsigned length;
  1321. X/* Function: Copies length fields of revision number rev2 into rev1.
  1322. X * Return rev1's string.
  1323. X */
  1324. X{
  1325. X    register char *r1;
  1326. X
  1327. X    bufscpy(rev1, rev2);
  1328. X    r1 = rev1->string;
  1329. X        while (length) {
  1330. X        while (*r1!='.' && *r1)
  1331. X            ++r1;
  1332. X        ++r1;
  1333. X                length--;
  1334. X        }
  1335. X        /* eliminate last '.'*/
  1336. X        *(r1-1)='\0';
  1337. X    return rev1->string;
  1338. X}
  1339. X
  1340. X
  1341. X
  1342. X
  1343. X    static void
  1344. Xstore1(store, next)
  1345. X    struct hshentries ***store;
  1346. X    struct hshentry *next;
  1347. X/*
  1348. X * Allocate a new list node that addresses NEXT.
  1349. X * Append it to the list that **STORE is the end pointer of.
  1350. X */
  1351. X{
  1352. X    register struct hshentries *p;
  1353. X
  1354. X    p = ftalloc(struct hshentries);
  1355. X    p->first = next;
  1356. X    **store = p;
  1357. X    *store = &p->rest;
  1358. X}
  1359. X
  1360. Xstruct hshentry * genrevs(revno,date,author,state,store)
  1361. X    const char *revno, *date, *author, *state;
  1362. X    struct hshentries **store;
  1363. X/* Function: finds the deltas needed for reconstructing the
  1364. X * revision given by revno, date, author, and state, and stores pointers
  1365. X * to these deltas into a list whose starting address is given by store.
  1366. X * The last delta (target delta) is returned.
  1367. X * If the proper delta could not be found, nil is returned.
  1368. X */
  1369. X{
  1370. X    unsigned length;
  1371. X        register struct hshentry * next;
  1372. X        int result;
  1373. X    const char *branchnum;
  1374. X    struct buf t;
  1375. X
  1376. X    bufautobegin(&t);
  1377. X
  1378. X    if (!(next = Head)) {
  1379. X        error("RCS file empty");
  1380. X        goto norev;
  1381. X        }
  1382. X
  1383. X        length = countnumflds(revno);
  1384. X
  1385. X        if (length >= 1) {
  1386. X                /* at least one field; find branch exactly */
  1387. X        while ((result=cmpnumfld(revno,next->num,1)) < 0) {
  1388. X            store1(&store, next);
  1389. X                        next = next->next;
  1390. X            if (!next) {
  1391. X                error("branch number %s too low", partialno(&t,revno,1));
  1392. X                goto norev;
  1393. X            }
  1394. X                }
  1395. X
  1396. X        if (result>0) {
  1397. X            error("branch number %s absent", partialno(&t,revno,1));
  1398. X            goto norev;
  1399. X        }
  1400. X        }
  1401. X        if (length<=1){
  1402. X                /* pick latest one on given branch */
  1403. X                branchnum = next->num; /* works even for empty revno*/
  1404. X                while ((next!=nil) &&
  1405. X                       (cmpnumfld(branchnum,next->num,1)==0) &&
  1406. X                       !(
  1407. X                        (date==nil?1:(cmpnum(date,next->date)>=0)) &&
  1408. X                        (author==nil?1:(strcmp(author,next->author)==0)) &&
  1409. X                        (state ==nil?1:(strcmp(state, next->state) ==0))
  1410. X                        )
  1411. X                       )
  1412. X        {
  1413. X            store1(&store, next);
  1414. X                        next=next->next;
  1415. X                }
  1416. X                if ((next==nil) ||
  1417. X                    (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ {
  1418. X            error("can't find revision on branch %s with a date before %s, author %s, and state %s",
  1419. X                length ? revno : partialno(&t,branchnum,1),
  1420. X                date ? date : "<now>",
  1421. X                                author==nil?"<any>":author, state==nil?"<any>":state);
  1422. X            goto norev;
  1423. X                } else {
  1424. X            store1(&store, next);
  1425. X                }
  1426. X                *store = nil;
  1427. X                return next;
  1428. X        }
  1429. X
  1430. X        /* length >=2 */
  1431. X        /* find revision; may go low if length==2*/
  1432. X    while ((result=cmpnumfld(revno,next->num,2)) < 0  &&
  1433. X               (cmpnumfld(revno,next->num,1)==0) ) {
  1434. X        store1(&store, next);
  1435. X                next = next->next;
  1436. X        if (!next)
  1437. X            break;
  1438. X        }
  1439. X
  1440. X        if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) {
  1441. X        error("revision number %s too low", partialno(&t,revno,2));
  1442. X        goto norev;
  1443. X        }
  1444. X        if ((length>2) && (result!=0)) {
  1445. X        error("revision %s absent", partialno(&t,revno,2));
  1446. X        goto norev;
  1447. X        }
  1448. X
  1449. X        /* print last one */
  1450. X    store1(&store, next);
  1451. X
  1452. X        if (length>2)
  1453. X                return genbranch(next,revno,length,date,author,state,store);
  1454. X        else { /* length == 2*/
  1455. X                if ((date!=nil) && (cmpnum(date,next->date)<0)){
  1456. X                        error("Revision %s has date %s.",next->num, next->date);
  1457. X                        return nil;
  1458. X                }
  1459. X                if ((author!=nil)&&(strcmp(author,next->author)!=0)) {
  1460. X                        error("Revision %s has author %s.",next->num,next->author);
  1461. X                        return nil;
  1462. X                }
  1463. X                if ((state!=nil)&&(strcmp(state,next->state)!=0)) {
  1464. X                        error("Revision %s has state %s.",next->num,
  1465. X                               next->state==nil?"<empty>":next->state);
  1466. X                        return nil;
  1467. X                }
  1468. X                *store=nil;
  1469. X                return next;
  1470. X        }
  1471. X    norev:
  1472. X    bufautoend(&t);
  1473. X    return nil;
  1474. X}
  1475. X
  1476. X
  1477. X
  1478. X
  1479. X    static struct hshentry *
  1480. Xgenbranch(bpoint, revno, length, date, author, state, store)
  1481. X    const struct hshentry *bpoint;
  1482. X    const char *revno;
  1483. X    unsigned length;
  1484. X    const char *date, *author, *state;
  1485. X    struct hshentries **store;
  1486. X/* Function: given a branchpoint, a revision number, date, author, and state,
  1487. X * genbranch finds the deltas necessary to reconstruct the given revision
  1488. X * from the branch point on.
  1489. X * Pointers to the found deltas are stored in a list beginning with store.
  1490. X * revno must be on a side branch.
  1491. X * return nil on error
  1492. X */
  1493. X{
  1494. X    unsigned field;
  1495. X        register struct hshentry * next, * trail;
  1496. X    register const struct branchhead *bhead;
  1497. X        int result;
  1498. X    struct buf t;
  1499. X
  1500. X    field = 3;
  1501. X        bhead = bpoint->branches;
  1502. X
  1503. X    do {
  1504. X        if (!bhead) {
  1505. X            bufautobegin(&t);
  1506. X            error("no side branches present for %s", partialno(&t,revno,field-1));
  1507. X            bufautoend(&t);
  1508. X            return nil;
  1509. X        }
  1510. X
  1511. X                /*find branch head*/
  1512. X                /*branches are arranged in increasing order*/
  1513. X        while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) {
  1514. X                        bhead = bhead->nextbranch;
  1515. X            if (!bhead) {
  1516. X                bufautobegin(&t);
  1517. X                error("branch number %s too high",partialno(&t,revno,field));
  1518. X                bufautoend(&t);
  1519. X                return nil;
  1520. X            }
  1521. X                }
  1522. X
  1523. X        if (result<0) {
  1524. X            bufautobegin(&t);
  1525. X            error("branch number %s absent", partialno(&t,revno,field));
  1526. X            bufautoend(&t);
  1527. X            return nil;
  1528. X        }
  1529. X
  1530. X                next = bhead->hsh;
  1531. X                if (length==field) {
  1532. X                        /* pick latest one on that branch */
  1533. X                        trail=nil;
  1534. X                        do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) &&
  1535. X                                 (author==nil?1:(strcmp(author,next->author)==0)) &&
  1536. X                                 (state ==nil?1:(strcmp(state, next->state) ==0))
  1537. X                             ) trail = next;
  1538. X                             next=next->next;
  1539. X                        } while (next!=nil);
  1540. X
  1541. X                        if (trail==nil) {
  1542. X                 error("can't find revision on branch %s with a date before %s, author %s, and state %s",
  1543. X                                        revno, date==nil?"<now>":date,
  1544. X                                        author==nil?"<any>":author, state==nil?"<any>":state);
  1545. X                             return nil;
  1546. X                        } else { /* print up to last one suitable */
  1547. X                             next = bhead->hsh;
  1548. X                             while (next!=trail) {
  1549. X                  store1(&store, next);
  1550. X                                  next=next->next;
  1551. X                             }
  1552. X                 store1(&store, next);
  1553. X                        }
  1554. X            *store = nil;
  1555. X                        return next;
  1556. X                }
  1557. X
  1558. X                /* length > field */
  1559. X                /* find revision */
  1560. X                /* check low */
  1561. X                if (cmpnumfld(revno,next->num,field+1)<0) {
  1562. X            bufautobegin(&t);
  1563. X            error("revision number %s too low", partialno(&t,revno,field+1));
  1564. X            bufautoend(&t);
  1565. X                        return(nil);
  1566. X                }
  1567. X        do {
  1568. X            store1(&store, next);
  1569. X                        trail = next;
  1570. X                        next = next->next;
  1571. X                } while ((next!=nil) &&
  1572. X                       (cmpnumfld(revno,next->num,field+1) >=0));
  1573. X
  1574. X                if ((length>field+1) &&  /*need exact hit */
  1575. X                    (cmpnumfld(revno,trail->num,field+1) !=0)){
  1576. X            bufautobegin(&t);
  1577. X            error("revision %s absent", partialno(&t,revno,field+1));
  1578. X            bufautoend(&t);
  1579. X                        return(nil);
  1580. X                }
  1581. X                if (length == field+1) {
  1582. X                        if ((date!=nil) && (cmpnum(date,trail->date)<0)){
  1583. X                                error("Revision %s has date %s.",trail->num, trail->date);
  1584. X                                return nil;
  1585. X                        }
  1586. X                        if ((author!=nil)&&(strcmp(author,trail->author)!=0)) {
  1587. X                                error("Revision %s has author %s.",trail->num,trail->author);
  1588. X                                return nil;
  1589. X                        }
  1590. X                        if ((state!=nil)&&(strcmp(state,trail->state)!=0)) {
  1591. X                                error("Revision %s has state %s.",trail->num,
  1592. X                                       trail->state==nil?"<empty>":trail->state);
  1593. X                                return nil;
  1594. X                        }
  1595. X                }
  1596. X                bhead = trail->branches;
  1597. X
  1598. X    } while ((field+=2) <= length);
  1599. X        * store = nil;
  1600. X        return trail;
  1601. X}
  1602. X
  1603. X
  1604. X    static const char *
  1605. Xlookupsym(id)
  1606. X    const char *id;
  1607. X/* Function: looks up id in the list of symbolic names starting
  1608. X * with pointer SYMBOLS, and returns a pointer to the corresponding
  1609. X * revision number. Returns nil if not present.
  1610. X */
  1611. X{
  1612. X    register const struct assoc *next;
  1613. X        next = Symbols;
  1614. X        while (next!=nil) {
  1615. X                if (strcmp(id, next->symbol)==0)
  1616. X            return next->num;
  1617. X                else    next=next->nextassoc;
  1618. X        }
  1619. X        return nil;
  1620. X}
  1621. X
  1622. Xint expandsym(source, target)
  1623. X    const char *source;
  1624. X    struct buf *target;
  1625. X/* Function: Source points to a revision number. Expandsym copies
  1626. X * the number to target, but replaces all symbolic fields in the
  1627. X * source number with their numeric values.
  1628. X * A trailing '.' is omitted; leading zeroes are compressed.
  1629. X * returns false on error;
  1630. X */
  1631. X{
  1632. X    register const char *sp;
  1633. X    register char *tp;
  1634. X    const char *tlim;
  1635. X        register enum tokens d;
  1636. X
  1637. X    bufalloc(target, 1);
  1638. X    tp = target->string;
  1639. X    sp = source;
  1640. X        if (sp == nil) { /*accept nil pointer as a legal value*/
  1641. X                *tp='\0';
  1642. X                return true;
  1643. X        }
  1644. X    tlim = tp + target->size;
  1645. X
  1646. X        while (*sp != '\0') {
  1647. X        switch (ctab[(unsigned char)*sp]) {
  1648. X            case DIGIT:
  1649. X                        if (*sp=='0') {
  1650. X                                /* skip leading zeroes */
  1651. X                                sp++;
  1652. X                                while(*sp == '0') sp++;
  1653. X                if (!*sp || *sp=='.') --sp; /* single zero */
  1654. X                        }
  1655. X            while (isdigit(*sp)) {
  1656. X                if (tlim <= tp)
  1657. X                    tp = bufenlarge(target, &tlim);
  1658. X                *tp++ = *sp++;
  1659. X            }
  1660. X            if (tlim <= tp)
  1661. X                tp = bufenlarge(target, &tlim);
  1662. X                        if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0'))) {
  1663. X                                *tp='\0'; return true;
  1664. X                        }
  1665. X            if (*sp != '.')
  1666. X                goto improper;
  1667. X            *tp++ = *sp++;
  1668. X            break;
  1669. X
  1670. X            case LETTER:
  1671. X            case Letter:
  1672. X            {
  1673. X            register char *bp = tp;
  1674. X            register size_t s = tp - target->string;
  1675. X            do {
  1676. X                if (tlim <= bp)
  1677. X                    bp = bufenlarge(target, &tlim);
  1678. X                *bp++ = *sp++;
  1679. X            } while ((d=ctab[(unsigned char)*sp])==LETTER ||
  1680. X                  d==Letter || d==DIGIT ||
  1681. X                              (d==IDCHAR));
  1682. X            if (tlim <= bp)
  1683. X                bp = bufenlarge(target, &tlim);
  1684. X                        *bp= '\0';
  1685. X            tp = target->string + s;
  1686. X            }
  1687. X            {
  1688. X            register const char *bp = lookupsym(tp);
  1689. X                        if (bp==nil) {
  1690. X                error("Symbolic number %s is undefined.", tp);
  1691. X                                return false;
  1692. X                        } else { /* copy number */
  1693. X                do {
  1694. X                    if (tlim <= tp)
  1695. X                        tp = bufenlarge(target, &tlim);
  1696. X                } while ((*tp++ = *bp++));
  1697. X                        }
  1698. X            }
  1699. X                        if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0')))
  1700. X                                return true;
  1701. X            if (*sp++ != '.')
  1702. X                goto improper;
  1703. X            tp[-1] = '.';
  1704. X            break;
  1705. X
  1706. X            default:
  1707. X            improper:
  1708. X            error("improper revision number: %s", source);
  1709. X                        return false;
  1710. X                }
  1711. X        }
  1712. X    if (tlim<=tp)
  1713. X        tp = bufenlarge(target,&tlim);
  1714. X        *tp = '\0';
  1715. X        return true;
  1716. X}
  1717. X
  1718. X
  1719. X
  1720. X#ifdef REVTEST
  1721. X
  1722. Xconst char cmdid[] = "revtest";
  1723. X
  1724. X    int
  1725. Xmain(argc,argv)
  1726. Xint argc; char * argv[];
  1727. X{
  1728. X    static struct buf numricrevno;
  1729. X    char symrevno[100];       /* used for input of revision numbers */
  1730. X        char author[20];
  1731. X        char state[20];
  1732. X        char date[20];
  1733. X    struct hshentries *gendeltas;
  1734. X        struct hshentry * target;
  1735. X        int i;
  1736. X
  1737. X        if (argc<2) {
  1738. X        aputs("No input file\n",stderr);
  1739. X        exitmain(EXIT_FAILURE);
  1740. X        }
  1741. X        if ((finptr=fopen(argv[1], "r")) == NULL) {
  1742. X        faterror("can't open input file %s", argv[1]);
  1743. X        }
  1744. X        Lexinit();
  1745. X        getadmin();
  1746. X
  1747. X        gettree();
  1748. X
  1749. X        getdesc(false);
  1750. X
  1751. X        do {
  1752. X                /* all output goes to stderr, to have diagnostics and       */
  1753. X                /* errors in sequence.                                      */
  1754. X        aputs("\nEnter revision number or <return> or '.': ",stderr);
  1755. X                if(gets(symrevno)==NULL) break;
  1756. X                if (*symrevno == '.') break;
  1757. X        aprintf(stderr,"%s;\n",symrevno);
  1758. X        expandsym(symrevno,&numricrevno);
  1759. X        aprintf(stderr,"expanded number: %s; ",numricrevno.string);
  1760. X        aprintf(stderr,"Date: ");
  1761. X        gets(date); aprintf(stderr,"%s; ",date);
  1762. X        aprintf(stderr,"Author: ");
  1763. X        gets(author); aprintf(stderr,"%s; ",author);
  1764. X        aprintf(stderr,"State: ");
  1765. X        gets(state); aprintf(stderr, "%s;\n", state);
  1766. X        target = genrevs(numricrevno.string, *date?date:(char *)nil, *author?author:(char *)nil,
  1767. X                 *state?state:(char*)nil, &gendeltas);
  1768. X                if (target!=nil) {
  1769. X            while (gendeltas) {
  1770. X                aprintf(stderr,"%s\n",gendeltas->first->num);
  1771. X                gendeltas = gendeltas->next;
  1772. X                        }
  1773. X                }
  1774. X        } while (true);
  1775. X    aprintf(stderr,"done\n");
  1776. X    exitmain(EXIT_SUCCESS);
  1777. X}
  1778. X
  1779. Xexiting void exiterr() { _exit(EXIT_FAILURE); }
  1780. X
  1781. X#endif
  1782. END_OF_FILE
  1783.   if test 20500 -ne `wc -c <'src/rcsrev.c'`; then
  1784.     echo shar: \"'src/rcsrev.c'\" unpacked with wrong size!
  1785.   fi
  1786.   # end of 'src/rcsrev.c'
  1787. fi
  1788. echo shar: End of archive 7 \(of 12\).
  1789. cp /dev/null ark7isdone
  1790. MISSING=""
  1791. for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
  1792.     if test ! -f ark${I}isdone ; then
  1793.     MISSING="${MISSING} ${I}"
  1794.     fi
  1795. done
  1796. if test "${MISSING}" = "" ; then
  1797.     echo You have unpacked all 12 archives.
  1798.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1799. else
  1800.     echo You still must unpack the following archives:
  1801.     echo "        " ${MISSING}
  1802. fi
  1803. exit 0
  1804. exit 0 # Just in case...
  1805.