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

  1. Subject:  v24i005:  RCS source control system, Part05/12
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: acf53e5d 10754ffb 88973713 807e4109
  5.  
  6. Submitted-by: Adam Hammer <hammer@cs.purdue.edu>
  7. Posting-number: Volume 24, Issue 5
  8. Archive-name: rcs/part05
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # Contents:  src/rcsfnms.c src/rcslex.c
  17. # Wrapped by rsalz@litchi.bbn.com on Thu Feb 21 14:36:58 1991
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. echo If this archive is complete, you will see the following message:
  20. echo '          "shar: End of archive 5 (of 12)."'
  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'\" \(26159 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 by Paul Eggert
  38. X   Distributed under license by the Free Software Foundation, Inc.
  39. X
  40. XThis file is part of RCS.
  41. X
  42. XRCS is free software; you can redistribute it and/or modify
  43. Xit under the terms of the GNU General Public License as published by
  44. Xthe Free Software Foundation; either version 1, or (at your option)
  45. Xany later version.
  46. X
  47. XRCS is distributed in the hope that it will be useful,
  48. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  49. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  50. XGNU General Public License for more details.
  51. X
  52. XYou should have received a copy of the GNU General Public License
  53. Xalong with RCS; see the file COPYING.  If not, write to
  54. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  55. X
  56. XReport 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.4  1990/11/01  05:03:43  eggert
  67. X * Permit arbitrary data in comment leaders.
  68. X *
  69. X * Revision 5.3  1990/09/14  22:56:16  hammer
  70. X * added more filename extensions and their comment leaders
  71. X *
  72. X * Revision 5.2  1990/09/04  08:02:23  eggert
  73. X * Fix typo when !RCSSEP.
  74. X *
  75. X * Revision 5.1  1990/08/29  07:13:59  eggert
  76. X * Work around buggy compilers with defective argument promotion.
  77. X *
  78. X * Revision 5.0  1990/08/22  08:12:50  eggert
  79. X * Ignore signals when manipulating the semaphore file.
  80. X * Modernize list of file name extensions.
  81. X * Permit paths of arbitrary length.  Beware file names beginning with "-".
  82. X * Remove compile-time limits; use malloc instead.
  83. X * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
  84. X * Ansify and Posixate.
  85. X * Don't use access().  Fix test for non-regular files.  Tune.
  86. X *
  87. X * Revision 4.8  89/05/01  15:09:41  narten
  88. X * changed getwd to not stat empty directories.
  89. X * 
  90. X * Revision 4.7  88/08/09  19:12:53  eggert
  91. X * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
  92. X * 
  93. X * Revision 4.6  87/12/18  11:40:23  narten
  94. X * additional file types added from 4.3 BSD version, and SPARC assembler
  95. X * comment character added. Also, more lint cleanups. (Guy Harris)
  96. X * 
  97. X * Revision 4.5  87/10/18  10:34:16  narten
  98. X * Updating version numbers. Changes relative to 1.1 actually relative
  99. X * to verion 4.3
  100. X * 
  101. X * Revision 1.3  87/03/27  14:22:21  jenkins
  102. X * Port to suns
  103. X * 
  104. X * Revision 1.2  85/06/26  07:34:28  svb
  105. X * Comment leader '% ' for '*.tex' files added.
  106. X * 
  107. X * Revision 4.3  83/12/15  12:26:48  wft
  108. X * Added check for KDELIM in file names to pairfilenames().
  109. X * 
  110. X * Revision 4.2  83/12/02  22:47:45  wft
  111. X * Added csh, red, and sl file name suffixes.
  112. X * 
  113. X * Revision 4.1  83/05/11  16:23:39  wft
  114. X * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
  115. X * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
  116. X * 2. added getting the file status of RCS and working files;
  117. X * 3. added ignoring of directories.
  118. X * 
  119. X * Revision 3.7  83/05/11  15:01:58  wft
  120. X * Added comtable[] which pairs file name suffixes with comment leaders;
  121. X * updated InitAdmin() accordingly.
  122. X * 
  123. X * Revision 3.6  83/04/05  14:47:36  wft
  124. X * fixed Suffix in InitAdmin().
  125. X * 
  126. X * Revision 3.5  83/01/17  18:01:04  wft
  127. X * Added getwd() and rename(); these can be removed by defining
  128. X * V4_2BSD, since they are not needed in 4.2 bsd.
  129. X * Changed sys/param.h to sys/types.h.
  130. X *
  131. X * Revision 3.4  82/12/08  21:55:20  wft
  132. X * removed unused variable.
  133. X *
  134. X * Revision 3.3  82/11/28  20:31:37  wft
  135. X * Changed mktempfile() to store the generated file names.
  136. X * Changed getfullRCSname() to store the file and pathname, and to
  137. X * delete leading "../" and "./".
  138. X *
  139. X * Revision 3.2  82/11/12  14:29:40  wft
  140. X * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
  141. X * checksuffix(), checkfullpath(). Semaphore name generation updated.
  142. X * mktempfile() now checks for nil path; freefilename initialized properly.
  143. X * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
  144. X * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
  145. X *
  146. X * Revision 3.1  82/10/18  14:51:28  wft
  147. X * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
  148. X * renamed checkpath() to checkfullpath().
  149. X */
  150. X
  151. X
  152. X#include "rcsbase.h"
  153. X
  154. XlibId(fnmsId, "$Id: rcsfnms.c,v 5.4 1990/11/01 05:03:43 eggert Exp $")
  155. X
  156. Xconst char *RCSfilename;
  157. Xchar *workfilename;
  158. Xstruct stat RCSstat, workstat; /* file status for RCS file and working file */
  159. Xint haveworkstat;
  160. X
  161. Xstatic const char rcsdir[] = RCSDIR;
  162. X
  163. X
  164. X#define TEMPNAMES 4 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
  165. Xstatic char tfnames[TEMPNAMES][L_tmpnam];    /* unlink these when done */
  166. Xstatic volatile int tfmade[TEMPNAMES];        /* if these flags are set */
  167. X
  168. X
  169. Xstruct compair {
  170. X    const char *suffix, *comlead;
  171. X};
  172. X
  173. Xstatic const struct compair comtable[] = {
  174. X/* comtable pairs each filename suffix with a comment leader. The comment   */
  175. X/* leader is placed before each line generated by the $Log keyword. This    */
  176. X/* table is used to guess the proper comment leader from the working file's */
  177. X/* suffix during initial ci (see InitAdmin()). Comment leaders are needed   */
  178. X/* for languages without multiline comments; for others they are optional.  */
  179. X    "a",   "-- ",   /* Ada         */
  180. X        "c",   " * ",   /* C           */
  181. X    "C",   "// ",    /* C++ in all its infinite guises */
  182. X    "CC",  "// ",
  183. X    "c++", "// ",
  184. X    "cc",  "// ",
  185. X    "cxx", "// ",
  186. X    "cl",  ";;; ",  /* Common Lisp */
  187. X    "cmf", "C ",    /* CM FORTRAN  */
  188. X    "cs",  " * ",    /* C*          */
  189. X    "el",  "; ",    /* Emacs Lisp  */
  190. X    "f",   "c ",    /* Fortran     */
  191. X    "for", "c ",
  192. X        "h",   " * ",   /* C-header    */
  193. X        "l",   " * ",   /* lex      NOTE: conflict between lex and franzlisp */
  194. X    "lisp",";;; ",    /* Lucid Lisp  */
  195. X        "mac", "; ",    /* macro       vms or dec-20 or pdp-11 macro */
  196. X    "me",  ".\\\" ",/* me-macros   t/nroff*/
  197. X    "ml",  "; ",    /* mocklisp    */
  198. X    "mm",  ".\\\" ",/* mm-macros   t/nroff*/
  199. X    "ms",  ".\\\" ",/* ms-macros   t/nroff*/
  200. X    "p",   " * ",   /* Pascal      */
  201. X    "pl",  "% ",    /* Prolog      */
  202. X    "tex", "% ",    /* TeX           */
  203. X        "y",   " * ",   /* yacc        */
  204. X    nil,   "# "     /* default for unknown suffix; must always be last */
  205. X};
  206. X
  207. X
  208. X    void
  209. Xffclose(fptr)
  210. XFILE * fptr;
  211. X/* Function: checks ferror(fptr) and aborts the program if there were
  212. X * errors; otherwise closes fptr.
  213. X */
  214. X{       if (ferror(fptr) || fclose(fptr)==EOF)
  215. X        IOerror();
  216. X}
  217. X
  218. X
  219. X
  220. X    char *
  221. Xmaketemp(n)
  222. X    int n;
  223. X/* Create a unique filename using n and the process id and store it
  224. X * into the nth slot in tfnames.
  225. X * Because of storage in tfnames, tempunlink() can unlink the file later.
  226. X * Returns a pointer to the filename created.
  227. X */
  228. X{
  229. X    char *p = tfnames[n];
  230. X
  231. X    if (!tfmade[n]) {
  232. X#if has_tmpnam
  233. X        if (!tmpnam(p))
  234. X#else
  235. X        VOID sprintf(p, "%sRCS%cXXXXXX", tmp(), 'A'+n);
  236. X        if (!mktemp(p))
  237. X#endif
  238. X            faterror("can't make temporary file name");
  239. X    }
  240. X    tfmade[n] = true;
  241. X    return p;
  242. X}
  243. X
  244. X    void
  245. Xtempunlink()
  246. X/* Clean up maketemp() files.  May be invoked by signal handler.
  247. X */
  248. X{
  249. X    register int i;
  250. X
  251. X    for (i = TEMPNAMES;  0 <= --i;  )
  252. X        if (tfmade[i]) {
  253. X        VOID unlink(tfnames[i]);
  254. X        tfmade[i] = 0;
  255. X        }
  256. X}
  257. X
  258. X
  259. X    const char *
  260. Xbindex(sp,ch)
  261. X    register const char *sp;
  262. X    int ch;
  263. X/* Function: Finds the last occurrence of character c in string sp
  264. X * and returns a pointer to the character just beyond it. If the
  265. X * character doesn't occur in the string, sp is returned.
  266. X */
  267. X{
  268. X    register const char c=ch, *r;
  269. X        r = sp;
  270. X        while (*sp) {
  271. X                if (*sp++ == c) r=sp;
  272. X        }
  273. X        return r;
  274. X}
  275. X
  276. X
  277. X
  278. X
  279. X
  280. X    static void
  281. XInitAdmin()
  282. X/* function: initializes an admin node */
  283. X{
  284. X    register const char *Suffix;
  285. X        register int i;
  286. X
  287. X    Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil;
  288. X        StrictLocks=STRICT_LOCKING;
  289. X
  290. X        /* guess the comment leader from the suffix*/
  291. X        Suffix=bindex(workfilename, '.');
  292. X        if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/
  293. X    for (i=0; comtable[i].suffix && strcmp(Suffix,comtable[i].suffix); i++)
  294. X        ;
  295. X    Comment.string = comtable[i].comlead;
  296. X    Comment.size = strlen(comtable[i].comlead);
  297. X        Lexinit(); /* Note: if finptr==NULL, reads nothing; only initializes*/
  298. X}
  299. X
  300. X
  301. X#if !RCSSEP
  302. X    static int
  303. XisRCSfilename(f, p)
  304. X    const char *f, *p;
  305. X/* Yield true iff F (with pure file name P) is an RCS file name.  */
  306. X{
  307. X    return
  308. X        p-f <= sizeof(rcsdir)-1  &&
  309. X        ((p -= sizeof(rcsdir)-1) == f  ||  p[-1] == SLASH)  &&
  310. X        strncmp(p, rcsdir, sizeof(rcsdir)-1) == 0;
  311. X}
  312. X#endif
  313. X
  314. X#if RCSSEP
  315. X#    define findpair(c,v,f,m) findpairfile(c,v,f)
  316. X#else
  317. X#    define findpair(c,v,f,m) findpairfile(c,v,f,m)
  318. X#endif
  319. X
  320. X    static char *
  321. X#if RCSSEP
  322. Xfindpairfile(argc, argv, fname)
  323. X#else
  324. Xfindpairfile(argc, argv, fname, rcsmatch)
  325. Xint rcsmatch; /* *ARGV must be an RCS file name iff this is set.  */
  326. X#endif
  327. Xint argc; char * argv[], *fname;
  328. X/* Peek ahead in an ARGC-ARGV argument vector for a pathname ending in FNAME.
  329. X * Yield it if found, and set the corresponding pointer in ARGV to nil.
  330. X * Yield FNAME otherwise.
  331. X */
  332. X{
  333. X    register char *arg;
  334. X#if !RCSSEP
  335. X    register char *b;
  336. X#endif
  337. X    if (
  338. X        0 < argc
  339. X#if RCSSEP
  340. X        && strcmp(bindex(arg = *argv,SLASH), fname) == 0
  341. X#else
  342. X        && strcmp(b = bindex(arg = *argv,SLASH), fname) == 0
  343. X        && isRCSfilename(arg, b) == rcsmatch
  344. X#endif
  345. X    ) {
  346. X        *argv = nil;
  347. X        return arg;
  348. X        }
  349. X        return fname;
  350. X}
  351. X
  352. X
  353. X    static int
  354. Xhandleworkstat(s)
  355. X    int s;
  356. X{
  357. X    if (s==0  &&  !S_ISREG(workstat.st_mode)) {
  358. X        error("%s isn't a regular file", workfilename);
  359. X        return false;
  360. X    }
  361. X    haveworkstat = errno;
  362. X    return true;
  363. X}
  364. X
  365. Xint getworkstat()
  366. X/* Function: get status of workfilename. */
  367. X{
  368. X    errno = 0;
  369. X    return handleworkstat(stat(workfilename, &workstat));
  370. X}
  371. X
  372. X    int
  373. Xgetfworkstat(f)
  374. X    int f;
  375. X/* Get status of file descriptor f. */
  376. X{
  377. X    errno = 0;
  378. X    return handleworkstat(fstat(f, &workstat));
  379. X}
  380. X
  381. X
  382. X#if defined(_POSIX_NO_TRUNC) & _POSIX_NO_TRUNC!=-1
  383. X#    define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 0
  384. X#else
  385. X#    define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 1
  386. X#endif
  387. X
  388. X#if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED
  389. X#ifdef NAME_MAX
  390. X#    define filenametoolong(path) (NAME_MAX < strlen(bindex(path,SLASH)))
  391. X#else
  392. X    static int
  393. Xfilenametoolong(path)
  394. X    char *path;
  395. X/* Yield true if the last file name in PATH is too long. */
  396. X{
  397. X    static unsigned long dot_namemax;
  398. X
  399. X    register size_t namelen;
  400. X    register char *lastslash;
  401. X    register unsigned long namemax;
  402. X
  403. X    lastslash = strrchr(path, SLASH);
  404. X    namelen = strlen(lastslash ? lastslash+1 : path);
  405. X    if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */
  406. X        return false;
  407. X    if (lastslash) {
  408. X        *lastslash = 0;
  409. X        namemax = pathconf(path, _PC_NAME_MAX);
  410. X        *lastslash = SLASH;
  411. X    } else {
  412. X        /* Cache the results for the working directory, for speed. */
  413. X        if (!dot_namemax)
  414. X            dot_namemax = pathconf(".", _PC_NAME_MAX);
  415. X        namemax = dot_namemax;
  416. X    }
  417. X    /* If pathconf() yielded -1, namemax is now ULONG_MAX.  */
  418. X    return namemax<namelen;
  419. X}
  420. X#endif
  421. X#endif
  422. X
  423. X    void
  424. Xbufalloc(b, size)
  425. X    register struct buf *b;
  426. X    size_t size;
  427. X/* Ensure *B is a name buffer of at least SIZE bytes.
  428. X * *B's old contents can be freed; *B's new contents are undefined.
  429. X */
  430. X{
  431. X    if (b->size < size) {
  432. X        if (b->size)
  433. X            tfree(b->string);
  434. X        else
  435. X            b->size = sizeof(malloc_type);
  436. X        while (b->size < size)
  437. X            b->size <<= 1;
  438. X        b->string = tnalloc(char, b->size);
  439. X    }
  440. X}
  441. X
  442. X    void
  443. Xbufrealloc(b, size)
  444. X    register struct buf *b;
  445. X    size_t size;
  446. X/* like bufalloc, except *B's old contents, if any, are preserved */
  447. X{
  448. X    if (b->size < size) {
  449. X        if (!b->size)
  450. X            bufalloc(b, size);
  451. X        else {
  452. X            while ((b->size <<= 1)  <  size)
  453. X                ;
  454. X            b->string = (char *)testrealloc((malloc_type)b->string, b->size);
  455. X        }
  456. X    }
  457. X}
  458. X
  459. X    void
  460. Xbufautoend(b)
  461. X    struct buf *b;
  462. X/* Free an auto buffer at block exit. */
  463. X{
  464. X    if (b->size)
  465. X        tfree(b->string);
  466. X}
  467. X
  468. X    char *
  469. Xbufenlarge(b, alim)
  470. X    register struct buf *b;
  471. X    const char **alim;
  472. X/* Make *B larger.  Set *ALIM to its new limit, and yield the relocated value
  473. X * of its old limit.
  474. X */
  475. X{
  476. X    size_t s = b->size;
  477. X    bufrealloc(b, s + 1);
  478. X    *alim = b->string + b->size;
  479. X    return b->string + s;
  480. X}
  481. X
  482. X    void
  483. Xbufscat(b, s)
  484. X    struct buf *b;
  485. X    const char *s;
  486. X/* Concatenate S to B's end. */
  487. X{
  488. X    size_t blen  =  b->string ? strlen(b->string) : 0;
  489. X    bufrealloc(b, blen+strlen(s)+1);
  490. X    VOID strcpy(b->string+blen, s);
  491. X}
  492. X
  493. X    void
  494. Xbufscpy(b, s)
  495. X    struct buf *b;
  496. X    const char *s;
  497. X/* Copy S into B. */
  498. X{
  499. X    bufalloc(b, strlen(s)+1);
  500. X    VOID strcpy(b->string, s);
  501. X}
  502. X
  503. X
  504. X    FILE *
  505. Xrcsreadopen(RCSname)
  506. X    const char *RCSname;
  507. X/* Open RCSNAME for reading and yield its FILE* descriptor.
  508. X * Pass this routine to pairfilenames() for read-only access to the file.  */
  509. X{
  510. X    FILE *f;
  511. X    seteid();
  512. X    f = fopen(RCSname, "r");
  513. X    setrid();
  514. X    return f;
  515. X}
  516. X
  517. X    int
  518. Xpairfilenames(argc, argv, rcsopen, mustread, tostdout)
  519. X    int argc;
  520. X    char **argv;
  521. X    FILE *(*rcsopen)P((const char*));
  522. X    int mustread, tostdout;
  523. X/* Function: Pairs the filenames pointed to by argv; argc indicates
  524. X * how many there are.
  525. X * Places a pointer to the RCS filename into RCSfilename,
  526. X * and a pointer to the name of the working file into workfilename.
  527. X * If both the workfilename and the RCS filename are given, and tostdout
  528. X * is true, a warning is printed.
  529. X *
  530. X * If the RCS file exists, places its status into RCSstat.
  531. X *
  532. X * If the RCS file exists, it is RCSOPENed for reading, the file pointer
  533. X * is placed into finptr, and the admin-node is read in; returns 1.
  534. X * If the RCS file does not exist and mustread is set, an error is printed
  535. X * and 0 returned.
  536. X * If the RCS file does not exist and !mustread, the admin node
  537. X * is initialized and -1 returned.
  538. X *
  539. X * 0 is returned on all errors, e.g. files that are not regular files.
  540. X */
  541. X{
  542. X    static struct buf RCSbuf, tempbuf;
  543. X
  544. X    register char *p, *arg, *tempfilename, *RCS1;
  545. X    const char *purefname, *pureRCSname;
  546. X    FILE *lock1;
  547. X
  548. X    if (!(arg = *argv)) return 0; /* already paired filename */
  549. X    if (*arg == '-') {
  550. X        error("%s option is ignored after file names", arg);
  551. X        return 0;
  552. X    }
  553. X
  554. X    /* Allocate buffer temporary to hold the default paired file name. */
  555. X    for (purefname = p = arg; *p; )
  556. X        switch (*p++) {
  557. X            case SLASH:
  558. X            purefname = p;
  559. X            break;
  560. X            /* Beware characters that cause havoc with ci -k. */
  561. X            case KDELIM:
  562. X            error("RCS file name `%s' contains %c", arg, KDELIM);
  563. X            return 0;
  564. X            case ' ': case '\n': case '\t':
  565. X            error("RCS file name `%s' contains white space", arg);
  566. X            return 0;
  567. X        }
  568. X    bufalloc(&tempbuf, p - purefname + 3);
  569. X    tempfilename = tempbuf.string;
  570. X
  571. X        /* first check suffix to see whether it is an RCS file or not */
  572. X#if RCSSEP
  573. X    if (purefname<(p-=2) && p[0]==RCSSEP && p[1]==RCSSUF)
  574. X#else
  575. X    if (isRCSfilename(arg, purefname))
  576. X#endif
  577. X    {
  578. X                /* RCS file name given*/
  579. X        RCS1 = arg;
  580. X        pureRCSname = purefname;
  581. X                /* derive workfilename*/
  582. X        VOID strcpy(tempfilename, purefname);
  583. X        tempfilename[p - purefname] = 0;
  584. X                /* try to find workfile name among arguments */
  585. X        workfilename = findpair(argc-1,argv+1,tempfilename,false);
  586. X        } else {
  587. X                /* working file given; now try to find RCS file */
  588. X        workfilename = arg;
  589. X                /* derive RCS file name*/
  590. X        VOID sprintf(tempfilename,"%s%c%c", purefname, RCSSEP, RCSSUF);
  591. X                /* Try to find RCS file name among arguments*/
  592. X        RCS1 = findpair(argc-1,argv+1,tempfilename,true);
  593. X                pureRCSname=bindex(RCS1, SLASH);
  594. X        }
  595. X        /* now we have a (tentative) RCS filename in RCS1 and workfilename  */
  596. X        /* Second, try to find the right RCS file */
  597. X        if (pureRCSname!=RCS1) {
  598. X                /* a path for RCSfile is given; single RCS file to look for */
  599. X        errno = 0;
  600. X        RCSfilename = p = RCS1;
  601. X        finptr = (*rcsopen)(RCSfilename = p = RCS1);
  602. X        } else {
  603. X        /* no path for RCS file name. Prefix it with path of work */
  604. X        /* file if RCS file omitted.  Try RCSDIR subdirectory 1st.*/
  605. X        bufalloc(&RCSbuf, strlen(workfilename)+sizeof(rcsdir)+2);
  606. X        RCSfilename = p = RCSbuf.string;
  607. X        if (RCS1==tempfilename) {
  608. X            /* RCS file name not given; prepend work path */
  609. X            VOID strncpy(p, arg, purefname-arg);
  610. X            p += purefname-arg;
  611. X        }
  612. X        VOID strcpy(p, rcsdir);
  613. X        VOID strcpy(p+sizeof(rcsdir)-1, RCS1);
  614. X
  615. X        /* Try D/RCS/file,v. */
  616. X        errno = 0;
  617. X        if (!(finptr = (*rcsopen)(RCSfilename))
  618. X            &&  (errno==ENOTDIR || errno==ENOENT)
  619. X            /*
  620. X             * Many (broken) systems yield ENOENT, not ENOTDIR,
  621. X             * when the problem is a missing RCS subdirectory.
  622. X             */
  623. X        ) {
  624. X            lock1 = frewrite;
  625. X
  626. X            /* Try D/file,v. */
  627. X            VOID strcpy(p, RCS1);
  628. X            errno = 0;
  629. X            if (!(finptr=(*rcsopen)(RCSfilename)) && errno==ENOENT) {
  630. X                /*
  631. X                 * Neither file exists; determine the default.
  632. X                 * Prefer D/RCS/file,v to D/file,v.
  633. X                 */
  634. X                if (mustread || lock1) {
  635. X                /* Switch back to D/RCS/file,v. */
  636. X                VOID strcpy(p, rcsdir);
  637. X                VOID strcpy(p+sizeof(rcsdir)-1, RCS1);
  638. X                }
  639. X            }
  640. X        }
  641. X        p = RCSbuf.string;
  642. X        }
  643. X    if (finptr) {
  644. X        if (fstat(fileno(finptr), &RCSstat) < 0)
  645. X            efaterror(p);
  646. X        if (!S_ISREG(RCSstat.st_mode)) {
  647. X            error("%s isn't a regular file -- ignored", p);
  648. X                        return 0;
  649. X                }
  650. X                Lexinit(); getadmin();
  651. X    } else {
  652. X        if (errno!=ENOENT || mustread || !frewrite) {
  653. X            if (errno == EEXIST)
  654. X                error("RCS file %s is in use", p);
  655. X            else
  656. X                eerror(p);
  657. X            return 0;
  658. X        }
  659. X                InitAdmin();
  660. X        };
  661. X#    if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED
  662. X        if (filenametoolong(p)) {
  663. X        error("RCS file name %s is too long", p);
  664. X        return 0;
  665. X        }
  666. X#        ifndef NAME_MAX
  667. X        /*
  668. X         * Check workfilename, even though it is shorter,
  669. X         * because it may reside on a different filesystem.
  670. X         */
  671. X        if (filenametoolong(workfilename)) {
  672. X            error("working file name %s is too long", workfilename);
  673. X            return 0;
  674. X        }
  675. X#        endif
  676. X#    endif
  677. X
  678. X        if (tostdout&&
  679. X            !(RCS1==tempfilename||workfilename==tempfilename))
  680. X                /*The last term determines whether a pair of        */
  681. X                /* file names was given in the argument list        */
  682. X                warn("Option -p is set; ignoring output file %s",workfilename);
  683. X
  684. X    return finptr ? 1 : -1;
  685. X}
  686. X
  687. X
  688. X    const char *
  689. XgetfullRCSname()
  690. X/* Function: returns a pointer to the full path name of the RCS file.
  691. X * Gets the working directory's name at most once.
  692. X * Removes leading "../" and "./".
  693. X */
  694. X{
  695. X    static const char *wd;
  696. X    static struct buf rcsbuf, wdbuf;
  697. X    static size_t pathlength;
  698. X
  699. X    register const char *realname;
  700. X    register size_t parentdirlength;
  701. X    register unsigned dotdotcounter;
  702. X    register char *d;
  703. X
  704. X    if (ROOTPATH(RCSfilename)) {
  705. X                return(RCSfilename);
  706. X        } else {
  707. X        if (!wd) { /* Get working directory for the first time. */
  708. X            if (!(d = cgetenv("PWD"))) {
  709. X            bufalloc(&wdbuf, 1 +
  710. X#                ifdef PATH_MAX
  711. X                PATH_MAX
  712. X#                else
  713. X                _POSIX_PATH_MAX
  714. X#                endif
  715. X            );
  716. X            errno = 0;
  717. X#            if !has_getcwd
  718. X                d = getwd(wdbuf.string);
  719. X#            else
  720. X                while (
  721. X                    !(d = getcwd(wdbuf.string,(int)wdbuf.size))
  722. X                &&  errno==ERANGE
  723. X                )
  724. X                bufalloc(&wdbuf, wdbuf.size<<1);
  725. X#            endif
  726. X            if (!d)
  727. X                efaterror("working directory");
  728. X            }
  729. X            pathlength = strlen(d);
  730. X            while (pathlength && d[pathlength-1]==SLASH) {
  731. X            d[--pathlength] = 0;
  732. X                        /* Check needed because some getwd implementations */
  733. X                        /* generate "/" for the root.                      */
  734. X                    }
  735. X            wd = d;
  736. X                }
  737. X                /*the following must be redone since RCSfilename may change*/
  738. X        /* Find how many `../'s to remove from RCSfilename.  */
  739. X                dotdotcounter =0;
  740. X                realname = RCSfilename;
  741. X                while( realname[0]=='.' &&
  742. X                      (realname[1]==SLASH||(realname[1]=='.'&&realname[2]==SLASH))){
  743. X                        if (realname[1]==SLASH) {
  744. X                            /* drop leading ./ */
  745. X                            realname += 2;
  746. X                        } else {
  747. X                            /* drop leading ../ and remember */
  748. X                            dotdotcounter++;
  749. X                            realname += 3;
  750. X                        }
  751. X                }
  752. X        /* Now remove dotdotcounter trailing directories from wd. */
  753. X        parentdirlength = pathlength;
  754. X        while (dotdotcounter && parentdirlength) {
  755. X                    /* move pointer backwards over trailing directory */
  756. X            if (wd[--parentdirlength] == SLASH) {
  757. X                        dotdotcounter--;
  758. X                    }
  759. X                }
  760. X        if (dotdotcounter) {
  761. X                    error("can't generate full path name for RCS file");
  762. X                    return RCSfilename;
  763. X                } else {
  764. X                    /* build full path name */
  765. X            bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2);
  766. X            VOID strncpy(rcsbuf.string, wd, parentdirlength);
  767. X            rcsbuf.string[parentdirlength++] = SLASH;
  768. X            VOID strcpy(rcsbuf.string+parentdirlength, realname);
  769. X            return rcsbuf.string;
  770. X        }
  771. X        }
  772. X}
  773. X
  774. X    const char *
  775. Xtmp()
  776. X/* Yield the name of the tmp directory, with a trailing SLASH.  */
  777. X{
  778. X    static const char *s;
  779. X    if (!s)
  780. X        if (!(s = getenv("TMP")))
  781. X            s = TMPDIR;
  782. X        else {
  783. X            size_t l = strlen(s);
  784. X            int extra = l && s[l-1]!=SLASH;
  785. X            char *p = ftnalloc(char, l + extra + 1);
  786. X            VOID strcpy(p, s);
  787. X            if (extra) {
  788. X                p[l] = SLASH;
  789. X                p[l+1] = 0;
  790. X            }
  791. X            s = p;
  792. X        }
  793. X    return s;
  794. X}
  795. X
  796. X
  797. X#if !has_rename | bad_rename
  798. X
  799. X    int
  800. Xre_name(from, to)
  801. X    const char *from, *to;
  802. X/* Function: renames a file with the name given by from to the name given by to.
  803. X * unlinks the to-file if it already exists. returns -1 on error, 0 otherwise.
  804. X */
  805. X{       VOID unlink(to);      /* no need to check return code; will be caught by link*/
  806. X                         /* no harm done if file "to" does not exist            */
  807. X#if has_rename
  808. X    return rename(from,to);
  809. X#else
  810. X        if (link(from,to)<0) return -1;
  811. X        return(unlink(from));
  812. X#endif
  813. X}
  814. X
  815. X#endif
  816. X
  817. X
  818. X#if !has_getcwd & !has_getwd
  819. X
  820. X#if !MAKEDEPEND
  821. X#include <sys/dir.h>
  822. X#endif
  823. X
  824. X
  825. X#define dot     "."
  826. X#define dotdot  ".."
  827. X
  828. X
  829. X
  830. Xchar * getwd(name)
  831. Xchar * name;
  832. X/* Function: places full pathname of current working directory into name and
  833. X * returns name on success, NULL on failure.
  834. X * getwd is an adaptation of pwd. May not return to the current directory on
  835. X * failure.
  836. X */
  837. X{
  838. X        FILE    *file;
  839. X        struct  stat    d, dd;
  840. X        char buf[2];    /* to NUL-terminate dir.d_name */
  841. X        struct  direct  dir;
  842. X
  843. X        int rdev, rino;
  844. X        int off;
  845. X        register i,j;
  846. X
  847. X    off = 0;
  848. X    name[0] = SLASH;
  849. X        name[1] = '\0';
  850. X        buf[0] = '\0';
  851. X    if (stat(name, &d)<0) return NULL;
  852. X        rdev = d.st_dev;
  853. X        rino = d.st_ino;
  854. X        for (;;) {
  855. X                if (stat(dot, &d)<0) return NULL;
  856. X                if (d.st_ino==rino && d.st_dev==rdev) {
  857. X            if (name[off] == SLASH)
  858. X                name[off] = '\0';
  859. X            VOID chdir(name); /*change back to current directory*/
  860. X                        return name;
  861. X                }
  862. X                if ((file = fopen(dotdot,"r")) == NULL) return NULL;
  863. X                if (fstat(fileno(file), &dd)<0) goto fail;
  864. X        VOID chdir(dotdot);
  865. X                if(d.st_dev == dd.st_dev) {
  866. X                        if(d.st_ino == dd.st_ino) {
  867. X                if (name[off] == SLASH)
  868. X                name[off] = 0;
  869. X                VOID chdir(name); /*change back to current directory*/
  870. X                ffclose(file);
  871. X                            return name;
  872. X                        }
  873. X                        do {
  874. X                            if (fread((char *)&dir, sizeof(dir), 1, file) !=1)
  875. X                                goto fail;
  876. X                        } while (dir.d_ino != d.st_ino);
  877. X                }
  878. X                else do {
  879. X                        if(fread((char *)&dir, sizeof(dir), 1, file) != 1) {
  880. X                            goto fail;
  881. X                        }
  882. X                        if (dir.d_ino == 0)
  883. X                dd.st_ino = d.st_ino + 1;
  884. X                        else if (stat(dir.d_name, &dd) < 0)
  885. X                goto fail;
  886. X                } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev);
  887. X        ffclose(file);
  888. X
  889. X                /* concatenate file name */
  890. X                i = -1;
  891. X                while (dir.d_name[++i] != 0);
  892. X                for(j=off+1; j>0; --j)
  893. X                        name[j+i+1] = name[j];
  894. X                off=i+off+1;
  895. X        name[i+1] = SLASH;
  896. X                for(--i; i>=0; --i)
  897. X                        name[i+1] = dir.d_name[i];
  898. X        } /* end for */
  899. X
  900. Xfail:   ffclose(file);
  901. X        return NULL;
  902. X}
  903. X
  904. X
  905. X#endif
  906. X
  907. X
  908. X#ifdef PAIRTEST
  909. X/* test program for pairfilenames() and getfullRCSname() */
  910. X
  911. Xconst char cmdid[] = "pair";
  912. X
  913. Xmain(argc, argv)
  914. Xint argc; char *argv[];
  915. X{
  916. X        int result;
  917. X        int initflag,tostdout;
  918. X        quietflag=tostdout=initflag=false;
  919. X
  920. X        while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
  921. X                switch ((*argv)[1]) {
  922. X
  923. X                case 'p':       tostdout=true;
  924. X                                break;
  925. X                case 'i':       initflag=true;
  926. X                                break;
  927. X                case 'q':       quietflag=true;
  928. X                                break;
  929. X                default:        error("unknown option: %s", *argv);
  930. X                                break;
  931. X                }
  932. X        }
  933. X
  934. X        do {
  935. X                RCSfilename=workfilename=nil;
  936. X        result = pairfilenames(argc,argv,rcsreadopen,!initflag,tostdout);
  937. X                if (result!=0) {
  938. X            diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n",
  939. X                 RCSfilename,workfilename,getfullRCSname()
  940. X            );
  941. X                }
  942. X                switch (result) {
  943. X                        case 0: continue; /* already paired file */
  944. X
  945. X                        case 1: if (initflag) {
  946. X                                    error("RCS file %s exists already",RCSfilename);
  947. X                                } else {
  948. X                    diagnose("RCS file %s exists\n",RCSfilename);
  949. X                                }
  950. X                ffclose(finptr);
  951. X                                break;
  952. X
  953. X            case -1:diagnose("RCS file doesn't exist\n");
  954. X                                break;
  955. X                }
  956. X
  957. X        } while (++argv, --argc>=1);
  958. X
  959. X}
  960. X#endif
  961. END_OF_FILE
  962.   if test 26159 -ne `wc -c <'src/rcsfnms.c'`; then
  963.     echo shar: \"'src/rcsfnms.c'\" unpacked with wrong size!
  964.   fi
  965.   # end of 'src/rcsfnms.c'
  966. fi
  967. if test -f 'src/rcslex.c' -a "${1}" != "-c" ; then 
  968.   echo shar: Will not clobber existing file \"'src/rcslex.c'\"
  969. else
  970.   echo shar: Extracting \"'src/rcslex.c'\" \(24012 characters\)
  971.   sed "s/^X//" >'src/rcslex.c' <<'END_OF_FILE'
  972. X/*
  973. X *                     RCS file input
  974. X */
  975. X/*********************************************************************************
  976. X *                     Lexical Analysis.
  977. X *                     hashtable, Lexinit, nextlex, getlex, getkey,
  978. X *                     getid, getnum, readstring, printstring, savestring,
  979. X *                     checkid, fatserror, error, faterror, warn, diagnose
  980. X *                     Testprogram: define LEXDB
  981. X *********************************************************************************
  982. X */
  983. X
  984. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  985. X   Copyright 1990 by Paul Eggert
  986. X   Distributed under license by the Free Software Foundation, Inc.
  987. X
  988. XThis file is part of RCS.
  989. X
  990. XRCS is free software; you can redistribute it and/or modify
  991. Xit under the terms of the GNU General Public License as published by
  992. Xthe Free Software Foundation; either version 1, or (at your option)
  993. Xany later version.
  994. X
  995. XRCS is distributed in the hope that it will be useful,
  996. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  997. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  998. XGNU General Public License for more details.
  999. X
  1000. XYou should have received a copy of the GNU General Public License
  1001. Xalong with RCS; see the file COPYING.  If not, write to
  1002. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  1003. X
  1004. XReport problems and direct all questions to:
  1005. X
  1006. X    rcs-bugs@cs.purdue.edu
  1007. X
  1008. X*/
  1009. X
  1010. X
  1011. X
  1012. X/* $Log: rcslex.c,v $
  1013. X * Revision 5.5  1990/12/04  05:18:47  eggert
  1014. X * Use -I for prompts and -q for diagnostics.
  1015. X *
  1016. X * Revision 5.4  1990/11/19  20:05:28  hammer
  1017. X * no longer gives warning about unknown keywords if -q is specified
  1018. X *
  1019. X * Revision 5.3  1990/11/01  05:03:48  eggert
  1020. X * When ignoring unknown phrases, copy them to the output RCS file.
  1021. X *
  1022. X * Revision 5.2  1990/09/04  08:02:27  eggert
  1023. X * Count RCS lines better.
  1024. X *
  1025. X * Revision 5.1  1990/08/29  07:14:03  eggert
  1026. X * Work around buggy compilers with defective argument promotion.
  1027. X *
  1028. X * Revision 5.0  1990/08/22  08:12:55  eggert
  1029. X * Remove compile-time limits; use malloc instead.
  1030. X * Report errno-related errors with perror().
  1031. X * Ansify and Posixate.  Add support for ISO 8859.
  1032. X * Use better hash function.
  1033. X *
  1034. X * Revision 4.6  89/05/01  15:13:07  narten
  1035. X * changed copyright header to reflect current distribution rules
  1036. X * 
  1037. X * Revision 4.5  88/08/28  15:01:12  eggert
  1038. X * Don't loop when writing error messages to a full filesystem.
  1039. X * Flush stderr/stdout when mixing output.
  1040. X * Yield exit status compatible with diff(1).
  1041. X * Shrink stdio code size; allow cc -R; remove lint.
  1042. X * 
  1043. X * Revision 4.4  87/12/18  11:44:47  narten
  1044. X * fixed to use "varargs" in "fprintf"; this is required if it is to
  1045. X * work on a SPARC machine such as a Sun-4
  1046. X * 
  1047. X * Revision 4.3  87/10/18  10:37:18  narten
  1048. X * Updating version numbers. Changes relative to 1.1 actually relative
  1049. X * to version 4.1
  1050. X * 
  1051. X * Revision 1.3  87/09/24  14:00:17  narten
  1052. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  1053. X * warnings)
  1054. X * 
  1055. X * Revision 1.2  87/03/27  14:22:33  jenkins
  1056. X * Port to suns
  1057. X * 
  1058. X * Revision 4.1  83/03/25  18:12:51  wft
  1059. X * Only changed $Header to $Id.
  1060. X * 
  1061. X * Revision 3.3  82/12/10  16:22:37  wft
  1062. X * Improved error messages, changed exit status on error to 1.
  1063. X *
  1064. X * Revision 3.2  82/11/28  21:27:10  wft
  1065. X * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
  1066. X * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
  1067. X * properly in case there is an IO-error (e.g., file system full).
  1068. X *
  1069. X * Revision 3.1  82/10/11  19:43:56  wft
  1070. X * removed unused label out:;
  1071. X * made sure all calls to getc() return into an integer, not a char.
  1072. X */
  1073. X
  1074. X
  1075. X/*
  1076. X#define LEXDB
  1077. X*/
  1078. X/* version LEXDB is for testing the lexical analyzer. The testprogram
  1079. X * reads a stream of lexemes, enters the revision numbers into the
  1080. X * hashtable, and prints the recognized tokens. Keywords are recognized
  1081. X * as identifiers.
  1082. X */
  1083. X
  1084. X
  1085. X
  1086. X#include "rcsbase.h"
  1087. X
  1088. XlibId(lexId, "$Id: rcslex.c,v 5.5 1990/12/04 05:18:47 eggert Exp $")
  1089. X
  1090. Xstatic struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
  1091. X
  1092. Xenum tokens     nexttok;    /*next token, set by nextlex                    */
  1093. X
  1094. Xint             hshenter;   /*if true, next suitable lexeme will be entered */
  1095. X                            /*into the symbol table. Handle with care.      */
  1096. Xint             nextc;      /*next input character, initialized by Lexinit  */
  1097. X
  1098. Xunsigned long    rcsline;    /*current line-number of input            */
  1099. Xint             nerror;     /*counter for errors                            */
  1100. Xint             quietflag;  /*indicates quiet mode                          */
  1101. XFILE *          finptr;     /*input file descriptor                         */
  1102. X
  1103. XFILE *          frewrite;   /*file descriptor for echoing input             */
  1104. X
  1105. XFILE *        foutptr;        /* copy of frewrite, but NULL to suppress echo  */
  1106. X
  1107. Xstatic struct buf tokbuf;   /* token buffer                    */
  1108. X
  1109. Xconst char *    NextString; /* next token                    */
  1110. X
  1111. X/*
  1112. X * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
  1113. X * so hshsize should be odd.
  1114. X * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
  1115. X * Software--practice & experience 20, 2 (Feb 1990), 209-224.
  1116. X */
  1117. X#ifndef hshsize
  1118. X#    define hshsize 511
  1119. X#endif
  1120. X
  1121. Xstatic struct hshentry *hshtab[hshsize]; /*hashtable                */
  1122. X
  1123. Xstatic int ignored_phrases; /* have we ignored phrases in this RCS file? */
  1124. X
  1125. X    void
  1126. Xwarnignore()
  1127. X{
  1128. X    if (! (ignored_phrases|quietflag)) {
  1129. X    ignored_phrases = true;
  1130. X    warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString);
  1131. X    }
  1132. X}
  1133. X
  1134. X
  1135. X
  1136. X    static void
  1137. Xlookup(str)
  1138. X    const char *str;
  1139. X/* Function: Looks up the character string pointed to by str in the
  1140. X * hashtable. If the string is not present, a new entry for it is created.
  1141. X * In any case, the address of the corresponding hashtable entry is placed
  1142. X * into nexthsh.
  1143. X */
  1144. X{
  1145. X    register unsigned ihash;  /* index into hashtable */
  1146. X    register const char *sp;
  1147. X    register struct hshentry *n, **p;
  1148. X
  1149. X        /* calculate hash code */
  1150. X    sp = str;
  1151. X        ihash = 0;
  1152. X    while (*sp)
  1153. X        ihash  =  (ihash<<2) + *sp++;
  1154. X    ihash %= hshsize;
  1155. X
  1156. X    for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
  1157. X        if (!(n = *p)) {
  1158. X            /* empty slot found */
  1159. X            *p = n = ftalloc(struct hshentry);
  1160. X            n->num = fstrsave(str);
  1161. X            n->nexthsh = nil;
  1162. X#            ifdef LEXDB
  1163. X                VOID printf("\nEntered: %s at %u ", str, ihash);
  1164. X#            endif
  1165. X            break;
  1166. X        } else if (strcmp(str, n->num) == 0)
  1167. X            /* match found */
  1168. X            break;
  1169. X    nexthsh = n;
  1170. X    NextString = n->num;
  1171. X}
  1172. X
  1173. X
  1174. X
  1175. X
  1176. X
  1177. X
  1178. X    void
  1179. XLexinit()
  1180. X/* Function: Initialization of lexical analyzer:
  1181. X * initializes the hashtable,
  1182. X * initializes nextc, nexttok if finptr != NULL
  1183. X */
  1184. X{       register int            c;
  1185. X
  1186. X    for (c = hshsize;  0 <= --c;  ) {
  1187. X        hshtab[c] = nil;
  1188. X        }
  1189. X
  1190. X    hshenter=true; rcsline=1; nerror=0;
  1191. X    ignored_phrases = false;
  1192. X    bufrealloc(&tokbuf, 2);
  1193. X        if (finptr) {
  1194. X        GETC(finptr,foutptr,c);
  1195. X        nextc = c; /*initial character*/
  1196. X        nexttok = DELIM;  /* anything but EOFILE */
  1197. X                nextlex();            /*initial token*/
  1198. X        } else {
  1199. X                nextc = '\0';
  1200. X                nexttok=EOFILE;
  1201. X        }
  1202. X}
  1203. X
  1204. X
  1205. X    static exiting void
  1206. XunexpectedEOF()
  1207. X{
  1208. X    fatserror("unexpected EOF");
  1209. X}
  1210. X
  1211. X
  1212. X
  1213. X
  1214. X
  1215. X
  1216. X
  1217. X    void
  1218. Xnextlex()
  1219. X
  1220. X/* Function: Reads the next token and sets nexttok to the next token code.
  1221. X * Only if hshenter is set, a revision number is entered into the
  1222. X * hashtable and a pointer to it is placed into nexthsh.
  1223. X * This is useful for avoiding that dates are placed into the hashtable.
  1224. X * For ID's and NUM's, NextString is set to the character string.
  1225. X * Assumption: nextc contains the next character.
  1226. X */
  1227. X{       register c;
  1228. X    register FILE * fin, * frew;
  1229. X        register char * sp;
  1230. X    const char *lim;
  1231. X        register enum tokens d;
  1232. X
  1233. X    if (nexttok == EOFILE)
  1234. X        unexpectedEOF();
  1235. X    fin=finptr; frew=foutptr;
  1236. X
  1237. X    for (;;) switch ((nexttok=ctab[nextc])) {
  1238. X
  1239. X    default:
  1240. X        fatserror("unknown character `%c'", nextc);
  1241. X        /*NOTREACHED*/
  1242. X
  1243. X        case NEWLN:
  1244. X        ++rcsline;
  1245. X#               ifdef LEXDB
  1246. X        afputc('\n',stdout);
  1247. X#               endif
  1248. X                /* Note: falls into next case */
  1249. X
  1250. X        case SPACE:
  1251. X        GETC(fin,frew,c);
  1252. X        nextc = c;
  1253. X        continue;
  1254. X
  1255. X        case EOFILE:
  1256. X                return;
  1257. X
  1258. X        case DIGIT:
  1259. X        sp = tokbuf.string;
  1260. X        lim = sp + tokbuf.size;
  1261. X                *sp++ = nextc;
  1262. X        for (;;) {
  1263. X            GETC(fin,frew,c);
  1264. X            if ((d=ctab[c])!=DIGIT && d!=PERIOD)
  1265. X                break;
  1266. X                        *sp++ = c;         /* 1.2. and 1.2 are different */
  1267. X            if (lim <= sp)
  1268. X                sp = bufenlarge(&tokbuf, &lim);
  1269. X                }
  1270. X        *sp = 0;
  1271. X                nextc = c;
  1272. X        if (hshenter)
  1273. X            lookup(tokbuf.string);
  1274. X        else
  1275. X            NextString = fstrsave(tokbuf.string);
  1276. X                nexttok = NUM;
  1277. X                return;
  1278. X
  1279. X
  1280. X        case LETTER:
  1281. X    case Letter:
  1282. X        sp = tokbuf.string;
  1283. X        lim = sp + tokbuf.size;
  1284. X                *sp++ = nextc;
  1285. X        for (;;) {
  1286. X            GETC(fin,frew,c);
  1287. X            if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR)
  1288. X                break;
  1289. X                        *sp++ = c;
  1290. X            if (lim <= sp)
  1291. X                sp = bufenlarge(&tokbuf, &lim);
  1292. X                }
  1293. X        *sp = 0;
  1294. X                nextc = c;
  1295. X        NextString = fstrsave(tokbuf.string);
  1296. X                nexttok = ID;  /* may be ID or keyword */
  1297. X                return;
  1298. X
  1299. X        case SBEGIN: /* long string */
  1300. X                nexttok = STRING;
  1301. X                /* note: only the initial SBEGIN has been read*/
  1302. X                /* read the string, and reset nextc afterwards*/
  1303. X                return;
  1304. X
  1305. X    case COLON:
  1306. X    case SEMI:
  1307. X        GETC(fin,frew,c);
  1308. X        nextc = c;
  1309. X                return;
  1310. X        }
  1311. X}
  1312. X
  1313. X
  1314. Xint getlex(token)
  1315. Xenum tokens token;
  1316. X/* Function: Checks if nexttok is the same as token. If so,
  1317. X * advances the input by calling nextlex and returns true.
  1318. X * otherwise returns false.
  1319. X * Doesn't work for strings and keywords; loses the character string for ids.
  1320. X */
  1321. X{
  1322. X        if (nexttok==token) {
  1323. X                nextlex();
  1324. X                return(true);
  1325. X        } else  return(false);
  1326. X}
  1327. X
  1328. X    int
  1329. Xgetkeyopt(key)
  1330. X    const char *key;
  1331. X/* Function: If the current token is a keyword identical to key,
  1332. X * advances the input by calling nextlex and returns true;
  1333. X * otherwise returns false.
  1334. X */
  1335. X{
  1336. X    if (nexttok==ID  &&  strcmp(key,NextString) == 0) {
  1337. X         /* match found */
  1338. X         ffree1(NextString);
  1339. X         nextlex();
  1340. X         return(true);
  1341. X        }
  1342. X        return(false);
  1343. X}
  1344. X
  1345. X    void
  1346. Xgetkey(key)
  1347. X    const char *key;
  1348. X/* Check that the current input token is a keyword identical to key,
  1349. X * and advance the input by calling nextlex.
  1350. X */
  1351. X{
  1352. X    if (!getkeyopt(key))
  1353. X        fatserror("missing '%s' keyword", key);
  1354. X}
  1355. X
  1356. X    void
  1357. Xgetkeystring(key)
  1358. X    const char *key;
  1359. X/* Check that the current input token is a keyword identical to key,
  1360. X * and advance the input by calling nextlex; then look ahead for a string.
  1361. X */
  1362. X{
  1363. X    getkey(key);
  1364. X    if (nexttok != STRING)
  1365. X        fatserror("missing string after '%s' keyword", key);
  1366. X}
  1367. X
  1368. X
  1369. X    const char *
  1370. Xgetid()
  1371. X/* Function: Checks if nexttok is an identifier. If so,
  1372. X * advances the input by calling nextlex and returns a pointer
  1373. X * to the identifier; otherwise returns nil.
  1374. X * Treats keywords as identifiers.
  1375. X */
  1376. X{
  1377. X    register const char *name;
  1378. X        if (nexttok==ID) {
  1379. X                name = NextString;
  1380. X                nextlex();
  1381. X                return name;
  1382. X        } else  return nil;
  1383. X}
  1384. X
  1385. X
  1386. Xstruct hshentry * getnum()
  1387. X/* Function: Checks if nexttok is a number. If so,
  1388. X * advances the input by calling nextlex and returns a pointer
  1389. X * to the hashtable entry. Otherwise returns nil.
  1390. X * Doesn't work if hshenter is false.
  1391. X */
  1392. X{
  1393. X        register struct hshentry * num;
  1394. X        if (nexttok==NUM) {
  1395. X                num=nexthsh;
  1396. X                nextlex();
  1397. X                return num;
  1398. X        } else  return nil;
  1399. X}
  1400. X
  1401. X    struct cbuf
  1402. Xgetphrases(key)
  1403. X    const char *key;
  1404. X/* Get a series of phrases that do not start with KEY, yield resulting buffer.
  1405. X * Stop when the next phrase starts with a token that is not an identifier,
  1406. X * or is KEY.
  1407. X * Assume foutptr == NULL.
  1408. X */
  1409. X{
  1410. X    register FILE *fin;
  1411. X    register int c;
  1412. X    register char *p;
  1413. X    const char *lim;
  1414. X    register const char *ki, *kn;
  1415. X    struct cbuf r;
  1416. X    struct buf b;
  1417. X
  1418. X    if (nexttok!=ID  ||  strcmp(NextString,key) == 0) {
  1419. X    r.string = 0;
  1420. X    r.size = 0;
  1421. X    return r;
  1422. X    } else {
  1423. X    warnignore();
  1424. X    fin = finptr;
  1425. X    bufautobegin(&b);
  1426. X    bufscpy(&b, NextString);
  1427. X    ffree1(NextString);
  1428. X    p = b.string + strlen(b.string);
  1429. X    lim = b.string + b.size;
  1430. X    c = nextc;
  1431. X    for (;;) {
  1432. X        for (;;) {
  1433. X        if (lim <= p)
  1434. X            p = bufenlarge(&b, &lim);
  1435. X        *p++ = c;
  1436. X        switch (ctab[c]) {
  1437. X            default:
  1438. X            fatserror("unknown character `%c'", c);
  1439. X            /*NOTREACHED*/
  1440. X            case EOFILE:
  1441. X            unexpectedEOF();
  1442. X            /*NOTREACHED*/
  1443. X            case NEWLN:
  1444. X            ++rcsline;
  1445. X            /* fall into */
  1446. X            case COLON: case DIGIT: case LETTER: case Letter:
  1447. X            case PERIOD: case SPACE:
  1448. X            c = getc(fin);
  1449. X            continue;
  1450. X            case SBEGIN: /* long string */
  1451. X            for (;;) {
  1452. X                for (;;) {
  1453. X                c = getc(fin);
  1454. X                if (lim <= p)
  1455. X                    p = bufenlarge(&b, &lim);
  1456. X                *p++ = c;
  1457. X                switch (c) {
  1458. X                    case EOF:
  1459. X                    unexpectedEOF();
  1460. X                    /*NOTREACHED*/
  1461. X                    case '\n':
  1462. X                    ++rcsline;
  1463. X                    /* fall into */
  1464. X                    default:
  1465. X                    continue;
  1466. X                    case SDELIM:
  1467. X                    break;
  1468. X                }
  1469. X                break;
  1470. X                }
  1471. X                c = getc(fin);
  1472. X                if (c != SDELIM)
  1473. X                break;
  1474. X                if (lim <= p)
  1475. X                p = bufenlarge(&b, &lim);
  1476. X                *p++ = c;
  1477. X            }
  1478. X            continue;
  1479. X            case SEMI:
  1480. X            c = getc(fin);
  1481. X            if (ctab[c] == NEWLN) {
  1482. X                ++rcsline;
  1483. X                if (lim <= p)
  1484. X                p = bufenlarge(&b, &lim);
  1485. X                *p++ = c;
  1486. X                c = getc(fin);
  1487. X            }
  1488. X            for (;; c = getc(fin)) {
  1489. X                switch (ctab[c]) {
  1490. X                case NEWLN: ++rcsline; continue;
  1491. X                case SPACE: continue;
  1492. X                default: break;
  1493. X                }
  1494. X                break;
  1495. X            }
  1496. X            break;
  1497. X        }
  1498. X        break;
  1499. X        }
  1500. X        switch (ctab[c]) {
  1501. X        case LETTER:
  1502. X        case Letter:
  1503. X            for (kn = key;  c && *kn==c;  kn++)
  1504. X            if ((c = getc(fin)) == EOF)
  1505. X                unexpectedEOF();
  1506. X            if (!*kn)
  1507. X            switch (ctab[c]) {
  1508. X                case DIGIT: case LETTER: case Letter:
  1509. X                break;
  1510. X                default:
  1511. X                nextc = c;
  1512. X                NextString = fstrsave(key);
  1513. X                nexttok = ID;
  1514. X                goto returnit;
  1515. X            }
  1516. X            for (ki=key; ki<kn; ) {
  1517. X            if (lim <= p)
  1518. X                p = bufenlarge(&b, &lim);
  1519. X            *p++ = *ki++;
  1520. X            }
  1521. X            break;
  1522. X        default:
  1523. X            nextc = c;
  1524. X            nextlex();
  1525. X            goto returnit;
  1526. X        }
  1527. X    }
  1528. X
  1529. X    returnit:
  1530. X    /*
  1531. X     * Do the following instead of bufautoend(&b),
  1532. X     * because the buffer must survive until we are done with the file.
  1533. X     */
  1534. X    r.size = p - b.string;
  1535. X    r.string = (char*)fremember(testrealloc((malloc_type)b.string, r.size));
  1536. X    return r;
  1537. X    }
  1538. X}
  1539. X
  1540. X
  1541. X    void
  1542. Xreadstring()
  1543. X/* skip over characters until terminating single SDELIM        */
  1544. X/* If foutptr is set, copy every character read to foutptr.    */
  1545. X/* Does not advance nextlex at the end.                        */
  1546. X{       register c;
  1547. X    register FILE * fin,  * frew;
  1548. X    fin=finptr; frew=foutptr;
  1549. X    if (frew) {
  1550. X        /* Copy string verbatim to foutptr.  */
  1551. X                while ((c=getc(fin)) != EOF) {
  1552. X            aputc(c,frew);
  1553. X            switch (c) {
  1554. X                case '\n':
  1555. X                ++rcsline;
  1556. X                break;
  1557. X                case SDELIM:
  1558. X                if ((c=getc(fin)) == EOF) {
  1559. X                    nextc=c;
  1560. X                    return;
  1561. X                }
  1562. X                aputc(c,frew);
  1563. X                if (c != SDELIM) {
  1564. X                                        /* end of string */
  1565. X                                        nextc=c;
  1566. X                                        return;
  1567. X                                }
  1568. X                break;
  1569. X                        }
  1570. X                }
  1571. X        } else {
  1572. X                /* skip string */
  1573. X                while ((c=getc(fin)) != EOF) {
  1574. X            switch (c) {
  1575. X                case '\n':
  1576. X                ++rcsline;
  1577. X                break;
  1578. X                case SDELIM:
  1579. X                                if ((c=getc(fin)) != SDELIM) {
  1580. X                                        /* end of string */
  1581. X                                        nextc=c;
  1582. X                                        return;
  1583. X                                }
  1584. X                break;
  1585. X                        }
  1586. X                }
  1587. X        }
  1588. X    unterminatedString();
  1589. X}
  1590. X
  1591. X
  1592. X    void
  1593. Xprintstring()
  1594. X/* Function: copy a string to stdout, until terminated with a single SDELIM.
  1595. X * Does not advance nextlex at the end.
  1596. X */
  1597. X{
  1598. X        register c;
  1599. X    register FILE *fin, *fout;
  1600. X    fin=finptr;
  1601. X    fout = stdout;
  1602. X    while ((c=getc(fin)) != EOF) {
  1603. X        switch (c) {
  1604. X            case '\n':
  1605. X            ++rcsline;
  1606. X            break;
  1607. X            case SDELIM:
  1608. X            if ((c=getc(fin)) != SDELIM) {
  1609. X                                /* end of string */
  1610. X                                nextc=c;
  1611. X                                return;
  1612. X                        }
  1613. X            break;
  1614. X                }
  1615. X        aputc(c,fout);
  1616. X        }
  1617. X    unterminatedString();
  1618. X}
  1619. X
  1620. X
  1621. X
  1622. X    struct cbuf
  1623. Xsavestring(target)
  1624. X    struct buf *target;
  1625. X/* Copies a string terminated with SDELIM from file finptr to buffer target.
  1626. X * Double SDELIM is replaced with SDELIM.
  1627. X * If foutptr is set, the string is also copied unchanged to foutptr.
  1628. X * Does not advance nextlex at the end.
  1629. X * Yield a copy of *TARGET, except with exact length.
  1630. X */
  1631. X{
  1632. X        register c;
  1633. X    register FILE * fin, * frew;
  1634. X    register char *tp;
  1635. X    const char *lim;
  1636. X    struct cbuf r;
  1637. X
  1638. X    fin=finptr; frew=foutptr;
  1639. X    tp = target->string;  lim = tp + target->size;
  1640. X    for (;;) {
  1641. X        GETC(fin,frew,c);
  1642. X        switch (c) {
  1643. X            case '\n':
  1644. X            ++rcsline;
  1645. X            break;
  1646. X            case SDELIM:
  1647. X            GETC(fin,frew,c);
  1648. X            if (c != SDELIM) {
  1649. X                                /* end of string */
  1650. X                                nextc=c;
  1651. X                r.string = target->string;
  1652. X                r.size = tp - r.string;
  1653. X                return r;
  1654. X                        }
  1655. X            break;
  1656. X            case EOF:
  1657. X            unterminatedString();
  1658. X                }
  1659. X        if (tp == lim)
  1660. X            tp = bufenlarge(target, &lim);
  1661. X        *tp++ = c;
  1662. X        }
  1663. X}
  1664. X
  1665. X
  1666. X    char *
  1667. Xcheckid(id, delimiter)
  1668. X    register char *id;
  1669. X    int delimiter;
  1670. X/*   Function:  check whether the string starting at id is an   */
  1671. X/*        identifier and return a pointer to the delimiter*/
  1672. X/*        after the identifier.  White space, delim and 0 */
  1673. X/*              are legal delimiters.  Aborts the program if not*/
  1674. X/*              a legal identifier. Useful for checking commands*/
  1675. X/*        If !delim, the only delimiter is 0.        */
  1676. X{
  1677. X        register enum  tokens  d;
  1678. X        register char    *temp;
  1679. X        register char    c,tc;
  1680. X    register char delim = delimiter;
  1681. X
  1682. X    temp = id;
  1683. X    if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) {
  1684. X        while ((d = ctab[(unsigned char)(c = *++id)])==LETTER
  1685. X        || d==Letter || d==DIGIT || d==IDCHAR
  1686. X        )
  1687. X        ;
  1688. X        if (c  &&  (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) {
  1689. X                /* append \0 to end of id before error message */
  1690. X                tc = c;
  1691. X                while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
  1692. X                *id = '\0';
  1693. X        faterror("invalid character %c in identifier `%s'",tc,temp);
  1694. X        }
  1695. X        } else {
  1696. X            /* append \0 to end of id before error message */
  1697. X            while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
  1698. X            *id = '\0';
  1699. X        faterror("identifier `%s' doesn't start with letter", temp);
  1700. X        }
  1701. X    return id;
  1702. X}
  1703. X
  1704. X    void
  1705. Xchecksid(id)
  1706. X    register char *id;
  1707. X/* Check whether the string ID is an identifier.  */
  1708. X{
  1709. X    VOID checkid(id, 0);
  1710. X}
  1711. X
  1712. X
  1713. X    exiting void
  1714. XIOerror()
  1715. X{
  1716. X    static looping;
  1717. X    if (looping)
  1718. X        exiterr();
  1719. X    looping = true;
  1720. X    faterror("input/output error; is the file system full?");
  1721. X}
  1722. X
  1723. Xvoid eflush() { if (fflush(stderr) == EOF) IOerror(); }
  1724. Xvoid oflush() { if (fflush(stdout) == EOF) IOerror(); }
  1725. X
  1726. Xexiting void unterminatedString() { fatserror("unterminated string"); }
  1727. X
  1728. X    static exiting void
  1729. Xfatcleanup(already_newline)
  1730. X    int already_newline;
  1731. X{
  1732. X    VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
  1733. X    exiterr();
  1734. X}
  1735. X
  1736. Xstatic void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; }
  1737. Xstatic void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); }
  1738. X
  1739. Xvoid eerror(n) const char *n; { errsay(); perror(n); eflush(); }
  1740. Xexiting void efaterror(n) const char *n; { fatsay(); perror(n); fatcleanup(true); }
  1741. X
  1742. X#if has_prototypes
  1743. X    void
  1744. Xerror(const char *format,...)
  1745. X#else
  1746. X    /*VARARGS1*/ void error(format, va_alist) const char *format; va_dcl
  1747. X#endif
  1748. X/* non-fatal error */
  1749. X{
  1750. X    va_list args;
  1751. X    errsay();
  1752. X    vararg_start(args, format);
  1753. X    fvfprintf(stderr, format, args);
  1754. X    va_end(args);
  1755. X    afputc('\n',stderr);
  1756. X    eflush();
  1757. X}
  1758. X
  1759. X#if has_prototypes
  1760. X    exiting void
  1761. Xfatserror(const char *format,...)
  1762. X#else
  1763. X    /*VARARGS1*/ exiting void
  1764. X    fatserror(format, va_alist) const char *format; va_dcl
  1765. X#endif
  1766. X/* fatal syntax error */
  1767. X{
  1768. X    va_list args;
  1769. X    oflush();
  1770. X    VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline);
  1771. X    vararg_start(args, format);
  1772. X    fvfprintf(stderr, format, args);
  1773. X    va_end(args);
  1774. X    fatcleanup(false);
  1775. X}
  1776. X
  1777. X#if has_prototypes
  1778. X    exiting void
  1779. Xfaterror(const char *format,...)
  1780. X#else
  1781. X    /*VARARGS1*/ exiting void faterror(format, va_alist)
  1782. X    const char *format; va_dcl
  1783. X#endif
  1784. X/* fatal error, terminates program after cleanup */
  1785. X{
  1786. X    va_list args;
  1787. X    fatsay();
  1788. X    vararg_start(args, format);
  1789. X    fvfprintf(stderr, format, args);
  1790. X    va_end(args);
  1791. X    fatcleanup(false);
  1792. X}
  1793. X
  1794. X#if has_prototypes
  1795. X    void
  1796. Xwarn(const char *format,...)
  1797. X#else
  1798. X    /*VARARGS1*/ void warn(format, va_alist) const char *format; va_dcl
  1799. X#endif
  1800. X/* prints a warning message */
  1801. X{
  1802. X    va_list args;
  1803. X    oflush();
  1804. X    aprintf(stderr,"%s warning: ",cmdid);
  1805. X    vararg_start(args, format);
  1806. X    fvfprintf(stderr, format, args);
  1807. X    va_end(args);
  1808. X    afputc('\n',stderr);
  1809. X    eflush();
  1810. X}
  1811. X
  1812. X    void
  1813. Xredefined(c)
  1814. X    int c;
  1815. X{
  1816. X    warn("redefinition of -%c option", c);
  1817. X}
  1818. X
  1819. X#if has_prototypes
  1820. X    void
  1821. Xdiagnose(const char *format,...)
  1822. X#else
  1823. X    /*VARARGS1*/ void diagnose(format, va_alist) const char *format; va_dcl
  1824. X#endif
  1825. X/* prints a diagnostic message */
  1826. X/* Unlike the other routines, it does not append a newline. */
  1827. X/* This lets some callers suppress the newline, and is faster */
  1828. X/* in implementations that flush stderr just at the end of each printf. */
  1829. X{
  1830. X    va_list args;
  1831. X        if (!quietflag) {
  1832. X        oflush();
  1833. X        vararg_start(args, format);
  1834. X        fvfprintf(stderr, format, args);
  1835. X        va_end(args);
  1836. X        eflush();
  1837. X        }
  1838. X}
  1839. X
  1840. X
  1841. X
  1842. X    void
  1843. Xafputc(c, f)
  1844. X/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower.
  1845. X */
  1846. X    int c;
  1847. X    register FILE *f;
  1848. X{
  1849. X    aputc(c,f);
  1850. X}
  1851. X
  1852. X
  1853. X    void
  1854. Xaputs(s, iop)
  1855. X    const char *s;
  1856. X    FILE *iop;
  1857. X/* Function: Put string s on file iop, abort on error.
  1858. X */
  1859. X{
  1860. X    if (fputs(s, iop) == EOF)
  1861. X        IOerror();
  1862. X}
  1863. X
  1864. X
  1865. X
  1866. X    void
  1867. X#if has_prototypes
  1868. Xfvfprintf(FILE *stream, const char *format, va_list args)
  1869. X#else
  1870. X    fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
  1871. X#endif
  1872. X/* like vfprintf, except abort program on error */
  1873. X{
  1874. X#if has_vfprintf
  1875. X    if (vfprintf(stream, format, args) == EOF)
  1876. X#else
  1877. X    _doprnt(format, args, stream);
  1878. X    if (ferror(stream))
  1879. X#endif
  1880. X        IOerror();
  1881. X}
  1882. X
  1883. X#if has_prototypes
  1884. X    void
  1885. Xaprintf(FILE *iop, const char *fmt, ...)
  1886. X#else
  1887. X    /*VARARGS2*/ void
  1888. Xaprintf(iop, fmt, va_alist)
  1889. XFILE *iop;
  1890. Xconst char *fmt;
  1891. Xva_dcl
  1892. X#endif
  1893. X/* Function: formatted output. Same as fprintf in stdio,
  1894. X * but aborts program on error
  1895. X */
  1896. X{
  1897. X    va_list ap;
  1898. X    vararg_start(ap, fmt);
  1899. X    fvfprintf(iop, fmt, ap);
  1900. X    va_end(ap);
  1901. X}
  1902. X
  1903. X
  1904. X
  1905. X#ifdef LEXDB
  1906. X/* test program reading a stream of lexemes and printing the tokens.
  1907. X */
  1908. X
  1909. X
  1910. X
  1911. X    int
  1912. Xmain(argc,argv)
  1913. Xint argc; char * argv[];
  1914. X{
  1915. X        cmdid="lextest";
  1916. X        if (argc<2) {
  1917. X        aputs("No input file\n",stderr);
  1918. X        exitmain(EXIT_FAILURE);
  1919. X        }
  1920. X        if ((finptr=fopen(argv[1], "r")) == NULL) {
  1921. X        faterror("can't open input file %s",argv[1]);
  1922. X        }
  1923. X        Lexinit();
  1924. X        while (nexttok != EOFILE) {
  1925. X        switch (nexttok) {
  1926. X
  1927. X        case ID:
  1928. X                VOID printf("ID: %s",NextString);
  1929. X                break;
  1930. X
  1931. X        case NUM:
  1932. X        if (hshenter)
  1933. X                   VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
  1934. X                else
  1935. X                   VOID printf("NUM, unentered: %s",NextString);
  1936. X                hshenter = !hshenter; /*alternate between dates and numbers*/
  1937. X                break;
  1938. X
  1939. X        case COLON:
  1940. X                VOID printf("COLON"); break;
  1941. X
  1942. X        case SEMI:
  1943. X                VOID printf("SEMI"); break;
  1944. X
  1945. X        case STRING:
  1946. X                readstring();
  1947. X                VOID printf("STRING"); break;
  1948. X
  1949. X        case UNKN:
  1950. X                VOID printf("UNKN"); break;
  1951. X
  1952. X        default:
  1953. X                VOID printf("DEFAULT"); break;
  1954. X        }
  1955. X        VOID printf(" | ");
  1956. X        nextlex();
  1957. X        }
  1958. X        VOID printf("\nEnd of lexical analyzer test\n");
  1959. X    exitmain(EXIT_SUCCESS);
  1960. X}
  1961. X
  1962. Xexiting void exiterr() { _exit(EXIT_FAILURE); }
  1963. X
  1964. X
  1965. X#endif
  1966. END_OF_FILE
  1967.   if test 24012 -ne `wc -c <'src/rcslex.c'`; then
  1968.     echo shar: \"'src/rcslex.c'\" unpacked with wrong size!
  1969.   fi
  1970.   # end of 'src/rcslex.c'
  1971. fi
  1972. echo shar: End of archive 5 \(of 12\).
  1973. cp /dev/null ark5isdone
  1974. MISSING=""
  1975. for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
  1976.     if test ! -f ark${I}isdone ; then
  1977.     MISSING="${MISSING} ${I}"
  1978.     fi
  1979. done
  1980. if test "${MISSING}" = "" ; then
  1981.     echo You have unpacked all 12 archives.
  1982.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1983. else
  1984.     echo You still must unpack the following archives:
  1985.     echo "        " ${MISSING}
  1986. fi
  1987. exit 0
  1988. exit 0 # Just in case...
  1989.