home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / rcs-5.6 / part06 < prev    next >
Encoding:
Text File  |  1991-12-19  |  91.0 KB  |  3,600 lines

  1. Newsgroups: comp.sources.unix
  2. From: hammer@cs.purdue.edu (Adam Hammer)
  3. Subject: v25i082: rcs-5.6 - Revision Control System, V5.6, Part06/11
  4. Sender: sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: hammer@cs.purdue.edu (Adam Hammer)
  8. Posting-Number: Volume 25, Issue 82
  9. Archive-Name: rcs-5.6/part06
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 6 (of 11)."
  18. # Contents:  src/rcsfnms.c src/rcslex.c src/rlog.c
  19. # Wrapped by vixie@cognition.pa.dec.com on Fri Dec 20 16:23:41 1991
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'src/rcsfnms.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'src/rcsfnms.c'\"
  23. else
  24. echo shar: Extracting \"'src/rcsfnms.c'\" \(27744 characters\)
  25. sed "s/^X//" >'src/rcsfnms.c' <<'END_OF_FILE'
  26. X/*
  27. X *                     RCS file name handling
  28. X */
  29. X/****************************************************************************
  30. X *                     creation and deletion of /tmp temporaries
  31. X *                     pairing of RCS file names and working file names.
  32. X *                     Testprogram: define PAIRTEST
  33. X ****************************************************************************
  34. X */
  35. X
  36. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  37. X   Copyright 1990, 1991 by Paul Eggert
  38. X   Distributed under license by the Free Software Foundation, Inc.
  39. X
  40. This file is part of RCS.
  41. X
  42. RCS is free software; you can redistribute it and/or modify
  43. it under the terms of the GNU General Public License as published by
  44. the Free Software Foundation; either version 2, or (at your option)
  45. any later version.
  46. X
  47. RCS is distributed in the hope that it will be useful,
  48. but WITHOUT ANY WARRANTY; without even the implied warranty of
  49. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  50. GNU General Public License for more details.
  51. X
  52. You should have received a copy of the GNU General Public License
  53. along with RCS; see the file COPYING.  If not, write to
  54. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  55. X
  56. Report problems and direct all questions to:
  57. X
  58. X    rcs-bugs@cs.purdue.edu
  59. X
  60. X*/
  61. X
  62. X
  63. X
  64. X
  65. X/* $Log: rcsfnms.c,v $
  66. X * Revision 5.8  1991/09/24  00:28:40  eggert
  67. X * Don't export bindex().
  68. X *
  69. X * Revision 5.7  1991/08/19  03:13:55  eggert
  70. X * Fix messages when rcswriteopen fails.
  71. X * Look in $TMP and $TEMP if $TMPDIR isn't set.  Tune.
  72. X *
  73. X * Revision 5.6  1991/04/21  11:58:23  eggert
  74. X * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
  75. X *
  76. X * Revision 5.5  1991/02/26  17:48:38  eggert
  77. X * Fix setuid bug.  Support new link behavior.
  78. X * Define more portable getcwd().
  79. X *
  80. X * Revision 5.4  1990/11/01  05:03:43  eggert
  81. X * Permit arbitrary data in comment leaders.
  82. X *
  83. X * Revision 5.3  1990/09/14  22:56:16  hammer
  84. X * added more filename extensions and their comment leaders
  85. X *
  86. X * Revision 5.2  1990/09/04  08:02:23  eggert
  87. X * Fix typo when !RCSSEP.
  88. X *
  89. X * Revision 5.1  1990/08/29  07:13:59  eggert
  90. X * Work around buggy compilers with defective argument promotion.
  91. X *
  92. X * Revision 5.0  1990/08/22  08:12:50  eggert
  93. X * Ignore signals when manipulating the semaphore file.
  94. X * Modernize list of file name extensions.
  95. X * Permit paths of arbitrary length.  Beware file names beginning with "-".
  96. X * Remove compile-time limits; use malloc instead.
  97. X * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
  98. X * Ansify and Posixate.
  99. X * Don't use access().  Fix test for non-regular files.  Tune.
  100. X *
  101. X * Revision 4.8  89/05/01  15:09:41  narten
  102. X * changed getwd to not stat empty directories.
  103. X * 
  104. X * Revision 4.7  88/08/09  19:12:53  eggert
  105. X * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
  106. X * 
  107. X * Revision 4.6  87/12/18  11:40:23  narten
  108. X * additional file types added from 4.3 BSD version, and SPARC assembler
  109. X * comment character added. Also, more lint cleanups. (Guy Harris)
  110. X * 
  111. X * Revision 4.5  87/10/18  10:34:16  narten
  112. X * Updating version numbers. Changes relative to 1.1 actually relative
  113. X * to verion 4.3
  114. X * 
  115. X * Revision 1.3  87/03/27  14:22:21  jenkins
  116. X * Port to suns
  117. X * 
  118. X * Revision 1.2  85/06/26  07:34:28  svb
  119. X * Comment leader '% ' for '*.tex' files added.
  120. X * 
  121. X * Revision 4.3  83/12/15  12:26:48  wft
  122. X * Added check for KDELIM in file names to pairfilenames().
  123. X * 
  124. X * Revision 4.2  83/12/02  22:47:45  wft
  125. X * Added csh, red, and sl file name suffixes.
  126. X * 
  127. X * Revision 4.1  83/05/11  16:23:39  wft
  128. X * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
  129. X * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
  130. X * 2. added getting the file status of RCS and working files;
  131. X * 3. added ignoring of directories.
  132. X * 
  133. X * Revision 3.7  83/05/11  15:01:58  wft
  134. X * Added comtable[] which pairs file name suffixes with comment leaders;
  135. X * updated InitAdmin() accordingly.
  136. X * 
  137. X * Revision 3.6  83/04/05  14:47:36  wft
  138. X * fixed Suffix in InitAdmin().
  139. X * 
  140. X * Revision 3.5  83/01/17  18:01:04  wft
  141. X * Added getwd() and rename(); these can be removed by defining
  142. X * V4_2BSD, since they are not needed in 4.2 bsd.
  143. X * Changed sys/param.h to sys/types.h.
  144. X *
  145. X * Revision 3.4  82/12/08  21:55:20  wft
  146. X * removed unused variable.
  147. X *
  148. X * Revision 3.3  82/11/28  20:31:37  wft
  149. X * Changed mktempfile() to store the generated file names.
  150. X * Changed getfullRCSname() to store the file and pathname, and to
  151. X * delete leading "../" and "./".
  152. X *
  153. X * Revision 3.2  82/11/12  14:29:40  wft
  154. X * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
  155. X * checksuffix(), checkfullpath(). Semaphore name generation updated.
  156. X * mktempfile() now checks for nil path; freefilename initialized properly.
  157. X * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
  158. X * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
  159. X *
  160. X * Revision 3.1  82/10/18  14:51:28  wft
  161. X * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
  162. X * renamed checkpath() to checkfullpath().
  163. X */
  164. X
  165. X
  166. X#include "rcsbase.h"
  167. X
  168. libId(fnmsId, "$Id: rcsfnms.c,v 5.8 1991/09/24 00:28:40 eggert Exp $")
  169. X
  170. char const *RCSfilename;
  171. char *workfilename;
  172. XFILE *workstdout;
  173. struct stat RCSstat;
  174. char const *suffixes;
  175. X
  176. static char const rcsdir[] = "RCS";
  177. X#define rcsdirlen (sizeof(rcsdir)-1)
  178. X
  179. static struct buf RCSbuf, RCSb;
  180. static int RCSerrno;
  181. X
  182. X
  183. X/* Temp file names to be unlinked when done, if they are not nil.  */
  184. X#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
  185. static char *volatile tfnames[TEMPNAMES];
  186. X
  187. X
  188. struct compair {
  189. X    char const *suffix, *comlead;
  190. X};
  191. X
  192. static struct compair const comtable[] = {
  193. X/* comtable pairs each filename suffix with a comment leader. The comment   */
  194. X/* leader is placed before each line generated by the $Log keyword. This    */
  195. X/* table is used to guess the proper comment leader from the working file's */
  196. X/* suffix during initial ci (see InitAdmin()). Comment leaders are needed   */
  197. X/* for languages without multiline comments; for others they are optional.  */
  198. X    "a",   "-- ",   /* Ada         */
  199. X    "ada", "-- ",
  200. X    "asm", ";; ",    /* assembler (MS-DOS) */
  201. X    "bat", ":: ",    /* batch (MS-DOS) */
  202. X        "c",   " * ",   /* C           */
  203. X    "c++", "// ",    /* C++ in all its infinite guises */
  204. X    "cc",  "// ",
  205. X    "cpp", "// ",
  206. X    "cxx", "// ",
  207. X    "cl",  ";;; ",  /* Common Lisp */
  208. X    "cmd", ":: ",    /* command (OS/2) */
  209. X    "cmf", "c ",    /* CM Fortran  */
  210. X    "cs",  " * ",    /* C*          */
  211. X    "el",  "; ",    /* Emacs Lisp  */
  212. X    "f",   "c ",    /* Fortran     */
  213. X    "for", "c ",
  214. X        "h",   " * ",   /* C-header    */
  215. X    "hpp", "// ",    /* C++ header  */
  216. X    "hxx", "// ",
  217. X        "l",   " * ",   /* lex      NOTE: conflict between lex and franzlisp */
  218. X    "lisp",";;; ",    /* Lucid Lisp  */
  219. X    "lsp", ";; ",    /* Microsoft Lisp */
  220. X    "mac", ";; ",    /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
  221. X    "me",  ".\\\" ",/* me-macros   t/nroff*/
  222. X    "ml",  "; ",    /* mocklisp    */
  223. X    "mm",  ".\\\" ",/* mm-macros   t/nroff*/
  224. X    "ms",  ".\\\" ",/* ms-macros   t/nroff*/
  225. X    "p",   " * ",   /* Pascal      */
  226. X    "pas", " * ",
  227. X    "pl",  "% ",    /* Prolog      */
  228. X    "tex", "% ",    /* TeX           */
  229. X        "y",   " * ",   /* yacc        */
  230. X    nil,   "# "     /* default for unknown suffix; must always be last */
  231. X};
  232. X
  233. X#if has_mktemp
  234. X    static char const *
  235. tmp()
  236. X/* Yield the name of the tmp directory.  */
  237. X{
  238. X    static char const *s;
  239. X    if (!s
  240. X        &&  !(s = cgetenv("TMPDIR"))    /* Unix tradition */
  241. X        &&  !(s = cgetenv("TMP"))    /* DOS tradition */
  242. X        &&  !(s = cgetenv("TEMP"))    /* another DOS tradition */
  243. X    )
  244. X        s = TMPDIR;
  245. X    return s;
  246. X}
  247. X#endif
  248. X
  249. X    char const *
  250. maketemp(n)
  251. X    int n;
  252. X/* Create a unique filename using n and the process id and store it
  253. X * into the nth slot in tfnames.
  254. X * Because of storage in tfnames, tempunlink() can unlink the file later.
  255. X * Returns a pointer to the filename created.
  256. X */
  257. X{
  258. X    char *p;
  259. X    char const *t = tfnames[n];
  260. X
  261. X    if (t)
  262. X        return t;
  263. X
  264. X    catchints();
  265. X    {
  266. X#    if has_mktemp
  267. X        char const *tp = tmp();
  268. X        p = testalloc(strlen(tp) + 10);
  269. X        VOID sprintf(p, "%s%cT%cXXXXXX", tp, SLASH, '0'+n);
  270. X        if (!mktemp(p) || !*p)
  271. X        faterror("can't make temporary file name `%s%cT%cXXXXXX'",
  272. X            tp, SLASH, '0'+n
  273. X        );
  274. X#    else
  275. X        static char tfnamebuf[TEMPNAMES][L_tmpnam];
  276. X        p = tfnamebuf[n];
  277. X        if (!tmpnam(p) || !*p)
  278. X#        ifdef P_tmpdir
  279. X            faterror("can't make temporary file name `%s...'",P_tmpdir);
  280. X#        else
  281. X            faterror("can't make temporary file name");
  282. X#        endif
  283. X#    endif
  284. X    }
  285. X
  286. X    tfnames[n] = p;
  287. X    return p;
  288. X}
  289. X
  290. X    void
  291. tempunlink()
  292. X/* Clean up maketemp() files.  May be invoked by signal handler.
  293. X */
  294. X{
  295. X    register int i;
  296. X    register char *p;
  297. X
  298. X    for (i = TEMPNAMES;  0 <= --i;  )
  299. X        if ((p = tfnames[i])) {
  300. X        VOID unlink(p);
  301. X        /*
  302. X         * We would tfree(p) here,
  303. X         * but this might dump core if we're handing a signal.
  304. X         * We're about to exit anyway, so we won't bother.
  305. X         */
  306. X        tfnames[i] = 0;
  307. X        }
  308. X}
  309. X
  310. X
  311. X    static char const *
  312. bindex(sp,ch)
  313. X    register char const *sp;
  314. X    int ch;
  315. X/* Function: Finds the last occurrence of character c in string sp
  316. X * and returns a pointer to the character just beyond it. If the
  317. X * character doesn't occur in the string, sp is returned.
  318. X */
  319. X{
  320. X    register char const c=ch, *r;
  321. X        r = sp;
  322. X        while (*sp) {
  323. X                if (*sp++ == c) r=sp;
  324. X        }
  325. X        return r;
  326. X}
  327. X
  328. X
  329. X
  330. X    static int
  331. suffix_matches(suffix, pattern)
  332. X    register char const *suffix, *pattern;
  333. X{
  334. X    register int c;
  335. X    if (!pattern)
  336. X        return true;
  337. X    for (;;)
  338. X        switch (*suffix++ - (c = *pattern++)) {
  339. X            case 0:
  340. X            if (!c)
  341. X                return true;
  342. X            break;
  343. X
  344. X            case 'A'-'a':
  345. X            if (ctab[c] == Letter)
  346. X                break;
  347. X            /* fall into */
  348. X            default:
  349. X            return false;
  350. X        }
  351. X}
  352. X
  353. X
  354. X    static void
  355. InitAdmin()
  356. X/* function: initializes an admin node */
  357. X{
  358. X    register char const *Suffix;
  359. X        register int i;
  360. X
  361. X    Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil;
  362. X        StrictLocks=STRICT_LOCKING;
  363. X
  364. X        /* guess the comment leader from the suffix*/
  365. X        Suffix=bindex(workfilename, '.');
  366. X        if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/
  367. X    for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
  368. X        ;
  369. X    Comment.string = comtable[i].comlead;
  370. X    Comment.size = strlen(comtable[i].comlead);
  371. X    Lexinit(); /* note: if !finptr, reads nothing; only initializes */
  372. X}
  373. X
  374. X
  375. X#if defined(_POSIX_NO_TRUNC) && _POSIX_NO_TRUNC!=-1
  376. X#    define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 0
  377. X#else
  378. X#    define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 1
  379. X#endif
  380. X
  381. X#if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED
  382. X#ifdef NAME_MAX
  383. X#    define filenametoolong(path) (NAME_MAX < strlen(basename(path)))
  384. X#else
  385. X    static int
  386. filenametoolong(path)
  387. X    char *path;
  388. X/* Yield true if the last file name in PATH is too long. */
  389. X{
  390. X    static unsigned long dot_namemax;
  391. X
  392. X    register size_t namelen;
  393. X    register char *base;
  394. X    register unsigned long namemax;
  395. X
  396. X    base = path + dirlen(path);
  397. X    namelen = strlen(base);
  398. X    if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */
  399. X        return false;
  400. X    if (base != path) {
  401. X        *--base = 0;
  402. X        namemax = pathconf(path, _PC_NAME_MAX);
  403. X        *base = SLASH;
  404. X    } else {
  405. X        /* Cache the results for the working directory, for speed. */
  406. X        if (!dot_namemax)
  407. X            dot_namemax = pathconf(".", _PC_NAME_MAX);
  408. X        namemax = dot_namemax;
  409. X    }
  410. X    /* If pathconf() yielded -1, namemax is now ULONG_MAX.  */
  411. X    return namemax<namelen;
  412. X}
  413. X#endif
  414. X#endif
  415. X
  416. X    void
  417. bufalloc(b, size)
  418. X    register struct buf *b;
  419. X    size_t size;
  420. X/* Ensure *B is a name buffer of at least SIZE bytes.
  421. X * *B's old contents can be freed; *B's new contents are undefined.
  422. X */
  423. X{
  424. X    if (b->size < size) {
  425. X        if (b->size)
  426. X            tfree(b->string);
  427. X        else
  428. X            b->size = sizeof(malloc_type);
  429. X        while (b->size < size)
  430. X            b->size <<= 1;
  431. X        b->string = tnalloc(char, b->size);
  432. X    }
  433. X}
  434. X
  435. X    void
  436. bufrealloc(b, size)
  437. X    register struct buf *b;
  438. X    size_t size;
  439. X/* like bufalloc, except *B's old contents, if any, are preserved */
  440. X{
  441. X    if (b->size < size) {
  442. X        if (!b->size)
  443. X            bufalloc(b, size);
  444. X        else {
  445. X            while ((b->size <<= 1)  <  size)
  446. X                ;
  447. X            b->string = trealloc(char, b->string, b->size);
  448. X        }
  449. X    }
  450. X}
  451. X
  452. X    void
  453. bufautoend(b)
  454. X    struct buf *b;
  455. X/* Free an auto buffer at block exit. */
  456. X{
  457. X    if (b->size)
  458. X        tfree(b->string);
  459. X}
  460. X
  461. X    struct cbuf
  462. bufremember(b, s)
  463. X    struct buf *b;
  464. X    size_t s;
  465. X/*
  466. X * Free the buffer B with used size S.
  467. X * Yield a cbuf with identical contents.
  468. X * The cbuf will be reclaimed when this input file is finished.
  469. X */
  470. X{
  471. X    struct cbuf cb;
  472. X
  473. X    if ((cb.size = s))
  474. X        cb.string = fremember(trealloc(char, b->string, s));
  475. X    else {
  476. X        bufautoend(b); /* not really auto */
  477. X        cb.string = "";
  478. X    }
  479. X    return cb;
  480. X}
  481. X
  482. X    char *
  483. bufenlarge(b, alim)
  484. X    register struct buf *b;
  485. X    char const **alim;
  486. X/* Make *B larger.  Set *ALIM to its new limit, and yield the relocated value
  487. X * of its old limit.
  488. X */
  489. X{
  490. X    size_t s = b->size;
  491. X    bufrealloc(b, s + 1);
  492. X    *alim = b->string + b->size;
  493. X    return b->string + s;
  494. X}
  495. X
  496. X    void
  497. bufscat(b, s)
  498. X    struct buf *b;
  499. X    char const *s;
  500. X/* Concatenate S to B's end. */
  501. X{
  502. X    size_t blen  =  b->string ? strlen(b->string) : 0;
  503. X    bufrealloc(b, blen+strlen(s)+1);
  504. X    VOID strcpy(b->string+blen, s);
  505. X}
  506. X
  507. X    void
  508. bufscpy(b, s)
  509. X    struct buf *b;
  510. X    char const *s;
  511. X/* Copy S into B. */
  512. X{
  513. X    bufalloc(b, strlen(s)+1);
  514. X    VOID strcpy(b->string, s);
  515. X}
  516. X
  517. X
  518. X    char const *
  519. basename(p)
  520. X    char const *p;
  521. X/* Yield the address of the base filename of the pathname P.  */
  522. X{
  523. X    register char const *b = p, *q = p;
  524. X    for (;;)
  525. X        switch (*q++) {
  526. X        case SLASHes: b = q; break;
  527. X        case 0: return b;
  528. X        }
  529. X}
  530. X
  531. X    size_t
  532. dirlen(p)
  533. X    char const *p;
  534. X/* Yield the length of P's directory, including its trailing SLASH.  */
  535. X{
  536. X    return basename(p) - p;
  537. X}
  538. X
  539. X
  540. X    static size_t
  541. suffixlen(x)
  542. X    char const *x;
  543. X/* Yield the length of X, an RCS filename suffix.  */
  544. X{
  545. X    register char const *p;
  546. X
  547. X    p = x;
  548. X    for (;;)
  549. X        switch (*p) {
  550. X        case 0: case SLASHes:
  551. X            return p - x;
  552. X
  553. X        default:
  554. X            ++p;
  555. X            continue;
  556. X        }
  557. X}
  558. X
  559. X    char const *
  560. rcssuffix(name)
  561. X    char const *name;
  562. X/* Yield the suffix of NAME if it is an RCS filename, 0 otherwise.  */
  563. X{
  564. X    char const *x, *p, *nz;
  565. X    size_t dl, nl, xl;
  566. X
  567. X    nl = strlen(name);
  568. X    nz = name + nl;
  569. X    x = suffixes;
  570. X    do {
  571. X        if ((xl = suffixlen(x))) {
  572. X        if (xl <= nl  &&  memcmp(p = nz-xl, x, xl) == 0)
  573. X            return p;
  574. X        } else {
  575. X        dl = dirlen(name);
  576. X        if (
  577. X            rcsdirlen < dl  &&
  578. X            !memcmp(p = name+(dl-=rcsdirlen+1), rcsdir, rcsdirlen) &&
  579. X            (!dl  ||  isSLASH(*--p))
  580. X        )
  581. X            return nz;
  582. X        }
  583. X        x += xl;
  584. X    } while (*x++);
  585. X    return 0;
  586. X}
  587. X
  588. X    /*ARGSUSED*/ RILE *
  589. rcsreadopen(RCSname, status, mustread)
  590. X    struct buf *RCSname;
  591. X    struct stat *status;
  592. X    int mustread;
  593. X/* Open RCSNAME for reading and yield its FILE* descriptor.
  594. X * If successful, set *STATUS to its status.
  595. X * Pass this routine to pairfilenames() for read-only access to the file.  */
  596. X{
  597. X    return Iopen(RCSname->string, FOPEN_R, status);
  598. X}
  599. X
  600. X    static int
  601. finopen(rcsopen, mustread)
  602. X    RILE *(*rcsopen)P((struct buf*,struct stat*,int));
  603. X    int mustread;
  604. X/*
  605. X * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
  606. X * Set finptr to the result and yield true if successful.
  607. X * RCSb holds the file's name.
  608. X * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
  609. X * Yield true if successful or if an unusual failure.
  610. X */
  611. X{
  612. X    int interesting, preferold;
  613. X
  614. X    /*
  615. X     * We prefer an old name to that of a nonexisting new RCS file,
  616. X     * unless we tried locking the old name and failed.
  617. X     */
  618. X    preferold  =  RCSbuf.string[0] && (mustread||frewrite);
  619. X
  620. X    finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
  621. X    interesting = finptr || errno!=ENOENT;
  622. X    if (interesting || !preferold) {
  623. X        /* Use the new name.  */
  624. X        RCSerrno = errno;
  625. X        bufscpy(&RCSbuf, RCSb.string);
  626. X    }
  627. X    return interesting;
  628. X}
  629. X
  630. X    static int
  631. fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
  632. X    char const *d, *base, *x;
  633. X    size_t dlen, baselen, xlen;
  634. X    RILE *(*rcsopen)P((struct buf*,struct stat*,int));
  635. X    int mustread;
  636. X/*
  637. X * D is a directory name with length DLEN (including trailing slash).
  638. X * BASE is a filename with length BASELEN.
  639. X * X is an RCS filename suffix with length XLEN.
  640. X * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
  641. X * Yield true if successful.
  642. X * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
  643. X * Put these potential names in RCSb.
  644. X * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
  645. X * Yield true if successful or if an unusual failure.
  646. X */
  647. X{
  648. X    register char *p;
  649. X
  650. X    bufalloc(&RCSb, dlen + rcsdirlen + 1 + baselen + xlen + 1);
  651. X
  652. X    /* Try dRCS/basex.  */
  653. X    VOID memcpy(p = RCSb.string, d, dlen);
  654. X    VOID memcpy(p += dlen, rcsdir, rcsdirlen);
  655. X    p += rcsdirlen;
  656. X    *p++ = SLASH;
  657. X    VOID memcpy(p, base, baselen);
  658. X    VOID memcpy(p += baselen, x, xlen);
  659. X    p[xlen] = 0;
  660. X    if (xlen) {
  661. X        if (finopen(rcsopen, mustread))
  662. X        return true;
  663. X
  664. X        /* Try dbasex.  */
  665. X        /* Start from scratch, because finopen() may have changed RCSb.  */
  666. X        VOID memcpy(p = RCSb.string, d, dlen);
  667. X        VOID memcpy(p += dlen, base, baselen);
  668. X        VOID memcpy(p += baselen, x, xlen);
  669. X        p[xlen] = 0;
  670. X    }
  671. X    return finopen(rcsopen, mustread);
  672. X}
  673. X
  674. X    int
  675. pairfilenames(argc, argv, rcsopen, mustread, quiet)
  676. X    int argc;
  677. X    char **argv;
  678. X    RILE *(*rcsopen)P((struct buf*,struct stat*,int));
  679. X    int mustread, quiet;
  680. X/* Function: Pairs the filenames pointed to by argv; argc indicates
  681. X * how many there are.
  682. X * Places a pointer to the RCS filename into RCSfilename,
  683. X * and a pointer to the name of the working file into workfilename.
  684. X * If both the workfilename and the RCS filename are given, and workstdout
  685. X * is set, a warning is printed.
  686. X *
  687. X * If the RCS file exists, places its status into RCSstat.
  688. X *
  689. X * If the RCS file exists, it is RCSOPENed for reading, the file pointer
  690. X * is placed into finptr, and the admin-node is read in; returns 1.
  691. X * If the RCS file does not exist and MUSTREAD,
  692. X * print an error unless QUIET and return 0.
  693. X * Otherwise, initialize the admin node and return -1.
  694. X *
  695. X * 0 is returned on all errors, e.g. files that are not regular files.
  696. X */
  697. X{
  698. X    static struct buf tempbuf;
  699. X
  700. X    register char *p, *arg, *RCS1;
  701. X    char const *purefname, *pureRCSname, *x;
  702. X    int paired;
  703. X    size_t arglen, dlen, baselen, xlen;
  704. X
  705. X    if (!(arg = *argv)) return 0; /* already paired filename */
  706. X    if (*arg == '-') {
  707. X        error("%s option is ignored after file names", arg);
  708. X        return 0;
  709. X    }
  710. X
  711. X    purefname = basename(arg);
  712. X
  713. X    /* Allocate buffer temporary to hold the default paired file name. */
  714. X    p = arg;
  715. X    for (;;) {
  716. X        switch (*p++) {
  717. X            /* Beware characters that cause havoc with ci -k. */
  718. X            case KDELIM:
  719. X            error("RCS file name `%s' contains %c", arg, KDELIM);
  720. X            return 0;
  721. X            case ' ': case '\n': case '\t':
  722. X            error("RCS file name `%s' contains white space", arg);
  723. X            return 0;
  724. X            default:
  725. X            continue;
  726. X            case 0:
  727. X            break;
  728. X        }
  729. X        break;
  730. X    }
  731. X
  732. X    paired = false;
  733. X
  734. X        /* first check suffix to see whether it is an RCS file or not */
  735. X    if ((x = rcssuffix(arg)))
  736. X    {
  737. X                /* RCS file name given*/
  738. X        RCS1 = arg;
  739. X        pureRCSname = purefname;
  740. X        baselen = x - purefname;
  741. X        if (
  742. X            1 < argc  &&
  743. X            !rcssuffix(workfilename = p = argv[1])  &&
  744. X            baselen <= (arglen = strlen(p))  &&
  745. X            ((p+=arglen-baselen) == workfilename  ||  isSLASH(p[-1])) &&
  746. X            memcmp(purefname, p, baselen) == 0
  747. X        ) {
  748. X            argv[1] = 0;
  749. X            paired = true;
  750. X        } else {
  751. X            bufscpy(&tempbuf, purefname);
  752. X            workfilename = p = tempbuf.string;
  753. X            p[baselen] = 0;
  754. X        }
  755. X        } else {
  756. X                /* working file given; now try to find RCS file */
  757. X        workfilename = arg;
  758. X        baselen = p - purefname - 1;
  759. X                /* derive RCS file name*/
  760. X        if (
  761. X            1 < argc  &&
  762. X            (x = rcssuffix(RCS1 = argv[1]))  &&
  763. X            baselen  <=  x - RCS1  &&
  764. X            ((pureRCSname=x-baselen)==RCS1 || isSLASH(pureRCSname[-1])) &&
  765. X            memcmp(purefname, pureRCSname, baselen) == 0
  766. X        ) {
  767. X            argv[1] = 0;
  768. X            paired = true;
  769. X        } else
  770. X            pureRCSname = RCS1 = 0;
  771. X        }
  772. X        /* now we have a (tentative) RCS filename in RCS1 and workfilename  */
  773. X        /* Second, try to find the right RCS file */
  774. X        if (pureRCSname!=RCS1) {
  775. X                /* a path for RCSfile is given; single RCS file to look for */
  776. X        bufscpy(&RCSbuf, RCS1);
  777. X        finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
  778. X        RCSerrno = errno;
  779. X        } else {
  780. X        bufscpy(&RCSbuf, "");
  781. X        if (RCS1)
  782. X            /* RCS file name was given without path.  */
  783. X            VOID fin2open(arg, (size_t)0, pureRCSname, baselen,
  784. X                x, strlen(x), rcsopen, mustread
  785. X            );
  786. X        else {
  787. X            /* No RCS file name was given.  */
  788. X            /* Try each suffix in turn.  */
  789. X            dlen = purefname-arg;
  790. X            x = suffixes;
  791. X            while (! fin2open(arg, dlen, purefname, baselen,
  792. X                    x, xlen=suffixlen(x), rcsopen, mustread
  793. X            )) {
  794. X                x += xlen;
  795. X                if (!*x++)
  796. X                    break;
  797. X            }
  798. X        }
  799. X        }
  800. X    RCSfilename = p = RCSbuf.string;
  801. X    if (finptr) {
  802. X        if (!S_ISREG(RCSstat.st_mode)) {
  803. X            error("%s isn't a regular file -- ignored", p);
  804. X                        return 0;
  805. X                }
  806. X                Lexinit(); getadmin();
  807. X    } else {
  808. X        if (RCSerrno!=ENOENT || mustread || !frewrite) {
  809. X            if (RCSerrno == EEXIST)
  810. X                error("RCS file %s is in use", p);
  811. X            else if (!quiet || RCSerrno!=ENOENT)
  812. X                enerror(RCSerrno, p);
  813. X            return 0;
  814. X        }
  815. X                InitAdmin();
  816. X        };
  817. X#    if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED
  818. X        if (filenametoolong(p)) {
  819. X        error("RCS file name %s is too long", p);
  820. X        return 0;
  821. X        }
  822. X#        ifndef NAME_MAX
  823. X        /*
  824. X         * Check workfilename too, even though it cannot be longer,
  825. X         * because it may reside on a different filesystem.
  826. X         */
  827. X        if (filenametoolong(workfilename)) {
  828. X            error("working file name %s is too long", workfilename);
  829. X            return 0;
  830. X        }
  831. X#        endif
  832. X#    endif
  833. X
  834. X    if (paired && workstdout)
  835. X                warn("Option -p is set; ignoring output file %s",workfilename);
  836. X
  837. X    prevkeys = false;
  838. X    return finptr ? 1 : -1;
  839. X}
  840. X
  841. X
  842. X    char const *
  843. getfullRCSname()
  844. X/* Function: returns a pointer to the full path name of the RCS file.
  845. X * Gets the working directory's name at most once.
  846. X * Removes leading "../" and "./".
  847. X */
  848. X{
  849. X    static char const *wdptr;
  850. X    static struct buf rcsbuf, wdbuf;
  851. X    static size_t pathlength;
  852. X
  853. X    register char const *realname;
  854. X    register size_t parentdirlength;
  855. X    register unsigned dotdotcounter;
  856. X    register char *d;
  857. X    register char const *wd;
  858. X
  859. X    if (ROOTPATH(RCSfilename)) {
  860. X                return(RCSfilename);
  861. X        } else {
  862. X        if (!(wd = wdptr)) {
  863. X            /* Get working directory for the first time.  */
  864. X            if (!(d = cgetenv("PWD"))) {
  865. X            bufalloc(&wdbuf, SIZEABLE_PATH + 1);
  866. X#            if !has_getcwd && has_getwd
  867. X                d = getwd(wdbuf.string);
  868. X#            else
  869. X                while (
  870. X                    !(d = getcwd(wdbuf.string, wdbuf.size))
  871. X                &&  errno==ERANGE
  872. X                )
  873. X                bufalloc(&wdbuf, wdbuf.size<<1);
  874. X#            endif
  875. X            if (!d)
  876. X                efaterror("working directory");
  877. X            }
  878. X            parentdirlength = strlen(d);
  879. X            while (parentdirlength && isSLASH(d[parentdirlength-1])) {
  880. X            d[--parentdirlength] = 0;
  881. X                        /* Check needed because some getwd implementations */
  882. X                        /* generate "/" for the root.                      */
  883. X                    }
  884. X            wdptr = wd = d;
  885. X            pathlength = parentdirlength;
  886. X                }
  887. X                /*the following must be redone since RCSfilename may change*/
  888. X        /* Find how many `../'s to remove from RCSfilename.  */
  889. X                dotdotcounter =0;
  890. X                realname = RCSfilename;
  891. X        while (realname[0]=='.') {
  892. X            if (isSLASH(realname[1])) {
  893. X                            /* drop leading ./ */
  894. X                            realname += 2;
  895. X            } else if (realname[1]=='.' && isSLASH(realname[2])) {
  896. X                            /* drop leading ../ and remember */
  897. X                            dotdotcounter++;
  898. X                            realname += 3;
  899. X            } else
  900. X                break;
  901. X                }
  902. X        /* Now remove dotdotcounter trailing directories from wd. */
  903. X        parentdirlength = pathlength;
  904. X        while (dotdotcounter && parentdirlength) {
  905. X                    /* move pointer backwards over trailing directory */
  906. X            if (isSLASH(wd[--parentdirlength])) {
  907. X                        dotdotcounter--;
  908. X                    }
  909. X                }
  910. X        /* build full path name */
  911. X        bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2);
  912. X        d = rcsbuf.string;
  913. X        VOID memcpy(d, wd, parentdirlength);
  914. X        d += parentdirlength;
  915. X        *d++ = SLASH;
  916. X        VOID strcpy(d, realname);
  917. X        return rcsbuf.string;
  918. X        }
  919. X}
  920. X
  921. X#ifndef isSLASH
  922. X    int
  923. isSLASH(c)
  924. X    int c;
  925. X{
  926. X    switch (c) {
  927. X        case SLASHes:
  928. X        return true;
  929. X        default:
  930. X        return false;
  931. X    }
  932. X}
  933. X#endif
  934. X
  935. X
  936. X#if !has_getcwd && !has_getwd
  937. X
  938. X    char *
  939. getcwd(path, size)
  940. X    char *path;
  941. X    size_t size;
  942. X{
  943. X    static char const usrbinpwd[] = "/usr/bin/pwd";
  944. X#    define binpwd (usrbinpwd+4)
  945. X
  946. X    register FILE *fp;
  947. X    register int c;
  948. X    register char *p, *lim;
  949. X    int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
  950. X    pid_t child;
  951. X#    if !has_waitpid
  952. X        pid_t w;
  953. X#    endif
  954. X
  955. X    if (!size) {
  956. X        errno = EINVAL;
  957. X        return 0;
  958. X    }
  959. X    if (pipe(fd) != 0)
  960. X        return 0;
  961. X    if (!(child = vfork())) {
  962. X        if (
  963. X            close(fd[0]) == 0 &&
  964. X            (fd[1] == STDOUT_FILENO ||
  965. X#                ifdef F_DUPFD
  966. X                    (VOID close(STDOUT_FILENO),
  967. X                    fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
  968. X#                else
  969. X                    dup2(fd[1], STDOUT_FILENO)
  970. X#                endif
  971. X                == STDOUT_FILENO &&
  972. X                close(fd[1]) == 0
  973. X            )
  974. X        ) {
  975. X            VOID close(STDERR_FILENO);
  976. X            VOID execl(binpwd, binpwd, (char *)0);
  977. X            VOID execl(usrbinpwd, usrbinpwd, (char *)0);
  978. X        }
  979. X        _exit(EXIT_FAILURE);
  980. X    }
  981. X    e = errno;
  982. X    closeerror = close(fd[1]);
  983. X    closeerrno = errno;
  984. X    fp = 0;
  985. X    readerror = toolong = wstatus = 0;
  986. X    p = path;
  987. X    if (0 <= child) {
  988. X        fp = fdopen(fd[0], "r");
  989. X        e = errno;
  990. X        if (fp) {
  991. X            lim = p + size;
  992. X            for (p = path;  ;  *p++ = c) {
  993. X                if ((c=getc(fp)) < 0) {
  994. X                    if (feof(fp))
  995. X                        break;
  996. X                    if (ferror(fp)) {
  997. X                        readerror = 1;
  998. X                        e = errno;
  999. X                        break;
  1000. X                    }
  1001. X                }
  1002. X                if (p == lim) {
  1003. X                    toolong = 1;
  1004. X                    break;
  1005. X                }
  1006. X            }
  1007. X        }
  1008. X#        if has_waitpid
  1009. X            if (waitpid(child, &wstatus, 0) < 0)
  1010. X                wstatus = 1;
  1011. X#        else
  1012. X            do {
  1013. X                if ((w = wait(&wstatus)) < 0) {
  1014. X                    wstatus = 1;
  1015. X                    break;
  1016. X                }
  1017. X            } while (w != child);
  1018. X#        endif
  1019. X    }
  1020. X    if (!fp) {
  1021. X        VOID close(fd[0]);
  1022. X        errno = e;
  1023. X        return 0;
  1024. X    }
  1025. X    if (fclose(fp) != 0)
  1026. X        return 0;
  1027. X    if (readerror) {
  1028. X        errno = e;
  1029. X        return 0;
  1030. X    }
  1031. X    if (closeerror) {
  1032. X        errno = closeerrno;
  1033. X        return 0;
  1034. X    }
  1035. X    if (toolong) {
  1036. X        errno = ERANGE;
  1037. X        return 0;
  1038. X    }
  1039. X    if (wstatus  ||  p == path  ||  *--p != '\n') {
  1040. X        errno = EACCES;
  1041. X        return 0;
  1042. X    }
  1043. X    *p = '\0';
  1044. X    return path;
  1045. X}
  1046. X#endif
  1047. X
  1048. X
  1049. X#ifdef PAIRTEST
  1050. X/* test program for pairfilenames() and getfullRCSname() */
  1051. X
  1052. char const cmdid[] = "pair";
  1053. X
  1054. main(argc, argv)
  1055. int argc; char *argv[];
  1056. X{
  1057. X        int result;
  1058. X    int initflag;
  1059. X    quietflag = initflag = false;
  1060. X
  1061. X        while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
  1062. X                switch ((*argv)[1]) {
  1063. X
  1064. X        case 'p':       workstdout = stdout;
  1065. X                                break;
  1066. X                case 'i':       initflag=true;
  1067. X                                break;
  1068. X                case 'q':       quietflag=true;
  1069. X                                break;
  1070. X                default:        error("unknown option: %s", *argv);
  1071. X                                break;
  1072. X                }
  1073. X        }
  1074. X
  1075. X        do {
  1076. X                RCSfilename=workfilename=nil;
  1077. X        result = pairfilenames(argc,argv,rcsreadopen,!initflag,quietflag);
  1078. X                if (result!=0) {
  1079. X            diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n",
  1080. X                 RCSfilename,workfilename,getfullRCSname()
  1081. X            );
  1082. X                }
  1083. X                switch (result) {
  1084. X                        case 0: continue; /* already paired file */
  1085. X
  1086. X                        case 1: if (initflag) {
  1087. X                                    error("RCS file %s exists already",RCSfilename);
  1088. X                                } else {
  1089. X                    diagnose("RCS file %s exists\n",RCSfilename);
  1090. X                                }
  1091. X                Ifclose(finptr);
  1092. X                                break;
  1093. X
  1094. X            case -1:diagnose("RCS file doesn't exist\n");
  1095. X                                break;
  1096. X                }
  1097. X
  1098. X        } while (++argv, --argc>=1);
  1099. X
  1100. X}
  1101. X
  1102. X    exiting void
  1103. exiterr()
  1104. X{
  1105. X    dirtempunlink();
  1106. X    tempunlink();
  1107. X    _exit(EXIT_FAILURE);
  1108. X}
  1109. X#endif
  1110. END_OF_FILE
  1111. if test 27744 -ne `wc -c <'src/rcsfnms.c'`; then
  1112.     echo shar: \"'src/rcsfnms.c'\" unpacked with wrong size!
  1113. fi
  1114. # end of 'src/rcsfnms.c'
  1115. fi
  1116. if test -f 'src/rcslex.c' -a "${1}" != "-c" ; then 
  1117.   echo shar: Will not clobber existing file \"'src/rcslex.c'\"
  1118. else
  1119. echo shar: Extracting \"'src/rcslex.c'\" \(27526 characters\)
  1120. sed "s/^X//" >'src/rcslex.c' <<'END_OF_FILE'
  1121. X/*
  1122. X *                     RCS file input
  1123. X */
  1124. X/*********************************************************************************
  1125. X *                     Lexical Analysis.
  1126. X *                     hashtable, Lexinit, nextlex, getlex, getkey,
  1127. X *                     getid, getnum, readstring, printstring, savestring,
  1128. X *                     checkid, fatserror, error, faterror, warn, diagnose
  1129. X *                     Testprogram: define LEXDB
  1130. X *********************************************************************************
  1131. X */
  1132. X
  1133. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  1134. X   Copyright 1990, 1991 by Paul Eggert
  1135. X   Distributed under license by the Free Software Foundation, Inc.
  1136. X
  1137. This file is part of RCS.
  1138. X
  1139. RCS is free software; you can redistribute it and/or modify
  1140. it under the terms of the GNU General Public License as published by
  1141. the Free Software Foundation; either version 2, or (at your option)
  1142. any later version.
  1143. X
  1144. RCS is distributed in the hope that it will be useful,
  1145. but WITHOUT ANY WARRANTY; without even the implied warranty of
  1146. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  1147. GNU General Public License for more details.
  1148. X
  1149. You should have received a copy of the GNU General Public License
  1150. along with RCS; see the file COPYING.  If not, write to
  1151. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  1152. X
  1153. Report problems and direct all questions to:
  1154. X
  1155. X    rcs-bugs@cs.purdue.edu
  1156. X
  1157. X*/
  1158. X
  1159. X
  1160. X
  1161. X/* $Log: rcslex.c,v $
  1162. X * Revision 5.11  1991/11/03  03:30:44  eggert
  1163. X * Fix porting bug to ancient hosts lacking vfprintf.
  1164. X *
  1165. X * Revision 5.10  1991/10/07  17:32:46  eggert
  1166. X * Support piece tables even if !has_mmap.
  1167. X *
  1168. X * Revision 5.9  1991/09/24  00:28:42  eggert
  1169. X * Don't export errsay().
  1170. X *
  1171. X * Revision 5.8  1991/08/19  03:13:55  eggert
  1172. X * Add eoflex(), mmap support.  Tune.
  1173. X *
  1174. X * Revision 5.7  1991/04/21  11:58:26  eggert
  1175. X * Add MS-DOS support.
  1176. X *
  1177. X * Revision 5.6  1991/02/25  07:12:42  eggert
  1178. X * Work around fputs bug.  strsave -> str_save (DG/UX name clash)
  1179. X *
  1180. X * Revision 5.5  1990/12/04  05:18:47  eggert
  1181. X * Use -I for prompts and -q for diagnostics.
  1182. X *
  1183. X * Revision 5.4  1990/11/19  20:05:28  hammer
  1184. X * no longer gives warning about unknown keywords if -q is specified
  1185. X *
  1186. X * Revision 5.3  1990/11/01  05:03:48  eggert
  1187. X * When ignoring unknown phrases, copy them to the output RCS file.
  1188. X *
  1189. X * Revision 5.2  1990/09/04  08:02:27  eggert
  1190. X * Count RCS lines better.
  1191. X *
  1192. X * Revision 5.1  1990/08/29  07:14:03  eggert
  1193. X * Work around buggy compilers with defective argument promotion.
  1194. X *
  1195. X * Revision 5.0  1990/08/22  08:12:55  eggert
  1196. X * Remove compile-time limits; use malloc instead.
  1197. X * Report errno-related errors with perror().
  1198. X * Ansify and Posixate.  Add support for ISO 8859.
  1199. X * Use better hash function.
  1200. X *
  1201. X * Revision 4.6  89/05/01  15:13:07  narten
  1202. X * changed copyright header to reflect current distribution rules
  1203. X * 
  1204. X * Revision 4.5  88/08/28  15:01:12  eggert
  1205. X * Don't loop when writing error messages to a full filesystem.
  1206. X * Flush stderr/stdout when mixing output.
  1207. X * Yield exit status compatible with diff(1).
  1208. X * Shrink stdio code size; allow cc -R; remove lint.
  1209. X * 
  1210. X * Revision 4.4  87/12/18  11:44:47  narten
  1211. X * fixed to use "varargs" in "fprintf"; this is required if it is to
  1212. X * work on a SPARC machine such as a Sun-4
  1213. X * 
  1214. X * Revision 4.3  87/10/18  10:37:18  narten
  1215. X * Updating version numbers. Changes relative to 1.1 actually relative
  1216. X * to version 4.1
  1217. X * 
  1218. X * Revision 1.3  87/09/24  14:00:17  narten
  1219. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  1220. X * warnings)
  1221. X * 
  1222. X * Revision 1.2  87/03/27  14:22:33  jenkins
  1223. X * Port to suns
  1224. X * 
  1225. X * Revision 4.1  83/03/25  18:12:51  wft
  1226. X * Only changed $Header to $Id.
  1227. X * 
  1228. X * Revision 3.3  82/12/10  16:22:37  wft
  1229. X * Improved error messages, changed exit status on error to 1.
  1230. X *
  1231. X * Revision 3.2  82/11/28  21:27:10  wft
  1232. X * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
  1233. X * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
  1234. X * properly in case there is an IO-error (e.g., file system full).
  1235. X *
  1236. X * Revision 3.1  82/10/11  19:43:56  wft
  1237. X * removed unused label out:;
  1238. X * made sure all calls to getc() return into an integer, not a char.
  1239. X */
  1240. X
  1241. X
  1242. X/*
  1243. X#define LEXDB
  1244. X*/
  1245. X/* version LEXDB is for testing the lexical analyzer. The testprogram
  1246. X * reads a stream of lexemes, enters the revision numbers into the
  1247. X * hashtable, and prints the recognized tokens. Keywords are recognized
  1248. X * as identifiers.
  1249. X */
  1250. X
  1251. X
  1252. X
  1253. X#include "rcsbase.h"
  1254. X
  1255. libId(lexId, "$Id: rcslex.c,v 5.11 1991/11/03 03:30:44 eggert Exp $")
  1256. X
  1257. static struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
  1258. X
  1259. enum tokens     nexttok;    /*next token, set by nextlex                    */
  1260. X
  1261. int             hshenter;   /*if true, next suitable lexeme will be entered */
  1262. X                            /*into the symbol table. Handle with care.      */
  1263. int             nextc;      /*next input character, initialized by Lexinit  */
  1264. X
  1265. unsigned long    rcsline;    /*current line-number of input            */
  1266. int             nerror;     /*counter for errors                            */
  1267. int             quietflag;  /*indicates quiet mode                          */
  1268. RILE *        finptr;        /*input file descriptor                */
  1269. X
  1270. XFILE *          frewrite;   /*file descriptor for echoing input             */
  1271. X
  1272. XFILE *        foutptr;    /* copy of frewrite, but 0 to suppress echo  */
  1273. X
  1274. static struct buf tokbuf;   /* token buffer                    */
  1275. X
  1276. char const *    NextString; /* next token                    */
  1277. X
  1278. X/*
  1279. X * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
  1280. X * so hshsize should be odd.
  1281. X * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
  1282. X * Software--practice & experience 20, 2 (Feb 1990), 209-224.
  1283. X */
  1284. X#ifndef hshsize
  1285. X#    define hshsize 511
  1286. X#endif
  1287. X
  1288. static struct hshentry *hshtab[hshsize]; /*hashtable                */
  1289. X
  1290. static int ignored_phrases; /* have we ignored phrases in this RCS file? */
  1291. X
  1292. X    void
  1293. warnignore()
  1294. X{
  1295. X    if (! (ignored_phrases|quietflag)) {
  1296. X    ignored_phrases = true;
  1297. X    warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString);
  1298. X    }
  1299. X}
  1300. X
  1301. X
  1302. X
  1303. X    static void
  1304. lookup(str)
  1305. X    char const *str;
  1306. X/* Function: Looks up the character string pointed to by str in the
  1307. X * hashtable. If the string is not present, a new entry for it is created.
  1308. X * In any case, the address of the corresponding hashtable entry is placed
  1309. X * into nexthsh.
  1310. X */
  1311. X{
  1312. X    register unsigned ihash;  /* index into hashtable */
  1313. X    register char const *sp;
  1314. X    register struct hshentry *n, **p;
  1315. X
  1316. X        /* calculate hash code */
  1317. X    sp = str;
  1318. X        ihash = 0;
  1319. X    while (*sp)
  1320. X        ihash  =  (ihash<<2) + *sp++;
  1321. X    ihash %= hshsize;
  1322. X
  1323. X    for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
  1324. X        if (!(n = *p)) {
  1325. X            /* empty slot found */
  1326. X            *p = n = ftalloc(struct hshentry);
  1327. X            n->num = fstr_save(str);
  1328. X            n->nexthsh = nil;
  1329. X#            ifdef LEXDB
  1330. X                VOID printf("\nEntered: %s at %u ", str, ihash);
  1331. X#            endif
  1332. X            break;
  1333. X        } else if (strcmp(str, n->num) == 0)
  1334. X            /* match found */
  1335. X            break;
  1336. X    nexthsh = n;
  1337. X    NextString = n->num;
  1338. X}
  1339. X
  1340. X
  1341. X
  1342. X
  1343. X
  1344. X
  1345. X    void
  1346. Lexinit()
  1347. X/* Function: Initialization of lexical analyzer:
  1348. X * initializes the hashtable,
  1349. X * initializes nextc, nexttok if finptr != 0
  1350. X */
  1351. X{       register int            c;
  1352. X
  1353. X    for (c = hshsize;  0 <= --c;  ) {
  1354. X        hshtab[c] = nil;
  1355. X        }
  1356. X
  1357. X    nerror = 0;
  1358. X    if (finptr) {
  1359. X        foutptr = 0;
  1360. X        hshenter = true;
  1361. X        ignored_phrases = false;
  1362. X        rcsline = 1;
  1363. X        bufrealloc(&tokbuf, 2);
  1364. X        Iget(finptr, nextc);
  1365. X                nextlex();            /*initial token*/
  1366. X        }
  1367. X}
  1368. X
  1369. X
  1370. X
  1371. X
  1372. X
  1373. X
  1374. X
  1375. X    void
  1376. nextlex()
  1377. X
  1378. X/* Function: Reads the next token and sets nexttok to the next token code.
  1379. X * Only if hshenter is set, a revision number is entered into the
  1380. X * hashtable and a pointer to it is placed into nexthsh.
  1381. X * This is useful for avoiding that dates are placed into the hashtable.
  1382. X * For ID's and NUM's, NextString is set to the character string.
  1383. X * Assumption: nextc contains the next character.
  1384. X */
  1385. X{       register c;
  1386. X    declarecache;
  1387. X    register FILE *frew;
  1388. X        register char * sp;
  1389. X    char const *limit;
  1390. X        register enum tokens d;
  1391. X    register RILE *fin;
  1392. X
  1393. X    fin=finptr; frew=foutptr;
  1394. X    setupcache(fin); cache(fin);
  1395. X    c = nextc;
  1396. X
  1397. X    for (;;) { switch ((d = ctab[c])) {
  1398. X
  1399. X    default:
  1400. X        fatserror("unknown character `%c'", c);
  1401. X        /*NOTREACHED*/
  1402. X
  1403. X        case NEWLN:
  1404. X        ++rcsline;
  1405. X#               ifdef LEXDB
  1406. X        afputc('\n',stdout);
  1407. X#               endif
  1408. X                /* Note: falls into next case */
  1409. X
  1410. X        case SPACE:
  1411. X        GETC(frew, c);
  1412. X        continue;
  1413. X
  1414. X        case DIGIT:
  1415. X        sp = tokbuf.string;
  1416. X        limit = sp + tokbuf.size;
  1417. X        *sp++ = c;
  1418. X        for (;;) {
  1419. X            GETC(frew, c);
  1420. X            if ((d=ctab[c])!=DIGIT && d!=PERIOD)
  1421. X                break;
  1422. X                        *sp++ = c;         /* 1.2. and 1.2 are different */
  1423. X            if (limit <= sp)
  1424. X                sp = bufenlarge(&tokbuf, &limit);
  1425. X                }
  1426. X        *sp = 0;
  1427. X        if (hshenter)
  1428. X            lookup(tokbuf.string);
  1429. X        else
  1430. X            NextString = fstr_save(tokbuf.string);
  1431. X        d = NUM;
  1432. X        break;
  1433. X
  1434. X
  1435. X        case LETTER:
  1436. X    case Letter:
  1437. X        sp = tokbuf.string;
  1438. X        limit = sp + tokbuf.size;
  1439. X        *sp++ = c;
  1440. X        for (;;) {
  1441. X            GETC(frew, c);
  1442. X            if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR)
  1443. X                break;
  1444. X                        *sp++ = c;
  1445. X            if (limit <= sp)
  1446. X                sp = bufenlarge(&tokbuf, &limit);
  1447. X                }
  1448. X        *sp = 0;
  1449. X        NextString = fstr_save(tokbuf.string);
  1450. X        d = ID;  /* may be ID or keyword */
  1451. X        break;
  1452. X
  1453. X        case SBEGIN: /* long string */
  1454. X        d = STRING;
  1455. X                /* note: only the initial SBEGIN has been read*/
  1456. X                /* read the string, and reset nextc afterwards*/
  1457. X        break;
  1458. X
  1459. X    case COLON:
  1460. X    case SEMI:
  1461. X        GETC(frew, c);
  1462. X        break;
  1463. X    } break; }
  1464. X    nextc = c;
  1465. X    nexttok = d;
  1466. X    uncache(fin);
  1467. X}
  1468. X
  1469. X    int
  1470. eoflex()
  1471. X/*
  1472. X * Yield true if we look ahead to the end of the input, false otherwise.
  1473. X * nextc becomes undefined at end of file.
  1474. X */
  1475. X{
  1476. X    register int c;
  1477. X    declarecache;
  1478. X    register FILE *fout;
  1479. X    register RILE *fin;
  1480. X
  1481. X    c = nextc;
  1482. X    fin = finptr;
  1483. X    fout = foutptr;
  1484. X    setupcache(fin); cache(fin);
  1485. X
  1486. X    for (;;) {
  1487. X        switch (ctab[c]) {
  1488. X            default:
  1489. X                nextc = c;
  1490. X                uncache(fin);
  1491. X                return false;
  1492. X
  1493. X            case NEWLN:
  1494. X                ++rcsline;
  1495. X                /* fall into */
  1496. X            case SPACE:
  1497. X                cachegeteof(c, {uncache(fin);return true;});
  1498. X                break;
  1499. X        }
  1500. X        if (fout)
  1501. X            aputc(c, fout);
  1502. X    }
  1503. X}
  1504. X
  1505. X
  1506. int getlex(token)
  1507. enum tokens token;
  1508. X/* Function: Checks if nexttok is the same as token. If so,
  1509. X * advances the input by calling nextlex and returns true.
  1510. X * otherwise returns false.
  1511. X * Doesn't work for strings and keywords; loses the character string for ids.
  1512. X */
  1513. X{
  1514. X        if (nexttok==token) {
  1515. X                nextlex();
  1516. X                return(true);
  1517. X        } else  return(false);
  1518. X}
  1519. X
  1520. X    int
  1521. getkeyopt(key)
  1522. X    char const *key;
  1523. X/* Function: If the current token is a keyword identical to key,
  1524. X * advances the input by calling nextlex and returns true;
  1525. X * otherwise returns false.
  1526. X */
  1527. X{
  1528. X    if (nexttok==ID  &&  strcmp(key,NextString) == 0) {
  1529. X         /* match found */
  1530. X         ffree1(NextString);
  1531. X         nextlex();
  1532. X         return(true);
  1533. X        }
  1534. X        return(false);
  1535. X}
  1536. X
  1537. X    void
  1538. getkey(key)
  1539. X    char const *key;
  1540. X/* Check that the current input token is a keyword identical to key,
  1541. X * and advance the input by calling nextlex.
  1542. X */
  1543. X{
  1544. X    if (!getkeyopt(key))
  1545. X        fatserror("missing '%s' keyword", key);
  1546. X}
  1547. X
  1548. X    void
  1549. getkeystring(key)
  1550. X    char const *key;
  1551. X/* Check that the current input token is a keyword identical to key,
  1552. X * and advance the input by calling nextlex; then look ahead for a string.
  1553. X */
  1554. X{
  1555. X    getkey(key);
  1556. X    if (nexttok != STRING)
  1557. X        fatserror("missing string after '%s' keyword", key);
  1558. X}
  1559. X
  1560. X
  1561. X    char const *
  1562. getid()
  1563. X/* Function: Checks if nexttok is an identifier. If so,
  1564. X * advances the input by calling nextlex and returns a pointer
  1565. X * to the identifier; otherwise returns nil.
  1566. X * Treats keywords as identifiers.
  1567. X */
  1568. X{
  1569. X    register char const *name;
  1570. X        if (nexttok==ID) {
  1571. X                name = NextString;
  1572. X                nextlex();
  1573. X                return name;
  1574. X        } else  return nil;
  1575. X}
  1576. X
  1577. X
  1578. struct hshentry * getnum()
  1579. X/* Function: Checks if nexttok is a number. If so,
  1580. X * advances the input by calling nextlex and returns a pointer
  1581. X * to the hashtable entry. Otherwise returns nil.
  1582. X * Doesn't work if hshenter is false.
  1583. X */
  1584. X{
  1585. X        register struct hshentry * num;
  1586. X        if (nexttok==NUM) {
  1587. X                num=nexthsh;
  1588. X                nextlex();
  1589. X                return num;
  1590. X        } else  return nil;
  1591. X}
  1592. X
  1593. X    struct cbuf
  1594. getphrases(key)
  1595. X    char const *key;
  1596. X/* Get a series of phrases that do not start with KEY, yield resulting buffer.
  1597. X * Stop when the next phrase starts with a token that is not an identifier,
  1598. X * or is KEY.
  1599. X * Assume !foutptr.
  1600. X */
  1601. X{
  1602. X    declarecache;
  1603. X    register int c;
  1604. X    register char *p;
  1605. X    char const *limit;
  1606. X    register char const *ki, *kn;
  1607. X    struct cbuf r;
  1608. X    struct buf b;
  1609. X    register RILE *fin;
  1610. X
  1611. X    if (nexttok!=ID  ||  strcmp(NextString,key) == 0) {
  1612. X    r.string = 0;
  1613. X    r.size = 0;
  1614. X    return r;
  1615. X    } else {
  1616. X    warnignore();
  1617. X    fin = finptr;
  1618. X    setupcache(fin); cache(fin);
  1619. X    bufautobegin(&b);
  1620. X    bufscpy(&b, NextString);
  1621. X    ffree1(NextString);
  1622. X    p = b.string + strlen(b.string);
  1623. X    limit = b.string + b.size;
  1624. X    c = nextc;
  1625. X    for (;;) {
  1626. X        for (;;) {
  1627. X        if (limit <= p)
  1628. X            p = bufenlarge(&b, &limit);
  1629. X        *p++ = c;
  1630. X        switch (ctab[c]) {
  1631. X            default:
  1632. X            fatserror("unknown character `%c'", c);
  1633. X            /*NOTREACHED*/
  1634. X            case NEWLN:
  1635. X            ++rcsline;
  1636. X            /* fall into */
  1637. X            case COLON: case DIGIT: case LETTER: case Letter:
  1638. X            case PERIOD: case SPACE:
  1639. X            cacheget(c);
  1640. X            continue;
  1641. X            case SBEGIN: /* long string */
  1642. X            for (;;) {
  1643. X                for (;;) {
  1644. X                if (limit <= p)
  1645. X                    p = bufenlarge(&b, &limit);
  1646. X                cacheget(c);
  1647. X                *p++ = c;
  1648. X                switch (c) {
  1649. X                    case '\n':
  1650. X                    ++rcsline;
  1651. X                    /* fall into */
  1652. X                    default:
  1653. X                    continue;
  1654. X
  1655. X                    case SDELIM:
  1656. X                    break;
  1657. X                }
  1658. X                break;
  1659. X                }
  1660. X                cacheget(c);
  1661. X                if (c != SDELIM)
  1662. X                break;
  1663. X                if (limit <= p)
  1664. X                p = bufenlarge(&b, &limit);
  1665. X                *p++ = c;
  1666. X            }
  1667. X            continue;
  1668. X            case SEMI:
  1669. X            cacheget(c);
  1670. X            if (ctab[c] == NEWLN) {
  1671. X                ++rcsline;
  1672. X                if (limit <= p)
  1673. X                p = bufenlarge(&b, &limit);
  1674. X                *p++ = c;
  1675. X                cacheget(c);
  1676. X            }
  1677. X            for (;;) {
  1678. X                switch (ctab[c]) {
  1679. X                case NEWLN:
  1680. X                    ++rcsline;
  1681. X                    /* fall into */
  1682. X                case SPACE:
  1683. X                    cacheget(c);
  1684. X                    continue;
  1685. X
  1686. X                default: break;
  1687. X                }
  1688. X                break;
  1689. X            }
  1690. X            break;
  1691. X        }
  1692. X        break;
  1693. X        }
  1694. X        switch (ctab[c]) {
  1695. X        case LETTER:
  1696. X        case Letter:
  1697. X            for (kn = key;  c && *kn==c;  kn++)
  1698. X            cacheget(c);
  1699. X            if (!*kn)
  1700. X            switch (ctab[c]) {
  1701. X                case DIGIT: case LETTER: case Letter:
  1702. X                break;
  1703. X                default:
  1704. X                nextc = c;
  1705. X                NextString = fstr_save(key);
  1706. X                nexttok = ID;
  1707. X                uncache(fin);
  1708. X                goto returnit;
  1709. X            }
  1710. X            for (ki=key; ki<kn; ) {
  1711. X            if (limit <= p)
  1712. X                p = bufenlarge(&b, &limit);
  1713. X            *p++ = *ki++;
  1714. X            }
  1715. X            break;
  1716. X
  1717. X        default:
  1718. X            nextc = c;
  1719. X            uncache(fin);
  1720. X            nextlex();
  1721. X            goto returnit;
  1722. X        }
  1723. X    }
  1724. X    returnit:
  1725. X    return bufremember(&b, (size_t)(p - b.string));
  1726. X    }
  1727. X}
  1728. X
  1729. X
  1730. X    void
  1731. readstring()
  1732. X/* skip over characters until terminating single SDELIM        */
  1733. X/* If foutptr is set, copy every character read to foutptr.    */
  1734. X/* Does not advance nextlex at the end.                        */
  1735. X{       register c;
  1736. X    declarecache;
  1737. X    register FILE *frew;
  1738. X    register RILE *fin;
  1739. X    fin=finptr; frew=foutptr;
  1740. X    setupcache(fin); cache(fin);
  1741. X    for (;;) {
  1742. X        GETC(frew, c);
  1743. X        switch (c) {
  1744. X            case '\n':
  1745. X            ++rcsline;
  1746. X            break;
  1747. X
  1748. X            case SDELIM:
  1749. X            GETC(frew, c);
  1750. X            if (c != SDELIM) {
  1751. X                /* end of string */
  1752. X                nextc = c;
  1753. X                uncache(fin);
  1754. X                return;
  1755. X            }
  1756. X            break;
  1757. X        }
  1758. X    }
  1759. X}
  1760. X
  1761. X
  1762. X    void
  1763. printstring()
  1764. X/* Function: copy a string to stdout, until terminated with a single SDELIM.
  1765. X * Does not advance nextlex at the end.
  1766. X */
  1767. X{
  1768. X        register c;
  1769. X    declarecache;
  1770. X    register FILE *fout;
  1771. X    register RILE *fin;
  1772. X    fin=finptr;
  1773. X    fout = stdout;
  1774. X    setupcache(fin); cache(fin);
  1775. X    for (;;) {
  1776. X        cacheget(c);
  1777. X        switch (c) {
  1778. X            case '\n':
  1779. X            ++rcsline;
  1780. X            break;
  1781. X            case SDELIM:
  1782. X            cacheget(c);
  1783. X            if (c != SDELIM) {
  1784. X                                nextc=c;
  1785. X                uncache(fin);
  1786. X                                return;
  1787. X                        }
  1788. X            break;
  1789. X                }
  1790. X        aputc(c,fout);
  1791. X        }
  1792. X}
  1793. X
  1794. X
  1795. X
  1796. X    struct cbuf
  1797. savestring(target)
  1798. X    struct buf *target;
  1799. X/* Copies a string terminated with SDELIM from file finptr to buffer target.
  1800. X * Double SDELIM is replaced with SDELIM.
  1801. X * If foutptr is set, the string is also copied unchanged to foutptr.
  1802. X * Does not advance nextlex at the end.
  1803. X * Yield a copy of *TARGET, except with exact length.
  1804. X */
  1805. X{
  1806. X        register c;
  1807. X    declarecache;
  1808. X    register FILE *frew;
  1809. X    register char *tp;
  1810. X    register RILE *fin;
  1811. X    char const *limit;
  1812. X    struct cbuf r;
  1813. X
  1814. X    fin=finptr; frew=foutptr;
  1815. X    setupcache(fin); cache(fin);
  1816. X    tp = target->string;  limit = tp + target->size;
  1817. X    for (;;) {
  1818. X        GETC(frew, c);
  1819. X        switch (c) {
  1820. X            case '\n':
  1821. X            ++rcsline;
  1822. X            break;
  1823. X            case SDELIM:
  1824. X            GETC(frew, c);
  1825. X            if (c != SDELIM) {
  1826. X                                /* end of string */
  1827. X                                nextc=c;
  1828. X                r.string = target->string;
  1829. X                r.size = tp - r.string;
  1830. X                uncache(fin);
  1831. X                return r;
  1832. X                        }
  1833. X            break;
  1834. X                }
  1835. X        if (tp == limit)
  1836. X            tp = bufenlarge(target, &limit);
  1837. X        *tp++ = c;
  1838. X        }
  1839. X}
  1840. X
  1841. X
  1842. X    char *
  1843. checkid(id, delimiter)
  1844. X    register char *id;
  1845. X    int delimiter;
  1846. X/*   Function:  check whether the string starting at id is an   */
  1847. X/*        identifier and return a pointer to the delimiter*/
  1848. X/*        after the identifier.  White space, delim and 0 */
  1849. X/*              are legal delimiters.  Aborts the program if not*/
  1850. X/*              a legal identifier. Useful for checking commands*/
  1851. X/*        If !delim, the only delimiter is 0.        */
  1852. X{
  1853. X        register enum  tokens  d;
  1854. X        register char    *temp;
  1855. X        register char    c,tc;
  1856. X    register char delim = delimiter;
  1857. X
  1858. X    temp = id;
  1859. X    if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) {
  1860. X        while ((d = ctab[(unsigned char)(c = *++id)])==LETTER
  1861. X        || d==Letter || d==DIGIT || d==IDCHAR
  1862. X        )
  1863. X        ;
  1864. X        if (c  &&  (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) {
  1865. X                /* append \0 to end of id before error message */
  1866. X                tc = c;
  1867. X                while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
  1868. X                *id = '\0';
  1869. X        faterror("invalid character %c in identifier `%s'",tc,temp);
  1870. X        }
  1871. X        } else {
  1872. X            /* append \0 to end of id before error message */
  1873. X            while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
  1874. X            *id = '\0';
  1875. X        faterror("identifier `%s' doesn't start with letter", temp);
  1876. X        }
  1877. X    return id;
  1878. X}
  1879. X
  1880. X    void
  1881. checksid(id)
  1882. X    char *id;
  1883. X/* Check whether the string ID is an identifier.  */
  1884. X{
  1885. X    VOID checkid(id, 0);
  1886. X}
  1887. X
  1888. X
  1889. X    static RILE *
  1890. X#if has_mmap && large_memory
  1891. fd2_RILE(fd, filename, status)
  1892. X#else
  1893. fd2RILE(fd, filename, mode, status)
  1894. X    char const *mode;
  1895. X#endif
  1896. X    int fd;
  1897. X    char const *filename;
  1898. X    register struct stat *status;
  1899. X{
  1900. X    struct stat st;
  1901. X
  1902. X    if (!status)
  1903. X        status = &st;
  1904. X    if (fstat(fd, status) != 0)
  1905. X        efaterror(filename);
  1906. X    if (!S_ISREG(status->st_mode)) {
  1907. X        error("`%s' is not a regular file", filename);
  1908. X        VOID close(fd);
  1909. X        errno = EINVAL;
  1910. X        return 0;
  1911. X    } else {
  1912. X
  1913. X#        if ! (has_mmap && large_memory)
  1914. X        FILE *stream;
  1915. X        if (!(stream = fdopen(fd, mode)))
  1916. X            efaterror(filename);
  1917. X#        endif
  1918. X
  1919. X#        if !large_memory
  1920. X        return stream;
  1921. X#        else
  1922. X#        define RILES 3
  1923. X        {
  1924. X            static RILE rilebuf[RILES];
  1925. X
  1926. X            register RILE *f;
  1927. X            size_t s = status->st_size;
  1928. X
  1929. X            if (s != status->st_size)
  1930. X                faterror("`%s' is enormous", filename);
  1931. X            for (f = rilebuf;  f->base;  f++)
  1932. X                if (f == rilebuf+RILES)
  1933. X                    faterror("too many RILEs");
  1934. X            if (!s) {
  1935. X                static unsigned char dummy;
  1936. X                f->base = &dummy;
  1937. X            } else {
  1938. X#                if has_mmap
  1939. X                if (
  1940. X                    (f->base = (unsigned char *)mmap(
  1941. X                    (caddr_t)0, s, PROT_READ, MAP_SHARED,
  1942. X                    fd, (off_t)0
  1943. X                    )) == (unsigned char *)-1
  1944. X                )
  1945. X                    efaterror("mmap");
  1946. X#                else
  1947. X                    f->base = tnalloc(unsigned char, s);
  1948. X#                endif
  1949. X            }
  1950. X            f->ptr = f->base;
  1951. X            f->lim = f->base + s;
  1952. X#            if has_mmap
  1953. X                f->fd = fd;
  1954. X#            else
  1955. X                f->readlim = f->base;
  1956. X                f->stream = stream;
  1957. X#            endif
  1958. X            if_advise_access(s, f, MADV_SEQUENTIAL);
  1959. X            return f;
  1960. X        }
  1961. X#        endif
  1962. X    }
  1963. X}
  1964. X
  1965. X#if !has_mmap && large_memory
  1966. X    int
  1967. Igetmore(f)
  1968. X    register RILE *f;
  1969. X{
  1970. X    register fread_type r;
  1971. X    register size_t s = f->lim - f->readlim;
  1972. X
  1973. X    if (BUFSIZ < s)
  1974. X        s = BUFSIZ;
  1975. X    if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
  1976. X        testIerror(f->stream);
  1977. X        f->lim = f->readlim;  /* The file might have shrunk!  */
  1978. X        return 0;
  1979. X    }
  1980. X    f->readlim += r;
  1981. X    return 1;
  1982. X}
  1983. X#endif
  1984. X
  1985. X#if has_madvise && has_mmap && large_memory
  1986. X    void
  1987. advise_access(f, advice)
  1988. X    register RILE *f;
  1989. X    int advice;
  1990. X{
  1991. X    if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0)
  1992. X        efaterror("madvise");
  1993. X}
  1994. X#endif
  1995. X
  1996. X    RILE *
  1997. X#if has_mmap && large_memory
  1998. I_open(filename, status)
  1999. X#else
  2000. Iopen(filename, mode, status)
  2001. X    char const *mode;
  2002. X#endif
  2003. X    char const *filename;
  2004. X    struct stat *status;
  2005. X/* Open FILENAME for reading, yield its descriptor, and set *STATUS.  */
  2006. X{
  2007. X    int fd;
  2008. X
  2009. X    if ((fd = open(filename,O_RDONLY|O_BINARY)) < 0)
  2010. X        return 0;
  2011. X#    if has_mmap && large_memory
  2012. X        return fd2_RILE(fd, filename, status);
  2013. X#    else
  2014. X        return fd2RILE(fd, filename, mode, status);
  2015. X#    endif
  2016. X}
  2017. X
  2018. X
  2019. X#if !large_memory
  2020. X#    define Iclose(f) fclose(f)
  2021. X#else
  2022. X        static int
  2023. X    Iclose(f)
  2024. X        register RILE *f;
  2025. X    {
  2026. X#        if has_mmap
  2027. X        size_t s = f->lim - f->base;
  2028. X        if (s  &&  munmap((caddr_t)f->base, s) != 0)
  2029. X            return -1;
  2030. X        f->base = 0;
  2031. X        return close(f->fd);
  2032. X#        else
  2033. X        tfree(f->base);
  2034. X        f->base = 0;
  2035. X        return fclose(f->stream);
  2036. X#        endif
  2037. X    }
  2038. X#endif
  2039. X
  2040. X
  2041. static int Oerrloop;
  2042. X
  2043. X    exiting void
  2044. Oerror()
  2045. X{
  2046. X    if (Oerrloop)
  2047. X        exiterr();
  2048. X    Oerrloop = true;
  2049. X    efaterror("output error");
  2050. X}
  2051. X
  2052. exiting void Ieof() { fatserror("unexpected end of file"); }
  2053. exiting void Ierror() { efaterror("input error"); }
  2054. void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
  2055. void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
  2056. X
  2057. void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
  2058. void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
  2059. void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
  2060. void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
  2061. X
  2062. X#if !large_memory
  2063. X    void
  2064. testIeof(f)
  2065. X    FILE *f;
  2066. X{
  2067. X    testIerror(f);
  2068. X    if (feof(f))
  2069. X        Ieof();
  2070. X}
  2071. void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
  2072. X#endif
  2073. X
  2074. void eflush()
  2075. X{
  2076. X    if (fflush(stderr) != 0  &&  !Oerrloop)
  2077. X        Oerror();
  2078. X}
  2079. X
  2080. void oflush()
  2081. X{
  2082. X    if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
  2083. X        Oerror();
  2084. X}
  2085. X
  2086. X    static exiting void
  2087. fatcleanup(already_newline)
  2088. X    int already_newline;
  2089. X{
  2090. X    VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
  2091. X    exiterr();
  2092. X}
  2093. X
  2094. static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; }
  2095. static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); }
  2096. X
  2097. void eerror(s) char const *s; { enerror(errno,s); }
  2098. X
  2099. X    void
  2100. enerror(e,s)
  2101. X    int e;
  2102. X    char const *s;
  2103. X{
  2104. X    errsay();
  2105. X    errno = e;
  2106. X    perror(s);
  2107. X    eflush();
  2108. X}
  2109. X
  2110. exiting void efaterror(s) char const *s; { enfaterror(errno,s); }
  2111. X
  2112. X    exiting void
  2113. enfaterror(e,s)
  2114. X    int e;
  2115. X    char const *s;
  2116. X{
  2117. X    fatsay();
  2118. X    errno = e;
  2119. X    perror(s);
  2120. X    fatcleanup(true);
  2121. X}
  2122. X
  2123. X#if has_prototypes
  2124. X    void
  2125. error(char const *format,...)
  2126. X#else
  2127. X    /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
  2128. X#endif
  2129. X/* non-fatal error */
  2130. X{
  2131. X    va_list args;
  2132. X    errsay();
  2133. X    vararg_start(args, format);
  2134. X    fvfprintf(stderr, format, args);
  2135. X    va_end(args);
  2136. X    afputc('\n',stderr);
  2137. X    eflush();
  2138. X}
  2139. X
  2140. X#if has_prototypes
  2141. X    exiting void
  2142. fatserror(char const *format,...)
  2143. X#else
  2144. X    /*VARARGS1*/ exiting void
  2145. X    fatserror(format, va_alist) char const *format; va_dcl
  2146. X#endif
  2147. X/* fatal syntax error */
  2148. X{
  2149. X    va_list args;
  2150. X    oflush();
  2151. X    VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline);
  2152. X    vararg_start(args, format);
  2153. X    fvfprintf(stderr, format, args);
  2154. X    va_end(args);
  2155. X    fatcleanup(false);
  2156. X}
  2157. X
  2158. X#if has_prototypes
  2159. X    exiting void
  2160. faterror(char const *format,...)
  2161. X#else
  2162. X    /*VARARGS1*/ exiting void faterror(format, va_alist)
  2163. X    char const *format; va_dcl
  2164. X#endif
  2165. X/* fatal error, terminates program after cleanup */
  2166. X{
  2167. X    va_list args;
  2168. X    fatsay();
  2169. X    vararg_start(args, format);
  2170. X    fvfprintf(stderr, format, args);
  2171. X    va_end(args);
  2172. X    fatcleanup(false);
  2173. X}
  2174. X
  2175. X#if has_prototypes
  2176. X    void
  2177. warn(char const *format,...)
  2178. X#else
  2179. X    /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
  2180. X#endif
  2181. X/* prints a warning message */
  2182. X{
  2183. X    va_list args;
  2184. X    oflush();
  2185. X    aprintf(stderr,"%s warning: ",cmdid);
  2186. X    vararg_start(args, format);
  2187. X    fvfprintf(stderr, format, args);
  2188. X    va_end(args);
  2189. X    afputc('\n',stderr);
  2190. X    eflush();
  2191. X}
  2192. X
  2193. X    void
  2194. redefined(c)
  2195. X    int c;
  2196. X{
  2197. X    warn("redefinition of -%c option", c);
  2198. X}
  2199. X
  2200. X#if has_prototypes
  2201. X    void
  2202. diagnose(char const *format,...)
  2203. X#else
  2204. X    /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
  2205. X#endif
  2206. X/* prints a diagnostic message */
  2207. X/* Unlike the other routines, it does not append a newline. */
  2208. X/* This lets some callers suppress the newline, and is faster */
  2209. X/* in implementations that flush stderr just at the end of each printf. */
  2210. X{
  2211. X    va_list args;
  2212. X        if (!quietflag) {
  2213. X        oflush();
  2214. X        vararg_start(args, format);
  2215. X        fvfprintf(stderr, format, args);
  2216. X        va_end(args);
  2217. X        eflush();
  2218. X        }
  2219. X}
  2220. X
  2221. X
  2222. X
  2223. X    void
  2224. afputc(c, f)
  2225. X/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower.
  2226. X */
  2227. X    int c;
  2228. X    register FILE *f;
  2229. X{
  2230. X    aputc(c,f);
  2231. X}
  2232. X
  2233. X
  2234. X    void
  2235. aputs(s, iop)
  2236. X    char const *s;
  2237. X    FILE *iop;
  2238. X/* Function: Put string s on file iop, abort on error.
  2239. X */
  2240. X{
  2241. X#if has_fputs
  2242. X    if (fputs(s, iop) < 0)
  2243. X        Oerror();
  2244. X#else
  2245. X    awrite(s, strlen(s), iop);
  2246. X#endif
  2247. X}
  2248. X
  2249. X
  2250. X
  2251. X    void
  2252. X#if has_prototypes
  2253. fvfprintf(FILE *stream, char const *format, va_list args)
  2254. X#else
  2255. X    fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
  2256. X#endif
  2257. X/* like vfprintf, except abort program on error */
  2258. X{
  2259. X#if has_vfprintf
  2260. X    if (vfprintf(stream, format, args) < 0)
  2261. X#else
  2262. X#    if has__doprintf
  2263. X        _doprintf(stream, format, args);
  2264. X#    else
  2265. X#    if has__doprnt
  2266. X        _doprnt(format, args, stream);
  2267. X#    else
  2268. X        int *a = (int *)args;
  2269. X        VOID fprintf(stream, format,
  2270. X            a[0], a[1], a[2], a[3], a[4],
  2271. X            a[5], a[6], a[7], a[8], a[9]
  2272. X        );
  2273. X#    endif
  2274. X#    endif
  2275. X    if (ferror(stream))
  2276. X#endif
  2277. X        Oerror();
  2278. X}
  2279. X
  2280. X#if has_prototypes
  2281. X    void
  2282. aprintf(FILE *iop, char const *fmt, ...)
  2283. X#else
  2284. X    /*VARARGS2*/ void
  2285. aprintf(iop, fmt, va_alist)
  2286. XFILE *iop;
  2287. char const *fmt;
  2288. va_dcl
  2289. X#endif
  2290. X/* Function: formatted output. Same as fprintf in stdio,
  2291. X * but aborts program on error
  2292. X */
  2293. X{
  2294. X    va_list ap;
  2295. X    vararg_start(ap, fmt);
  2296. X    fvfprintf(iop, fmt, ap);
  2297. X    va_end(ap);
  2298. X}
  2299. X
  2300. X
  2301. X
  2302. X#ifdef LEXDB
  2303. X/* test program reading a stream of lexemes and printing the tokens.
  2304. X */
  2305. X
  2306. X
  2307. X
  2308. X    int
  2309. main(argc,argv)
  2310. int argc; char * argv[];
  2311. X{
  2312. X        cmdid="lextest";
  2313. X        if (argc<2) {
  2314. X        aputs("No input file\n",stderr);
  2315. X        exitmain(EXIT_FAILURE);
  2316. X        }
  2317. X    if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
  2318. X        faterror("can't open input file %s",argv[1]);
  2319. X        }
  2320. X        Lexinit();
  2321. X    while (!eoflex()) {
  2322. X        switch (nexttok) {
  2323. X
  2324. X        case ID:
  2325. X                VOID printf("ID: %s",NextString);
  2326. X                break;
  2327. X
  2328. X        case NUM:
  2329. X        if (hshenter)
  2330. X                   VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
  2331. X                else
  2332. X                   VOID printf("NUM, unentered: %s",NextString);
  2333. X                hshenter = !hshenter; /*alternate between dates and numbers*/
  2334. X                break;
  2335. X
  2336. X        case COLON:
  2337. X                VOID printf("COLON"); break;
  2338. X
  2339. X        case SEMI:
  2340. X                VOID printf("SEMI"); break;
  2341. X
  2342. X        case STRING:
  2343. X                readstring();
  2344. X                VOID printf("STRING"); break;
  2345. X
  2346. X        case UNKN:
  2347. X                VOID printf("UNKN"); break;
  2348. X
  2349. X        default:
  2350. X                VOID printf("DEFAULT"); break;
  2351. X        }
  2352. X        VOID printf(" | ");
  2353. X        nextlex();
  2354. X        }
  2355. X    exitmain(EXIT_SUCCESS);
  2356. X}
  2357. X
  2358. exiting void exiterr() { _exit(EXIT_FAILURE); }
  2359. X
  2360. X
  2361. X#endif
  2362. END_OF_FILE
  2363. if test 27526 -ne `wc -c <'src/rcslex.c'`; then
  2364.     echo shar: \"'src/rcslex.c'\" unpacked with wrong size!
  2365. fi
  2366. # end of 'src/rcslex.c'
  2367. fi
  2368. if test -f 'src/rlog.c' -a "${1}" != "-c" ; then 
  2369.   echo shar: Will not clobber existing file \"'src/rlog.c'\"
  2370. else
  2371. echo shar: Extracting \"'src/rlog.c'\" \(32185 characters\)
  2372. sed "s/^X//" >'src/rlog.c' <<'END_OF_FILE'
  2373. X/*
  2374. X *                       RLOG    operation
  2375. X */
  2376. X/*****************************************************************************
  2377. X *                       print contents of RCS files
  2378. X *****************************************************************************
  2379. X */
  2380. X
  2381. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  2382. X   Copyright 1990, 1991 by Paul Eggert
  2383. X   Distributed under license by the Free Software Foundation, Inc.
  2384. X
  2385. This file is part of RCS.
  2386. X
  2387. RCS is free software; you can redistribute it and/or modify
  2388. it under the terms of the GNU General Public License as published by
  2389. the Free Software Foundation; either version 2, or (at your option)
  2390. any later version.
  2391. X
  2392. RCS is distributed in the hope that it will be useful,
  2393. but WITHOUT ANY WARRANTY; without even the implied warranty of
  2394. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  2395. GNU General Public License for more details.
  2396. X
  2397. You should have received a copy of the GNU General Public License
  2398. along with RCS; see the file COPYING.  If not, write to
  2399. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  2400. X
  2401. Report problems and direct all questions to:
  2402. X
  2403. X    rcs-bugs@cs.purdue.edu
  2404. X
  2405. X*/
  2406. X
  2407. X
  2408. X
  2409. X
  2410. X/* $Log: rlog.c,v $
  2411. X * Revision 5.9  1991/09/17  19:07:40  eggert
  2412. X * Getscript() didn't uncache partial lines.
  2413. X *
  2414. X * Revision 5.8  1991/08/19  03:13:55  eggert
  2415. X * Revision separator is `:', not `-'.
  2416. X * Check for missing and duplicate logs.  Tune.
  2417. X * Permit log messages that do not end in newline (including empty logs).
  2418. X *
  2419. X * Revision 5.7  1991/04/21  11:58:31  eggert
  2420. X * Add -x, RCSINIT, MS-DOS support.
  2421. X *
  2422. X * Revision 5.6  1991/02/26  17:07:17  eggert
  2423. X * Survive RCS files with missing logs.
  2424. X * strsave -> str_save (DG/UX name clash)
  2425. X *
  2426. X * Revision 5.5  1990/11/01  05:03:55  eggert
  2427. X * Permit arbitrary data in logs and comment leaders.
  2428. X *
  2429. X * Revision 5.4  1990/10/04  06:30:22  eggert
  2430. X * Accumulate exit status across files.
  2431. X *
  2432. X * Revision 5.3  1990/09/11  02:41:16  eggert
  2433. X * Plug memory leak.
  2434. X *
  2435. X * Revision 5.2  1990/09/04  08:02:33  eggert
  2436. X * Count RCS lines better.
  2437. X *
  2438. X * Revision 5.0  1990/08/22  08:13:48  eggert
  2439. X * Remove compile-time limits; use malloc instead.  Add setuid support.
  2440. X * Switch to GMT.
  2441. X * Report dates in long form, to warn about dates past 1999/12/31.
  2442. X * Change "added/del" message to make room for the longer dates.
  2443. X * Don't generate trailing white space.  Add -V.  Ansify and Posixate.
  2444. X *
  2445. X * Revision 4.7  89/05/01  15:13:48  narten
  2446. X * changed copyright header to reflect current distribution rules
  2447. X * 
  2448. X * Revision 4.6  88/08/09  19:13:28  eggert
  2449. X * Check for memory exhaustion; don't access freed storage.
  2450. X * Shrink stdio code size; remove lint.
  2451. X * 
  2452. X * Revision 4.5  87/12/18  11:46:38  narten
  2453. X * more lint cleanups (Guy Harris)
  2454. X * 
  2455. X * Revision 4.4  87/10/18  10:41:12  narten
  2456. X * Updating version numbers
  2457. X * Changes relative to 1.1 actually relative to 4.2
  2458. X * 
  2459. X * Revision 1.3  87/09/24  14:01:10  narten
  2460. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  2461. X * warnings)
  2462. X * 
  2463. X * Revision 1.2  87/03/27  14:22:45  jenkins
  2464. X * Port to suns
  2465. X * 
  2466. X * Revision 4.2  83/12/05  09:18:09  wft
  2467. X * changed rewriteflag to external.
  2468. X * 
  2469. X * Revision 4.1  83/05/11  16:16:55  wft
  2470. X * Added -b, updated getnumericrev() accordingly.
  2471. X * Replaced getpwuid() with getcaller().
  2472. X * 
  2473. X * Revision 3.7  83/05/11  14:24:13  wft
  2474. X * Added options -L and -R;
  2475. X * Fixed selection bug with -l on multiple files.
  2476. X * Fixed error on dates of the form -d'>date' (rewrote getdatepair()).
  2477. X * 
  2478. X * Revision 3.6  82/12/24  15:57:53  wft
  2479. X * shortened output format.
  2480. X *
  2481. X * Revision 3.5  82/12/08  21:45:26  wft
  2482. X * removed call to checkaccesslist(); used DATEFORM to format all dates;
  2483. X * removed unused variables.
  2484. X *
  2485. X * Revision 3.4  82/12/04  13:26:25  wft
  2486. X * Replaced getdelta() with gettree(); removed updating of field lockedby.
  2487. X *
  2488. X * Revision 3.3  82/12/03  14:08:20  wft
  2489. X * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE.
  2490. X * Fixed printing of nil, removed printing of Suffix,
  2491. X * added shortcut if no revisions are printed, disambiguated struct members.
  2492. X *
  2493. X * Revision 3.2  82/10/18  21:09:06  wft
  2494. X * call to curdir replaced with getfullRCSname(),
  2495. X * fixed call to getlogin(), cosmetic changes on output,
  2496. X * changed conflicting long identifiers.
  2497. X *
  2498. X * Revision 3.1  82/10/13  16:07:56  wft
  2499. X * fixed type of variables receiving from getc() (char -> int).
  2500. X */
  2501. X
  2502. X
  2503. X
  2504. X#include "rcsbase.h"
  2505. X
  2506. struct  lockers {                     /* lockers in locker option; stored   */
  2507. X     char const        * login;      /* lockerlist                */
  2508. X     struct     lockers * lockerlink;
  2509. X     }  ;
  2510. X
  2511. struct  stateattri {                  /* states in state option; stored in  */
  2512. X     char const        * status;     /* statelist                */
  2513. X     struct  stateattri * nextstate;
  2514. X     }  ;
  2515. X
  2516. struct  authors {                     /* login names in author option;      */
  2517. X     char const        * login;      /* stored in authorlist            */
  2518. X     struct     authors * nextauthor;
  2519. X     }  ;
  2520. X
  2521. struct Revpairs{                      /* revision or branch range in -r     */
  2522. X     unsigned          numfld;     /* option; stored in revlist        */
  2523. X     char const        * strtrev;
  2524. X     char const        * endrev;
  2525. X     struct  Revpairs   * rnext;
  2526. X     } ;
  2527. X
  2528. struct Datepairs{                     /* date range in -d option; stored in */
  2529. X     char               strtdate[datesize];   /* duelst and datelist      */
  2530. X     char               enddate[datesize];
  2531. X     struct  Datepairs  * dnext;
  2532. X     };
  2533. X
  2534. static char extractdelta P((struct hshentry const*));
  2535. static int checkrevpair P((char const*,char const*));
  2536. static struct hshentry const *readdeltalog P((void));
  2537. static unsigned extdate P((struct hshentry*));
  2538. static void cleanup P((void));
  2539. static void exttree P((struct hshentry*));
  2540. static void getauthor P((char*));
  2541. static void getdatepair P((char*));
  2542. static void getlocker P((char*));
  2543. static void getnumericrev P((void));
  2544. static void getrevpairs P((char*));
  2545. static void getscript P((struct hshentry*));
  2546. static void getstate P((char*));
  2547. static void putabranch P((struct hshentry const*));
  2548. static void putadelta P((struct hshentry const*,struct hshentry const*,int));
  2549. static void putforest P((struct branchhead const*));
  2550. static void putree P((struct hshentry const*));
  2551. static void putrunk P((void));
  2552. static void recentdate P((struct hshentry const*,struct Datepairs*));
  2553. static void trunclocks P((void));
  2554. X
  2555. static char const *insDelFormat;
  2556. static int branchflag;    /*set on -b */
  2557. static int exitstatus;
  2558. static int lockflag;
  2559. static struct Datepairs *datelist, *duelst;
  2560. static struct Revpairs *revlist, *Revlst;
  2561. static struct authors *authorlist;
  2562. static struct lockers *lockerlist;
  2563. static struct stateattri *statelist;
  2564. X
  2565. X
  2566. mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $")
  2567. X{
  2568. X    static char const cmdusage[] =
  2569. X        "\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ...";
  2570. X
  2571. X    register FILE *out;
  2572. X    char *a, **newargv;
  2573. X    struct Datepairs *currdate;
  2574. X    char const *accessListString, *accessFormat, *commentFormat;
  2575. X    char const *headFormat, *symbolFormat;
  2576. X    struct access const *curaccess;
  2577. X    struct assoc const *curassoc;
  2578. X    struct hshentry const *delta;
  2579. X    struct lock const *currlock;
  2580. X    int descflag, selectflag;
  2581. X    int onlylockflag;  /* print only files with locks */
  2582. X    int onlyRCSflag;  /* print only RCS file name */
  2583. X    unsigned revno;
  2584. X
  2585. X        descflag = selectflag = true;
  2586. X    onlylockflag = onlyRCSflag = false;
  2587. X    out = stdout;
  2588. X    suffixes = X_DEFAULT;
  2589. X
  2590. X    argc = getRCSINIT(argc, argv, &newargv);
  2591. X    argv = newargv;
  2592. X    while (a = *++argv,  0<--argc && *a++=='-') {
  2593. X        switch (*a++) {
  2594. X
  2595. X        case 'L':
  2596. X            onlylockflag = true;
  2597. X            break;
  2598. X
  2599. X        case 'R':
  2600. X            onlyRCSflag =true;
  2601. X            break;
  2602. X
  2603. X                case 'l':
  2604. X                        lockflag = true;
  2605. X            getlocker(a);
  2606. X                        break;
  2607. X
  2608. X                case 'b':
  2609. X                        branchflag = true;
  2610. X                        break;
  2611. X
  2612. X                case 'r':
  2613. X            getrevpairs(a);
  2614. X                        break;
  2615. X
  2616. X                case 'd':
  2617. X            getdatepair(a);
  2618. X                        break;
  2619. X
  2620. X                case 's':
  2621. X            getstate(a);
  2622. X                        break;
  2623. X
  2624. X                case 'w':
  2625. X            getauthor(a);
  2626. X                        break;
  2627. X
  2628. X                case 'h':
  2629. X            descflag = false;
  2630. X                        break;
  2631. X
  2632. X                case 't':
  2633. X                        selectflag = false;
  2634. X                        break;
  2635. X
  2636. X        case 'q':
  2637. X            /* This has no effect; it's here for consistency.  */
  2638. X            quietflag = true;
  2639. X            break;
  2640. X
  2641. X        case 'x':
  2642. X            suffixes = a;
  2643. X            break;
  2644. X
  2645. X        case 'V':
  2646. X            setRCSversion(*argv);
  2647. X            break;
  2648. X
  2649. X                default:
  2650. X            faterror("unknown option: %s%s", *argv, cmdusage);
  2651. X
  2652. X                };
  2653. X        } /* end of option processing */
  2654. X
  2655. X    if (argc<1) faterror("no input file%s", cmdusage);
  2656. X
  2657. X    if (! (descflag|selectflag)) {
  2658. X        warn("-t overrides -h.");
  2659. X        descflag = true;
  2660. X    }
  2661. X
  2662. X    if (RCSversion < VERSION(5)) {
  2663. X        accessListString = "\naccess list:   ";
  2664. X        accessFormat = "  %s";
  2665. X        commentFormat = "\ncomment leader:  \"";
  2666. X        headFormat = "\nRCS file:        %s;   Working file:    %s\nhead:           %s%s\nbranch:         %s%s\nlocks:         ";
  2667. X        insDelFormat = "  lines added/del: %lu/%lu";
  2668. X        symbolFormat = "  %s: %s;";
  2669. X    } else {
  2670. X        accessListString = "\naccess list:";
  2671. X        accessFormat = "\n\t%s";
  2672. X        commentFormat = "\ncomment leader: \"";
  2673. X        headFormat = "\nRCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s";
  2674. X        insDelFormat = "  lines: +%lu -%lu";
  2675. X        symbolFormat = "\n\t%s: %s";
  2676. X    }
  2677. X
  2678. X        /* now handle all filenames */
  2679. X        do {
  2680. X        ffree();
  2681. X
  2682. X        if (pairfilenames(argc, argv, rcsreadopen, true, false)  <=  0)
  2683. X        continue;
  2684. X
  2685. X            /* now RCSfilename contains the name of the RCS file, and finptr
  2686. X             * the file descriptor. Workfilename contains the name of the
  2687. X             * working file.
  2688. X             */
  2689. X
  2690. X        /* Keep only those locks given by -l.  */
  2691. X        if (lockflag)
  2692. X        trunclocks();
  2693. X
  2694. X            /* do nothing if -L is given and there are no locks*/
  2695. X        if (onlylockflag && !Locks)
  2696. X        continue;
  2697. X
  2698. X        if ( onlyRCSflag ) {
  2699. X        aprintf(out, "%s\n", RCSfilename);
  2700. X        continue;
  2701. X        }
  2702. X            /*   print RCS filename , working filename and optional
  2703. X                 administrative information                         */
  2704. X            /* could use getfullRCSname() here, but that is very slow */
  2705. X        aprintf(out, headFormat, RCSfilename, workfilename,
  2706. X            Head ? " " : "",  Head ? Head->num : "",
  2707. X            Dbranch ? " " : "",  Dbranch ? Dbranch : "",
  2708. X            StrictLocks ? " strict" : ""
  2709. X        );
  2710. X            currlock = Locks;
  2711. X            while( currlock ) {
  2712. X        aprintf(out, symbolFormat, currlock->login,
  2713. X                                currlock->delta->num);
  2714. X                currlock = currlock->nextlock;
  2715. X            }
  2716. X            if (StrictLocks && RCSversion<VERSION(5))
  2717. X        aputs("  strict", out);
  2718. X
  2719. X        aputs(accessListString, out);      /*  print access list  */
  2720. X            curaccess = AccessList;
  2721. X            while(curaccess) {
  2722. X        aprintf(out, accessFormat, curaccess->login);
  2723. X                curaccess = curaccess->nextaccess;
  2724. X            }
  2725. X
  2726. X        aputs("\nsymbolic names:", out);   /*  print symbolic names   */
  2727. X        for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc)
  2728. X        aprintf(out, symbolFormat, curassoc->symbol, curassoc->num);
  2729. X        aputs(commentFormat, out);
  2730. X        awrite(Comment.string, Comment.size, out);
  2731. X        aputs("\"\n", out);
  2732. X        if (VERSION(5)<=RCSversion  ||  Expand != KEYVAL_EXPAND)
  2733. X        aprintf(out, "keyword substitution: %s\n",
  2734. X            expand_names[Expand]
  2735. X        );
  2736. X
  2737. X            gettree();
  2738. X
  2739. X        aprintf(out, "total revisions: %u", TotalDeltas);
  2740. X
  2741. X        revno = 0;
  2742. X
  2743. X        if (Head  &&  selectflag & descflag) {
  2744. X
  2745. X        getnumericrev();    /* get numeric revision or branch names */
  2746. X
  2747. X        exttree(Head);
  2748. X
  2749. X        /*  get most recently date of the dates pointed by duelst  */
  2750. X        currdate = duelst;
  2751. X        while( currdate) {
  2752. X            VOID sprintf(currdate->strtdate,DATEFORM,0,0,0,0,0,0);
  2753. X            recentdate(Head, currdate);
  2754. X            currdate = currdate->dnext;
  2755. X        }
  2756. X
  2757. X        revno = extdate(Head);
  2758. X
  2759. X        aprintf(out, ";\tselected revisions: %u", revno);
  2760. X        }
  2761. X
  2762. X        afputc('\n',out);
  2763. X        if (descflag) {
  2764. X        aputs("description:\n", out);
  2765. X        getdesc(true);
  2766. X        }
  2767. X        if (revno) {
  2768. X        while (! (delta = readdeltalog())->selector  ||  --revno)
  2769. X            ;
  2770. X        if (delta->next && countnumflds(delta->num)==2)
  2771. X            /* Read through delta->next to get its insertlns.  */
  2772. X            while (readdeltalog() != delta->next)
  2773. X            ;
  2774. X        putrunk();
  2775. X        putree(Head);
  2776. X        }
  2777. X        aputs("=============================================================================\n",out);
  2778. X    } while (cleanup(),
  2779. X         ++argv, --argc >= 1);
  2780. X    Ofclose(out);
  2781. X    exitmain(exitstatus);
  2782. X}
  2783. X
  2784. X    static void
  2785. cleanup()
  2786. X{
  2787. X    if (nerror) exitstatus = EXIT_FAILURE;
  2788. X    Izclose(&finptr);
  2789. X}
  2790. X
  2791. X#if lint
  2792. X#    define exiterr rlogExit
  2793. X#endif
  2794. X    exiting void
  2795. exiterr()
  2796. X{
  2797. X    _exit(EXIT_FAILURE);
  2798. X}
  2799. X
  2800. X
  2801. X
  2802. X    static void
  2803. putrunk()
  2804. X/*  function:  print revisions chosen, which are in trunk      */
  2805. X
  2806. X{
  2807. X    register struct hshentry const *ptr;
  2808. X
  2809. X    for (ptr = Head;  ptr;  ptr = ptr->next)
  2810. X        putadelta(ptr, ptr->next, true);
  2811. X}
  2812. X
  2813. X
  2814. X
  2815. X    static void
  2816. putree(root)
  2817. X    struct hshentry const *root;
  2818. X/*   function: print delta tree (not including trunk) in reverse
  2819. X               order on each branch                                        */
  2820. X
  2821. X{
  2822. X        if ( root == nil ) return;
  2823. X
  2824. X        putree(root->next);
  2825. X
  2826. X        putforest(root->branches);
  2827. X}
  2828. X
  2829. X
  2830. X
  2831. X
  2832. X    static void
  2833. putforest(branchroot)
  2834. X    struct branchhead const *branchroot;
  2835. X/*   function:  print branches that has the same direct ancestor    */
  2836. X{
  2837. X
  2838. X        if ( branchroot == nil ) return;
  2839. X
  2840. X        putforest(branchroot->nextbranch);
  2841. X
  2842. X        putabranch(branchroot->hsh);
  2843. X        putree(branchroot->hsh);
  2844. X}
  2845. X
  2846. X
  2847. X
  2848. X
  2849. X    static void
  2850. putabranch(root)
  2851. X    struct hshentry const *root;
  2852. X/*   function  :  print one branch     */
  2853. X
  2854. X{
  2855. X
  2856. X        if ( root == nil) return;
  2857. X
  2858. X        putabranch(root->next);
  2859. X
  2860. X        putadelta(root, root, false);
  2861. X}
  2862. X
  2863. X
  2864. X
  2865. X
  2866. X
  2867. X    static void
  2868. putadelta(node,editscript,trunk)
  2869. X    register struct hshentry const *node, *editscript;
  2870. X    int trunk;
  2871. X/*  function: Print delta node if node->selector is set.        */
  2872. X/*      editscript indicates where the editscript is stored     */
  2873. X/*      trunk indicated whether this node is in trunk           */
  2874. X{
  2875. X    static char emptych[] = EMPTYLOG;
  2876. X
  2877. X    register FILE *out;
  2878. X    char const *s;
  2879. X    size_t n;
  2880. X    struct branchhead const *newbranch;
  2881. X    struct buf branchnum;
  2882. X    char datebuf[datesize];
  2883. X
  2884. X    if (!node->selector)
  2885. X            return;
  2886. X
  2887. X    out = stdout;
  2888. X    aprintf(out,
  2889. X        "----------------------------\nrevision %s", node->num
  2890. X    );
  2891. X        if ( node->lockedby )
  2892. X       aprintf(out, "\tlocked by: %s;", node->lockedby);
  2893. X
  2894. X    aprintf(out, "\ndate: %s;  author: %s;  state: %s;",
  2895. X        date2str(node->date, datebuf),
  2896. X        node->author, node->state
  2897. X    );
  2898. X
  2899. X        if ( editscript )
  2900. X           if(trunk)
  2901. X          aprintf(out, insDelFormat,
  2902. X                             editscript->deletelns, editscript->insertlns);
  2903. X           else
  2904. X          aprintf(out, insDelFormat,
  2905. X                             editscript->insertlns, editscript->deletelns);
  2906. X
  2907. X        newbranch = node->branches;
  2908. X        if ( newbranch ) {
  2909. X       bufautobegin(&branchnum);
  2910. X       aputs("\nbranches:", out);
  2911. X           while( newbranch ) {
  2912. X        getbranchno(newbranch->hsh->num, &branchnum);
  2913. X        aprintf(out, "  %s;", branchnum.string);
  2914. X                newbranch = newbranch->nextbranch;
  2915. X           }
  2916. X       bufautoend(&branchnum);
  2917. X        }
  2918. X
  2919. X    afputc('\n', out);
  2920. X    s = node->log.string;
  2921. X    if (!(n = node->log.size)) {
  2922. X        s = emptych;
  2923. X        n = sizeof(emptych)-1;
  2924. X    }
  2925. X    awrite(s, n, out);
  2926. X    if (s[n-1] != '\n')
  2927. X        afputc('\n', out);
  2928. X}
  2929. X
  2930. X
  2931. X
  2932. X
  2933. X
  2934. X    static struct hshentry const *
  2935. readdeltalog()
  2936. X/*  Function : get the log message and skip the text of a deltatext node.
  2937. X *           Return the delta found.
  2938. X *             Assumes the current lexeme is not yet in nexttok; does not
  2939. X *             advance nexttok.
  2940. X */
  2941. X{
  2942. X        register struct  hshentry  * Delta;
  2943. X    struct buf logbuf;
  2944. X    struct cbuf cb;
  2945. X
  2946. X    if (eoflex())
  2947. X        fatserror("missing delta log");
  2948. X        nextlex();
  2949. X    if (!(Delta = getnum()))
  2950. X        fatserror("delta number corrupted");
  2951. X    getkeystring(Klog);
  2952. X    if (Delta->log.string)
  2953. X        fatserror("duplicate delta log");
  2954. X    bufautobegin(&logbuf);
  2955. X    cb = savestring(&logbuf);
  2956. X    Delta->log = bufremember(&logbuf, cb.size);
  2957. X
  2958. X        nextlex();
  2959. X    while (nexttok==ID && strcmp(NextString,Ktext)!=0)
  2960. X        ignorephrase();
  2961. X    getkeystring(Ktext);
  2962. X        Delta->insertlns = Delta->deletelns = 0;
  2963. X        if ( Delta != Head)
  2964. X                getscript(Delta);
  2965. X        else
  2966. X                readstring();
  2967. X    return Delta;
  2968. X}
  2969. X
  2970. X
  2971. X    static void
  2972. getscript(Delta)
  2973. struct    hshentry   * Delta;
  2974. X/*   function:  read edit script of Delta and count how many lines added  */
  2975. X/*              and deleted in the script                                 */
  2976. X
  2977. X{
  2978. X        int ed;   /*  editor command  */
  2979. X    declarecache;
  2980. X    register RILE *fin;
  2981. X        register  int   c;
  2982. X    register unsigned long i;
  2983. X    struct diffcmd dc;
  2984. X
  2985. X    fin = finptr;
  2986. X    setupcache(fin);
  2987. X    initdiffcmd(&dc);
  2988. X    while (0  <=  (ed = getdiffcmd(fin,true,(FILE *)0,&dc)))
  2989. X        if (!ed)
  2990. X                 Delta->deletelns += dc.nlines;
  2991. X        else {
  2992. X                 /*  skip scripted lines  */
  2993. X         i = dc.nlines;
  2994. X         Delta->insertlns += i;
  2995. X         cache(fin);
  2996. X         do {
  2997. X             for (;;) {
  2998. X            cacheget(c);
  2999. X            switch (c) {
  3000. X                default:
  3001. X                continue;
  3002. X                case SDELIM:
  3003. X                cacheget(c);
  3004. X                if (c == SDELIM)
  3005. X                    continue;
  3006. X                if (--i)
  3007. X                    fatserror("unexpected end to edit script");
  3008. X                nextc = c;
  3009. X                uncache(fin);
  3010. X                return;
  3011. X                case '\n':
  3012. X                break;
  3013. X            }
  3014. X            break;
  3015. X             }
  3016. X             ++rcsline;
  3017. X         } while (--i);
  3018. X         uncache(fin);
  3019. X            }
  3020. X}
  3021. X
  3022. X
  3023. X
  3024. X
  3025. X
  3026. X
  3027. X
  3028. X    static void
  3029. exttree(root)
  3030. struct hshentry  *root;
  3031. X/*  function: select revisions , starting with root             */
  3032. X
  3033. X{
  3034. X    struct branchhead const *newbranch;
  3035. X
  3036. X        if (root == nil) return;
  3037. X
  3038. X    root->selector = extractdelta(root);
  3039. X    root->log.string = nil;
  3040. X        exttree(root->next);
  3041. X
  3042. X        newbranch = root->branches;
  3043. X        while( newbranch ) {
  3044. X            exttree(newbranch->hsh);
  3045. X            newbranch = newbranch->nextbranch;
  3046. X        }
  3047. X}
  3048. X
  3049. X
  3050. X
  3051. X
  3052. X    static void
  3053. getlocker(argv)
  3054. char    * argv;
  3055. X/*   function : get the login names of lockers from command line   */
  3056. X/*              and store in lockerlist.                           */
  3057. X
  3058. X{
  3059. X        register char c;
  3060. X        struct   lockers   * newlocker;
  3061. X        argv--;
  3062. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  3063. X                 c == '\n' || c == ';')  ;
  3064. X        if (  c == '\0') {
  3065. X            lockerlist=nil;
  3066. X            return;
  3067. X        }
  3068. X
  3069. X        while( c != '\0' ) {
  3070. X        newlocker = talloc(struct lockers);
  3071. X            newlocker->lockerlink = lockerlist;
  3072. X            newlocker->login = argv;
  3073. X            lockerlist = newlocker;
  3074. X            while ( ( c = (*++argv)) != ',' && c != '\0' && c != ' '
  3075. X                       && c != '\t' && c != '\n' && c != ';') ;
  3076. X            *argv = '\0';
  3077. X            if ( c == '\0' ) return;
  3078. X            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  3079. X                     c == '\n' || c == ';')  ;
  3080. X        }
  3081. X}
  3082. X
  3083. X
  3084. X
  3085. X    static void
  3086. getauthor(argv)
  3087. char   *argv;
  3088. X/*   function:  get the author's name from command line   */
  3089. X/*              and store in authorlist                   */
  3090. X
  3091. X{
  3092. X        register    c;
  3093. X        struct     authors  * newauthor;
  3094. X
  3095. X        argv--;
  3096. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  3097. X                 c == '\n' || c == ';')  ;
  3098. X        if ( c == '\0' ) {
  3099. X        authorlist = talloc(struct authors);
  3100. X        authorlist->login = getusername(false);
  3101. X            authorlist->nextauthor  = nil;
  3102. X            return;
  3103. X        }
  3104. X
  3105. X        while( c != '\0' ) {
  3106. X        newauthor = talloc(struct authors);
  3107. X            newauthor->nextauthor = authorlist;
  3108. X            newauthor->login = argv;
  3109. X            authorlist = newauthor;
  3110. X            while( ( c = *++argv) != ',' && c != '\0' && c != ' '
  3111. X                     && c != '\t' && c != '\n' && c != ';') ;
  3112. X            * argv = '\0';
  3113. X            if ( c == '\0') return;
  3114. X            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  3115. X                     c == '\n' || c == ';')  ;
  3116. X        }
  3117. X}
  3118. X
  3119. X
  3120. X
  3121. X
  3122. X    static void
  3123. getstate(argv)
  3124. char   * argv;
  3125. X/*   function :  get the states of revisions from command line  */
  3126. X/*               and store in statelist                         */
  3127. X
  3128. X{
  3129. X        register  char  c;
  3130. X        struct    stateattri    *newstate;
  3131. X
  3132. X        argv--;
  3133. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  3134. X                 c == '\n' || c == ';')  ;
  3135. X        if ( c == '\0'){
  3136. X        warn("missing state attributes after -s options");
  3137. X            return;
  3138. X        }
  3139. X
  3140. X        while( c != '\0' ) {
  3141. X        newstate = talloc(struct stateattri);
  3142. X            newstate->nextstate = statelist;
  3143. X            newstate->status = argv;
  3144. X            statelist = newstate;
  3145. X            while( (c = (*++argv)) != ',' && c != '\0' && c != ' '
  3146. X                    && c != '\t' && c != '\n' && c != ';')  ;
  3147. X            *argv = '\0';
  3148. X            if ( c == '\0' ) return;
  3149. X            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  3150. X                     c == '\n' || c == ';')  ;
  3151. X        }
  3152. X}
  3153. X
  3154. X
  3155. X
  3156. X    static void
  3157. trunclocks()
  3158. X/*  Function:  Truncate the list of locks to those that are held by the  */
  3159. X/*             id's on lockerlist. Do not truncate if lockerlist empty.  */
  3160. X
  3161. X{
  3162. X    struct lockers const *plocker;
  3163. X        struct lock     * plocked,  * nextlocked;
  3164. X
  3165. X        if ( (lockerlist == nil) || (Locks == nil)) return;
  3166. X
  3167. X        /* shorten Locks to those contained in lockerlist */
  3168. X        plocked = Locks;
  3169. X        Locks = nil;
  3170. X        while( plocked != nil) {
  3171. X            plocker = lockerlist;
  3172. X            while((plocker != nil) && ( strcmp(plocker->login, plocked->login)!=0))
  3173. X                plocker = plocker->lockerlink;
  3174. X            nextlocked = plocked->nextlock;
  3175. X            if ( plocker != nil) {
  3176. X                plocked->nextlock = Locks;
  3177. X                Locks = plocked;
  3178. X            }
  3179. X            plocked = nextlocked;
  3180. X        }
  3181. X}
  3182. X
  3183. X
  3184. X
  3185. X    static void
  3186. recentdate(root, pd)
  3187. X    struct hshentry const *root;
  3188. X    struct Datepairs *pd;
  3189. X/*  function:  Finds the delta that is closest to the cutoff date given by   */
  3190. X/*             pd among the revisions selected by exttree.                   */
  3191. X/*             Successively narrows down the interval given by pd,           */
  3192. X/*             and sets the strtdate of pd to the date of the selected delta */
  3193. X{
  3194. X    struct branchhead const *newbranch;
  3195. X
  3196. X    if ( root == nil) return;
  3197. X    if (root->selector) {
  3198. X             if ( cmpnum(root->date, pd->strtdate) >= 0 &&
  3199. X                  cmpnum(root->date, pd->enddate) <= 0)
  3200. X        VOID strcpy(pd->strtdate, root->date);
  3201. X        }
  3202. X
  3203. X        recentdate(root->next, pd);
  3204. X        newbranch = root->branches;
  3205. X        while( newbranch) {
  3206. X           recentdate(newbranch->hsh, pd);
  3207. X           newbranch = newbranch->nextbranch;
  3208. X    }
  3209. X}
  3210. X
  3211. X
  3212. X
  3213. X
  3214. X
  3215. X
  3216. X    static unsigned
  3217. extdate(root)
  3218. struct  hshentry        * root;
  3219. X/*  function:  select revisions which are in the date range specified     */
  3220. X/*             in duelst  and datelist, start at root                     */
  3221. X/* Yield number of revisions selected, including those already selected.  */
  3222. X{
  3223. X    struct branchhead const *newbranch;
  3224. X    struct Datepairs const *pdate;
  3225. X    unsigned revno;
  3226. X
  3227. X    if (!root)
  3228. X        return 0;
  3229. X
  3230. X        if ( datelist || duelst) {
  3231. X            pdate = datelist;
  3232. X            while( pdate ) {
  3233. X                if ( (pdate->strtdate)[0] == '\0' || cmpnum(root->date,pdate->strtdate) >= 0){
  3234. X                   if ((pdate->enddate)[0] == '\0' || cmpnum(pdate->enddate,root->date) >= 0)
  3235. X                        break;
  3236. X                }
  3237. X                pdate = pdate->dnext;
  3238. X            }
  3239. X            if ( pdate == nil) {
  3240. X                pdate = duelst;
  3241. X        for (;;) {
  3242. X           if (!pdate) {
  3243. X            root->selector = false;
  3244. X            break;
  3245. X           }
  3246. X                   if ( cmpnum(root->date, pdate->strtdate) == 0)
  3247. X                      break;
  3248. X                   pdate = pdate->dnext;
  3249. X                }
  3250. X            }
  3251. X        }
  3252. X    revno = root->selector + extdate(root->next);
  3253. X
  3254. X        newbranch = root->branches;
  3255. X        while( newbranch ) {
  3256. X       revno += extdate(newbranch->hsh);
  3257. X           newbranch = newbranch->nextbranch;
  3258. X        }
  3259. X    return revno;
  3260. X}
  3261. X
  3262. X
  3263. X
  3264. X    static char
  3265. extractdelta(pdelta)
  3266. X    struct hshentry const *pdelta;
  3267. X/*  function:  compare information of pdelta to the authorlist, lockerlist,*/
  3268. X/*             statelist, revlist and yield true if pdelta is selected.    */
  3269. X
  3270. X{
  3271. X    struct lock const *plock;
  3272. X    struct stateattri const *pstate;
  3273. X    struct authors const *pauthor;
  3274. X    struct Revpairs const *prevision;
  3275. X    unsigned length;
  3276. X
  3277. X    if ((pauthor = authorlist)) /* only certain authors wanted */
  3278. X        while (strcmp(pauthor->login, pdelta->author) != 0)
  3279. X        if (!(pauthor = pauthor->nextauthor))
  3280. X            return false;
  3281. X    if ((pstate = statelist)) /* only certain states wanted */
  3282. X        while (strcmp(pstate->status, pdelta->state) != 0)
  3283. X        if (!(pstate = pstate->nextstate))
  3284. X            return false;
  3285. X    if (lockflag) /* only locked revisions wanted */
  3286. X        for (plock = Locks;  ;  plock = plock->nextlock)
  3287. X        if (!plock)
  3288. X            return false;
  3289. X        else if (plock->delta == pdelta)
  3290. X            break;
  3291. X    if ((prevision = Revlst)) /* only certain revs or branches wanted */
  3292. X        for (;;) {
  3293. X                length = prevision->numfld;
  3294. X        if (
  3295. X            countnumflds(pdelta->num) == length+(length&1) &&
  3296. X            0 <= compartial(pdelta->num, prevision->strtrev, length) &&
  3297. X            0 <= compartial(prevision->endrev, pdelta->num, length)
  3298. X        )
  3299. X             break;
  3300. X        if (!(prevision = prevision->rnext))
  3301. X            return false;
  3302. X            }
  3303. X    return true;
  3304. X}
  3305. X
  3306. X
  3307. X
  3308. X    static void
  3309. getdatepair(argv)
  3310. X   char   * argv;
  3311. X/*  function:  get time range from command line and store in datelist if    */
  3312. X/*             a time range specified or in duelst if a time spot specified */
  3313. X
  3314. X{
  3315. X        register   char         c;
  3316. X        struct     Datepairs    * nextdate;
  3317. X    char const        * rawdate;
  3318. X    int                     switchflag;
  3319. X
  3320. X        argv--;
  3321. X        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
  3322. X                 c == '\n' || c == ';')  ;
  3323. X        if ( c == '\0' ) {
  3324. X        warn("missing date/time after -d");
  3325. X            return;
  3326. X        }
  3327. X
  3328. X        while( c != '\0' )  {
  3329. X        switchflag = false;
  3330. X        nextdate = talloc(struct Datepairs);
  3331. X            if ( c == '<' ) {   /*   case: -d <date   */
  3332. X                c = *++argv;
  3333. X                (nextdate->strtdate)[0] = '\0';
  3334. X        } else if (c == '>') { /* case: -d'>date' */
  3335. X        c = *++argv;
  3336. X        (nextdate->enddate)[0] = '\0';
  3337. X        switchflag = true;
  3338. X        } else {
  3339. X                rawdate = argv;
  3340. X        while( c != '<' && c != '>' && c != ';' && c != '\0')
  3341. X             c = *++argv;
  3342. X                *argv = '\0';
  3343. X        if ( c == '>' ) switchflag=true;
  3344. X        str2date(rawdate,
  3345. X             switchflag ? nextdate->enddate : nextdate->strtdate);
  3346. X        if ( c == ';' || c == '\0') {  /*  case: -d date  */
  3347. X            VOID strcpy(nextdate->enddate,nextdate->strtdate);
  3348. X                    nextdate->dnext = duelst;
  3349. X                    duelst = nextdate;
  3350. X            goto end;
  3351. X        } else {
  3352. X            /*   case:   -d date<  or -d  date>; see switchflag */
  3353. X            while ( (c= *++argv) == ' ' || c=='\t' || c=='\n');
  3354. X            if ( c == ';' || c == '\0') {
  3355. X            /* second date missing */
  3356. X            if (switchflag)
  3357. X                *nextdate->strtdate= '\0';
  3358. X            else
  3359. X                *nextdate->enddate= '\0';
  3360. X            nextdate->dnext = datelist;
  3361. X            datelist = nextdate;
  3362. X            goto end;
  3363. X            }
  3364. X                }
  3365. X            }
  3366. X            rawdate = argv;
  3367. X        while( c != '>' && c != '<' && c != ';' && c != '\0')
  3368. X         c = *++argv;
  3369. X            *argv = '\0';
  3370. X        str2date(rawdate,
  3371. X             switchflag ? nextdate->strtdate : nextdate->enddate);
  3372. X            nextdate->dnext = datelist;
  3373. X        datelist = nextdate;
  3374. X     end:
  3375. X        if ( c == '\0')  return;
  3376. X            while( (c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n');
  3377. X        }
  3378. X}
  3379. X
  3380. X
  3381. X
  3382. X    static void
  3383. getnumericrev()
  3384. X/*  function:  get the numeric name of revisions which stored in revlist  */
  3385. X/*             and then stored the numeric names in Revlst                */
  3386. X/*             if branchflag, also add default branch                     */
  3387. X
  3388. X{
  3389. X        struct  Revpairs        * ptr, *pt;
  3390. X    unsigned n;
  3391. X    struct buf s, e;
  3392. X    char const *lrev;
  3393. X    struct buf const *rstart, *rend;
  3394. X
  3395. X        Revlst = nil;
  3396. X        ptr = revlist;
  3397. X    bufautobegin(&s);
  3398. X    bufautobegin(&e);
  3399. X        while( ptr ) {
  3400. X        n = 0;
  3401. X        rstart = &s;
  3402. X        rend = &e;
  3403. X
  3404. X        switch (ptr->numfld) {
  3405. X
  3406. X          case 1: /* -r rev */
  3407. X        if (expandsym(ptr->strtrev, &s)) {
  3408. X            rend = &s;
  3409. X            n = countnumflds(s.string);
  3410. X            if (!n  &&  (lrev = tiprev())) {
  3411. X            bufscpy(&s, lrev);
  3412. X            n = countnumflds(lrev);
  3413. X            }
  3414. X                }
  3415. X        break;
  3416. X
  3417. X          case 2: /* -r rev- */
  3418. X        if (expandsym(ptr->strtrev, &s)) {
  3419. X            bufscpy(&e, s.string);
  3420. X            n = countnumflds(s.string);
  3421. X            (n<2 ? e.string : strrchr(e.string,'.'))[0]  =  0;
  3422. X                }
  3423. X        break;
  3424. X
  3425. X          case 3: /* -r -rev */
  3426. X        if (expandsym(ptr->endrev, &e)) {
  3427. X            if ((n = countnumflds(e.string)) < 2)
  3428. X            bufscpy(&s, ".1");
  3429. X            else {
  3430. X            bufscpy(&s, e.string);
  3431. X            VOID strcpy(strrchr(s.string,'.'), ".1");
  3432. X            }
  3433. X                }
  3434. X        break;
  3435. X
  3436. X          default: /* -r rev1-rev2 */
  3437. X        if (
  3438. X            expandsym(ptr->strtrev, &s)
  3439. X            &&    expandsym(ptr->endrev, &e)
  3440. X            &&    checkrevpair(s.string, e.string)
  3441. X        ) {
  3442. X            n = countnumflds(s.string);
  3443. X            /* Swap if out of order.  */
  3444. X            if (compartial(s.string,e.string,n) > 0) {
  3445. X            rstart = &e;
  3446. X            rend = &s;
  3447. X            }
  3448. X        }
  3449. X        break;
  3450. X        }
  3451. X
  3452. X        if (n) {
  3453. X        pt = ftalloc(struct Revpairs);
  3454. X        pt->numfld = n;
  3455. X        pt->strtrev = fstr_save(rstart->string);
  3456. X        pt->endrev = fstr_save(rend->string);
  3457. X                pt->rnext = Revlst;
  3458. X                Revlst = pt;
  3459. X        }
  3460. X        ptr = ptr->rnext;
  3461. X        }
  3462. X        /* Now take care of branchflag */
  3463. X    if (branchflag && (Dbranch||Head)) {
  3464. X        pt = ftalloc(struct Revpairs);
  3465. X        pt->strtrev = pt->endrev =
  3466. X        Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1));
  3467. X        pt->rnext=Revlst; Revlst=pt;
  3468. X        pt->numfld = countnumflds(pt->strtrev);
  3469. X        }
  3470. X    bufautoend(&s);
  3471. X    bufautoend(&e);
  3472. X}
  3473. X
  3474. X
  3475. X
  3476. X    static int
  3477. checkrevpair(num1,num2)
  3478. X    char const *num1, *num2;
  3479. X/*  function:  check whether num1, num2 are legal pair,i.e.
  3480. X    only the last field are different and have same number of
  3481. X    fields( if length <= 2, may be different if first field)   */
  3482. X
  3483. X{
  3484. X    unsigned length = countnumflds(num1);
  3485. X
  3486. X    if (
  3487. X            countnumflds(num2) != length
  3488. X        ||    2 < length  &&  compartial(num1, num2, length-1) != 0
  3489. X    ) {
  3490. X        error("invalid branch or revision pair %s : %s", num1, num2);
  3491. X            return false;
  3492. X        }
  3493. X
  3494. X        return true;
  3495. X}
  3496. X
  3497. X
  3498. X
  3499. X    static void
  3500. getrevpairs(argv)
  3501. register     char    * argv;
  3502. X/*  function:  get revision or branch range from command line, and   */
  3503. X/*             store in revlist                                      */
  3504. X
  3505. X{
  3506. X        register    char    c;
  3507. X        struct      Revpairs  * nextrevpair;
  3508. X    int separator;
  3509. X
  3510. X    c = *argv;
  3511. X
  3512. X    /* Support old ambiguous '-' syntax; this will go away.  */
  3513. X    if (strchr(argv,':'))
  3514. X        separator = ':';
  3515. X    else {
  3516. X        if (strchr(argv,'-')  &&  VERSION(5) <= RCSversion)
  3517. X        warn("`-' is obsolete in `-r%s'; use `:' instead", argv);
  3518. X        separator = '-';
  3519. X    }
  3520. X
  3521. X    for (;;) {
  3522. X        while (c==' ' || c=='\t' || c=='\n')
  3523. X        c = *++argv;
  3524. X        nextrevpair = talloc(struct Revpairs);
  3525. X            nextrevpair->rnext = revlist;
  3526. X            revlist = nextrevpair;
  3527. X        nextrevpair->numfld = 1;
  3528. X        nextrevpair->strtrev = argv;
  3529. X        for (;;  c = *++argv) {
  3530. X        switch (c) {
  3531. X            default:
  3532. X            continue;
  3533. X            case '\0': case ' ': case '\t': case '\n':
  3534. X            case ',': case ';':
  3535. X            break;
  3536. X            case ':': case '-':
  3537. X            if (c == separator)
  3538. X                break;
  3539. X            continue;
  3540. X        }
  3541. X        break;
  3542. X        }
  3543. X        *argv = '\0';
  3544. X        while (c==' ' || c=='\t' || c=='\n')
  3545. X        c = *++argv;
  3546. X        if (c == separator) {
  3547. X                while( (c =(*++argv)) == ' ' || c == '\t' || c =='\n') ;
  3548. X        nextrevpair->endrev = argv;
  3549. X        for (;;  c = *++argv) {
  3550. X            switch (c) {
  3551. X            default:
  3552. X                continue;
  3553. X            case '\0': case ' ': case '\t': case '\n':
  3554. X            case ',': case ';':
  3555. X                break;
  3556. X            case ':': case '-':
  3557. X                if (c == separator)
  3558. X                continue;
  3559. X                break;
  3560. X            }
  3561. X            break;
  3562. X        }
  3563. X        *argv = '\0';
  3564. X        while (c==' ' || c=='\t' || c =='\n')
  3565. X            c = *++argv;
  3566. X        nextrevpair->numfld =
  3567. X            !nextrevpair->endrev[0] ? 2 /* -rrev- */ :
  3568. X            !nextrevpair->strtrev[0] ? 3 /* -r-rev */ :
  3569. X            4 /* -rrev1-rev2 */;
  3570. X            }
  3571. X        if (!c)
  3572. X        break;
  3573. X        if (c!=',' && c!=';')
  3574. X        error("missing `,' near `%c%s'", c, argv+1);
  3575. X    }
  3576. X}
  3577. END_OF_FILE
  3578. if test 32185 -ne `wc -c <'src/rlog.c'`; then
  3579.     echo shar: \"'src/rlog.c'\" unpacked with wrong size!
  3580. fi
  3581. # end of 'src/rlog.c'
  3582. fi
  3583. echo shar: End of archive 6 \(of 11\).
  3584. cp /dev/null ark6isdone
  3585. MISSING=""
  3586. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  3587.     if test ! -f ark${I}isdone ; then
  3588.     MISSING="${MISSING} ${I}"
  3589.     fi
  3590. done
  3591. if test "${MISSING}" = "" ; then
  3592.     echo You have unpacked all 11 archives.
  3593.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  3594. else
  3595.     echo You still need to unpack the following archives:
  3596.     echo "        " ${MISSING}
  3597. fi
  3598. ##  End of shell archive.
  3599. exit 0
  3600.