home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 3 / goldfish_volume_3.bin / files / dev / misc / hwgrcs / src / rcs.rcsfiles / rcsedit.c,v < prev    next >
Encoding:
Text File  |  1995-06-25  |  110.6 KB  |  4,074 lines

  1. head    5.11;
  2. branch    5.11.1;
  3. access;
  4. symbols
  5.     HWGRCSP12F:5.11.1.10
  6.     HWGRCSP11F:5.11.1.10
  7.     HWGRCSP10F:5.11.1.7
  8.     HWGRCSP9:5.11.1.7
  9.     HWGRCSP8F:5.11.1.6
  10.     HWGRCSP7F:5.11.1.3
  11.     HWGRCSP6F:5.11.1.3
  12.     HWGRCSP5F:5.11.1.1
  13.     HWGRCSp4:5.11.1.1
  14.     HWGRCSp3:5.11.1
  15.     HWGRCS_Fish:5.11.1
  16.     HWGRCS:5.11.1;
  17. locks; strict;
  18. comment    @ * @;
  19.  
  20.  
  21. 5.11
  22. date    91.11.03.01.11.44;    author eggert;    state Exp;
  23. branches
  24.     5.11.1.1;
  25. next    ;
  26.  
  27. 5.11.1.1
  28. date    93.01.18.14.38.13;    author heinz;    state Exp;
  29. branches;
  30. next    5.11.1.2;
  31.  
  32. 5.11.1.2
  33. date    93.12.04.08.40.36;    author heinz;    state Exp;
  34. branches;
  35. next    5.11.1.3;
  36.  
  37. 5.11.1.3
  38. date    93.12.18.16.12.22;    author heinz;    state Exp;
  39. branches;
  40. next    5.11.1.4;
  41.  
  42. 5.11.1.4
  43. date    94.02.12.19.20.21;    author heinz;    state Exp;
  44. branches;
  45. next    5.11.1.5;
  46.  
  47. 5.11.1.5
  48. date    94.02.14.19.29.45;    author heinz;    state Exp;
  49. branches;
  50. next    5.11.1.6;
  51.  
  52. 5.11.1.6
  53. date    94.03.14.09.19.05;    author heinz;    state Exp;
  54. branches;
  55. next    5.11.1.7;
  56.  
  57. 5.11.1.7
  58. date    94.03.31.18.38.48;    author heinz;    state Exp;
  59. branches;
  60. next    5.11.1.8;
  61.  
  62. 5.11.1.8
  63. date    94.08.01.20.53.56;    author heinz;    state Exp;
  64. branches;
  65. next    5.11.1.9;
  66.  
  67. 5.11.1.9
  68. date    94.08.05.16.43.13;    author heinz;    state Exp;
  69. branches;
  70. next    5.11.1.10;
  71.  
  72. 5.11.1.10
  73. date    94.08.05.17.35.57;    author heinz;    state Exp;
  74. branches;
  75. next    ;
  76.  
  77.  
  78. desc
  79. @Checked in with -k 16.jan.93 HWG
  80. @
  81.  
  82.  
  83. 5.11
  84. log
  85. @checked in with -k by heinz at 1993/01/17 01:53:17
  86. @
  87. text
  88. @/*
  89.  *                     RCS stream editor
  90.  */
  91. /**********************************************************************************
  92.  *                       edits the input file according to a
  93.  *                       script from stdin, generated by diff -n
  94.  *                       performs keyword expansion
  95.  **********************************************************************************
  96.  */
  97.  
  98. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  99.    Copyright 1990, 1991 by Paul Eggert
  100.    Distributed under license by the Free Software Foundation, Inc.
  101.  
  102. This file is part of RCS.
  103.  
  104. RCS is free software; you can redistribute it and/or modify
  105. it under the terms of the GNU General Public License as published by
  106. the Free Software Foundation; either version 2, or (at your option)
  107. any later version.
  108.  
  109. RCS is distributed in the hope that it will be useful,
  110. but WITHOUT ANY WARRANTY; without even the implied warranty of
  111. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  112. GNU General Public License for more details.
  113.  
  114. You should have received a copy of the GNU General Public License
  115. along with RCS; see the file COPYING.  If not, write to
  116. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  117.  
  118. Report problems and direct all questions to:
  119.  
  120.     rcs-bugs@@cs.purdue.edu
  121.  
  122. */
  123.  
  124.  
  125. /* $Log: rcsedit.c,v $
  126.  * Revision 5.11  1991/11/03  01:11:44  eggert
  127.  * Move the warning about link breaking to where they're actually being broken.
  128.  *
  129.  * Revision 5.10  1991/10/07  17:32:46  eggert
  130.  * Support piece tables even if !has_mmap.  Fix rare NFS bugs.
  131.  *
  132.  * Revision 5.9  1991/09/17  19:07:40  eggert
  133.  * SGI readlink() yields ENXIO, not EINVAL, for nonlinks.
  134.  *
  135.  * Revision 5.8  1991/08/19  03:13:55  eggert
  136.  * Add piece tables, NFS bug workarounds.  Catch odd filenames.  Tune.
  137.  *
  138.  * Revision 5.7  1991/04/21  11:58:21  eggert
  139.  * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
  140.  *
  141.  * Revision 5.6  1991/02/25  07:12:40  eggert
  142.  * Fix setuid bug.  Support new link behavior.  Work around broken "w+" fopen.
  143.  *
  144.  * Revision 5.5  1990/12/30  05:07:35  eggert
  145.  * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL).
  146.  *
  147.  * Revision 5.4  1990/11/01  05:03:40  eggert
  148.  * Permit arbitrary data in comment leaders.
  149.  *
  150.  * Revision 5.3  1990/09/11  02:41:13  eggert
  151.  * Tune expandline().
  152.  *
  153.  * Revision 5.2  1990/09/04  08:02:21  eggert
  154.  * Count RCS lines better.  Improve incomplete line handling.
  155.  *
  156.  * Revision 5.1  1990/08/29  07:13:56  eggert
  157.  * Add -kkvl.
  158.  * Fix bug when getting revisions to files ending in incomplete lines.
  159.  * Fix bug in comment leader expansion.
  160.  *
  161.  * Revision 5.0  1990/08/22  08:12:47  eggert
  162.  * Don't require final newline.
  163.  * Don't append "checked in with -k by " to logs,
  164.  * so that checking in a program with -k doesn't change it.
  165.  * Don't generate trailing white space for empty comment leader.
  166.  * Remove compile-time limits; use malloc instead.  Add -k, -V.
  167.  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
  168.  * Ansify and Posixate.  Check diff's output.
  169.  *
  170.  * Revision 4.8  89/05/01  15:12:35  narten
  171.  * changed copyright header to reflect current distribution rules
  172.  * 
  173.  * Revision 4.7  88/11/08  13:54:14  narten
  174.  * misplaced semicolon caused infinite loop
  175.  * 
  176.  * Revision 4.6  88/08/09  19:12:45  eggert
  177.  * Shrink stdio code size; allow cc -R.
  178.  * 
  179.  * Revision 4.5  87/12/18  11:38:46  narten
  180.  * Changes from the 43. version. Don't know the significance of the
  181.  * first change involving "rewind". Also, additional "lint" cleanup.
  182.  * (Guy Harris)
  183.  * 
  184.  * Revision 4.4  87/10/18  10:32:21  narten
  185.  * Updating version numbers. Changes relative to version 1.1 actually
  186.  * relative to 4.1
  187.  * 
  188.  * Revision 1.4  87/09/24  13:59:29  narten
  189.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  190.  * warnings)
  191.  * 
  192.  * Revision 1.3  87/09/15  16:39:39  shepler
  193.  * added an initializatin of the variables editline and linecorr
  194.  * this will be done each time a file is processed.
  195.  * (there was an obscure bug where if co was used to retrieve multiple files
  196.  *  it would dump)
  197.  * fix attributed to  Roy Morris @@FileNet Corp ...!felix!roy
  198.  * 
  199.  * Revision 1.2  87/03/27  14:22:17  jenkins
  200.  * Port to suns
  201.  * 
  202.  * Revision 4.1  83/05/12  13:10:30  wft
  203.  * Added new markers Id and RCSfile; added locker to Header and Id.
  204.  * Overhauled expandline completely() (problem with $01234567890123456789@@).
  205.  * Moved trymatch() and marker table to rcskeys.c.
  206.  * 
  207.  * Revision 3.7  83/05/12  13:04:39  wft
  208.  * Added retry to expandline to resume after failed match which ended in $.
  209.  * Fixed truncation problem for $19chars followed by@@@@.
  210.  * Log no longer expands full path of RCS file.
  211.  * 
  212.  * Revision 3.6  83/05/11  16:06:30  wft
  213.  * added retry to expandline to resume after failed match which ended in $.
  214.  * Fixed truncation problem for $19chars followed by@@@@.
  215.  * 
  216.  * Revision 3.5  82/12/04  13:20:56  wft
  217.  * Added expansion of keyword Locker.
  218.  *
  219.  * Revision 3.4  82/12/03  12:26:54  wft
  220.  * Added line number correction in case editing does not start at the
  221.  * beginning of the file.
  222.  * Changed keyword expansion to always print a space before closing KDELIM;
  223.  * Expansion for Header shortened.
  224.  *
  225.  * Revision 3.3  82/11/14  14:49:30  wft
  226.  * removed Suffix from keyword expansion. Replaced fclose with ffclose.
  227.  * keyreplace() gets log message from delta, not from curlogmsg.
  228.  * fixed expression overflow in while(c=putc(GETC....
  229.  * checked nil printing.
  230.  *
  231.  * Revision 3.2  82/10/18  21:13:39  wft
  232.  * I added checks for write errors during the co process, and renamed
  233.  * expandstring() to xpandstring().
  234.  *
  235.  * Revision 3.1  82/10/13  15:52:55  wft
  236.  * changed type of result of getc() from char to int.
  237.  * made keyword expansion loop in expandline() portable to machines
  238.  * without sign-extension.
  239.  */
  240.  
  241.  
  242. #include "rcsbase.h"
  243.  
  244. libId(editId, "$Id: rcsedit.c,v 5.11 1991/11/03 01:11:44 eggert Exp $")
  245.  
  246. static void keyreplace P((enum markers,struct hshentry const*,FILE*));
  247.  
  248.  
  249. FILE *fcopy;         /* result file descriptor                */
  250. char const *resultfile;  /* result file name                    */
  251. int locker_expansion;     /* should the locker name be appended to Id val?   */
  252. #if !large_memory
  253.     static RILE *fedit; /* edit file descriptor */
  254.     static char const *editfile; /* edit pathname */
  255. #endif
  256. static unsigned long editline; /* edit line counter; #lines before cursor   */
  257. static long linecorr; /* #adds - #deletes in each edit run.            */
  258.                /*used to correct editline in case file is not rewound after */
  259.                /* applying one delta                                        */
  260.  
  261. #define DIRTEMPNAMES 2
  262. enum maker {notmade, real, effective};
  263. struct buf dirtfname[DIRTEMPNAMES];        /* unlink these when done */
  264. static enum maker volatile dirtfmaker[DIRTEMPNAMES];    /* if these are set */
  265.  
  266.  
  267. #if has_NFS || bad_unlink
  268.     int
  269. un_link(s)
  270.     char const *s;
  271. /*
  272.  * Remove S, even if it is unwritable.
  273.  * Ignore unlink() ENOENT failures; NFS generates bogus ones.
  274.  */
  275. {
  276. #    if bad_unlink
  277.         int e;
  278.         if (unlink(s) == 0)
  279.             return 0;
  280.         e = errno;
  281. #        if has_NFS
  282.             if (e == ENOENT)
  283.                 return 0;
  284. #        endif
  285.         if (chmod(s, S_IWUSR) != 0) {
  286.             errno = e;
  287.             return -1;
  288.         }
  289. #    endif
  290. #    if has_NFS
  291.         return unlink(s)==0 || errno==ENOENT  ?  0  :  -1;
  292. #    else
  293.         return unlink(s);
  294. #    endif
  295. }
  296. #endif
  297.  
  298. #if !has_rename
  299. #  if !has_NFS
  300. #    define do_link(s,t) link(s,t)
  301. #  else
  302.     static int
  303. do_link(s, t)
  304.     char const *s, *t;
  305. /* Link S to T, ignoring bogus EEXIST problems due to NFS failures.  */
  306. {
  307.     struct stat sb, tb;
  308.  
  309.     if (link(s,t) == 0)
  310.         return 0;
  311.     if (errno != EEXIST)
  312.         return -1;
  313.     if (
  314.         stat(s, &sb) == 0  &&
  315.         stat(t, &tb) == 0  &&
  316.         sb.st_ino == tb.st_ino  &&
  317.         sb.st_dev == tb.st_dev
  318.     )
  319.         return 0;
  320.     errno = EEXIST;
  321.     return -1;
  322. }
  323. #  endif
  324. #endif
  325.  
  326.  
  327.     static exiting void
  328. editEndsPrematurely()
  329. {
  330.     fatserror("edit script ends prematurely");
  331. }
  332.  
  333.     static exiting void
  334. editLineNumberOverflow()
  335. {
  336.     fatserror("edit script refers to line past end of file");
  337. }
  338.  
  339.  
  340. #if large_memory
  341.  
  342. #if has_memmove
  343. #    define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
  344. #else
  345.     static void
  346. movelines(s1, s2, n)
  347.     register Iptr_type *s1;
  348.     register Iptr_type const *s2;
  349.     register unsigned long n;
  350. {
  351.     if (s1 < s2)
  352.         do {
  353.             *s1++ = *s2++;
  354.         } while (--n);
  355.     else {
  356.         s1 += n;
  357.         s2 += n;
  358.         do {
  359.             *--s1 = *--s2;
  360.         } while (--n);
  361.     }
  362. }
  363. #endif
  364.  
  365. /*
  366.  * `line' contains pointers to the lines in the currently `edited' file.
  367.  * It is a 0-origin array that represents linelim-gapsize lines.
  368.  * line[0..gap-1] and line[gap+gapsize..linelim-1] contain pointers to lines.
  369.  * line[gap..gap+gapsize-1] contains garbage.
  370.  *
  371.  * Any @@s in lines are duplicated.
  372.  * Lines are terminated by \n, or (for a last partial line only) by single @@.
  373.  */
  374. static Iptr_type *line;
  375. static unsigned long gap, gapsize, linelim;
  376.  
  377.  
  378.     static void
  379. insertline(n, l)
  380.     unsigned long n;
  381.     Iptr_type l;
  382. /* Before line N, insert line L.  N is 0-origin.  */
  383. {
  384.     if (linelim-gapsize < n)
  385.         editLineNumberOverflow();
  386.     if (!gapsize)
  387.         line =
  388.         !linelim ?
  389.             tnalloc(Iptr_type, linelim = gapsize = 1024)
  390.         : (
  391.             gap = gapsize = linelim,
  392.             trealloc(Iptr_type, line, linelim <<= 1)
  393.         );
  394.     if (n < gap)
  395.         movelines(line+n+gapsize, line+n, gap-n);
  396.     else if (gap < n)
  397.         movelines(line+gap, line+gap+gapsize, n-gap);
  398.  
  399.     line[n] = l;
  400.     gap = n + 1;
  401.     gapsize--;
  402. }
  403.  
  404.     static void
  405. deletelines(n, nlines)
  406.     unsigned long n, nlines;
  407. /* Delete lines N through N+NLINES-1.  N is 0-origin.  */
  408. {
  409.     unsigned long l = n + nlines;
  410.     if (linelim-gapsize < l  ||  l < n)
  411.         editLineNumberOverflow();
  412.     if (l < gap)
  413.         movelines(line+l+gapsize, line+l, gap-l);
  414.     else if (gap < n)
  415.         movelines(line+gap, line+gap+gapsize, n-gap);
  416.  
  417.     gap = n;
  418.     gapsize += nlines;
  419. }
  420.  
  421.     static void
  422. snapshotline(f, l)
  423.     register FILE *f;
  424.     register Iptr_type l;
  425. {
  426.     register int c;
  427.     do {
  428.         if ((c = *l++) == SDELIM  &&  *l++ != SDELIM)
  429.             return;
  430.         aputc(c, f);
  431.     } while (c != '\n');
  432. }
  433.  
  434.     void
  435. snapshotedit(f)
  436.     FILE *f;
  437. /* Copy the current state of the edits to F.  */
  438. {
  439.     register Iptr_type *p, *lim, *l=line;
  440.     for (p=l, lim=l+gap;  p<lim;  )
  441.         snapshotline(f, *p++);
  442.     for (p+=gapsize, lim=l+linelim;  p<lim;  )
  443.         snapshotline(f, *p++);
  444. }
  445.  
  446.     static void
  447. finisheditline(fin, fout, l, delta)
  448.     RILE *fin;
  449.     FILE *fout;
  450.     Iptr_type l;
  451.     struct hshentry const *delta;
  452. {
  453.     Iseek(fin, l);
  454.     if (expandline(fin, fout, delta, true, (FILE*)0)  <  0)
  455.         faterror("finisheditline internal error");
  456. }
  457.  
  458.     void
  459. finishedit(delta, outfile, done)
  460.     struct hshentry const *delta;
  461.     FILE *outfile;
  462.     int done;
  463. /*
  464.  * Doing expansion if DELTA is set, output the state of the edits to OUTFILE.
  465.  * But do nothing unless DONE is set (which means we are on the last pass).
  466.  */
  467. {
  468.     if (done) {
  469.         openfcopy(outfile);
  470.         outfile = fcopy;
  471.         if (!delta)
  472.             snapshotedit(outfile);
  473.         else {
  474.             register Iptr_type *p, *lim, *l = line;
  475.             register RILE *fin = finptr;
  476.             Iptr_type here = Itell(fin);
  477.             for (p=l, lim=l+gap;  p<lim;  )
  478.                 finisheditline(fin, outfile, *p++, delta);
  479.             for (p+=gapsize, lim=l+linelim;  p<lim;  )
  480.                 finisheditline(fin, outfile, *p++, delta);
  481.             Iseek(fin, here);
  482.         }
  483.     }
  484. }
  485.  
  486. /* Open a temporary FILENAME for output, truncating any previous contents.  */
  487. #   define fopen_update_truncate(filename) fopen(filename, FOPEN_W_WORK)
  488. #else /* !large_memory */
  489.     static FILE *
  490. fopen_update_truncate(filename)
  491.     char const *filename;
  492. {
  493. #    if bad_fopen_wplus
  494.         if (un_link(filename) != 0)
  495.             efaterror(filename);
  496. #    endif
  497.     return fopen(filename, FOPEN_WPLUS_WORK);
  498. }
  499. #endif
  500.  
  501.  
  502.     void
  503. openfcopy(f)
  504.     FILE *f;
  505. {
  506.     if (!(fcopy = f)) {
  507.         if (!resultfile)
  508.             resultfile = maketemp(2);
  509.         if (!(fcopy = fopen_update_truncate(resultfile)))
  510.             efaterror(resultfile);
  511.     }
  512. }
  513.  
  514.  
  515. #if !large_memory
  516.  
  517.     static void
  518. swapeditfiles(outfile)
  519.     FILE *outfile;
  520. /* Function: swaps resultfile and editfile, assigns fedit=fcopy,
  521.  * and rewinds fedit for reading.  Set fcopy to outfile if nonnull;
  522.  * otherwise, set fcopy to be resultfile opened for reading and writing.
  523.  */
  524. {
  525.     char const *tmpptr;
  526.  
  527.     editline = 0;  linecorr = 0;
  528.     if (fseek(fcopy, 0L, SEEK_SET) != 0)
  529.         Oerror();
  530.     fedit = fcopy;
  531.         tmpptr=editfile; editfile=resultfile; resultfile=tmpptr;
  532.     openfcopy(outfile);
  533. }
  534.  
  535.     void
  536. snapshotedit(f)
  537.     FILE *f;
  538. /* Copy the current state of the edits to F.  */
  539. {
  540.     finishedit((struct hshentry *)nil, (FILE*)0, false);
  541.     fastcopy(fedit, f);
  542.     Irewind(fedit);
  543. }
  544.  
  545.     void
  546. finishedit(delta, outfile, done)
  547.     struct hshentry const *delta;
  548.     FILE *outfile;
  549.     int done;
  550. /* copy the rest of the edit file and close it (if it exists).
  551.  * if delta!=nil, perform keyword substitution at the same time.
  552.  * If DONE is set, we are finishing the last pass.
  553.  */
  554. {
  555.     register RILE *fe;
  556.     register FILE *fc;
  557.  
  558.     fe = fedit;
  559.     if (fe) {
  560.         fc = fcopy;
  561.                 if (delta!=nil) {
  562.             while (1 < expandline(fe,fc,delta,false,(FILE*)0))
  563.                 ;
  564.                 } else {
  565.             fastcopy(fe,fc);
  566.                 }
  567.         Ifclose(fe);
  568.         }
  569.     if (!done)
  570.         swapeditfiles(outfile);
  571. }
  572. #endif
  573.  
  574.  
  575.  
  576. #if large_memory
  577. #    define copylines(upto,delta) (editline = (upto))
  578. #else
  579.     static void
  580. copylines(upto,delta)
  581.     register unsigned long upto;
  582.     struct hshentry const *delta;
  583. /*
  584.  * Copy input lines editline+1..upto from fedit to fcopy.
  585.  * If delta != nil, keyword expansion is done simultaneously.
  586.  * editline is updated. Rewinds a file only if necessary.
  587.  */
  588. {
  589.     register int c;
  590.     declarecache;
  591.     register FILE *fc;
  592.     register RILE *fe;
  593.  
  594.     if (upto < editline) {
  595.                 /* swap files */
  596.         finishedit((struct hshentry *)nil, (FILE*)0, false);
  597.                 /* assumes edit only during last pass, from the beginning*/
  598.         }
  599.     fe = fedit;
  600.     fc = fcopy;
  601.     if (editline < upto)
  602.         if (delta)
  603.         do {
  604.             if (expandline(fe,fc,delta,false,(FILE*)0) <= 1)
  605.                 editLineNumberOverflow();
  606.         } while (++editline < upto);
  607.         else {
  608.         setupcache(fe); cache(fe);
  609.         do {
  610.             do {
  611.                 cachegeteof(c, editLineNumberOverflow(););
  612.                 aputc(c, fc);
  613.             } while (c != '\n');
  614.         } while (++editline < upto);
  615.         uncache(fe);
  616.         }
  617. }
  618. #endif
  619.  
  620.  
  621.  
  622.     void
  623. xpandstring(delta)
  624.     struct hshentry const *delta;
  625. /* Function: Reads a string terminated by SDELIM from finptr and writes it
  626.  * to fcopy. Double SDELIM is replaced with single SDELIM.
  627.  * Keyword expansion is performed with data from delta.
  628.  * If foutptr is nonnull, the string is also copied unchanged to foutptr.
  629.  */
  630. {
  631.     while (1 < expandline(finptr,fcopy,delta,true,foutptr))
  632.         ;
  633. }
  634.  
  635.  
  636.     void
  637. copystring()
  638. /* Function: copies a string terminated with a single SDELIM from finptr to
  639.  * fcopy, replacing all double SDELIM with a single SDELIM.
  640.  * If foutptr is nonnull, the string also copied unchanged to foutptr.
  641.  * editline is incremented by the number of lines copied.
  642.  * Assumption: next character read is first string character.
  643.  */
  644. {    register c;
  645.     declarecache;
  646.     register FILE *frew, *fcop;
  647.     register int amidline;
  648.     register RILE *fin;
  649.  
  650.     fin = finptr;
  651.     setupcache(fin); cache(fin);
  652.     frew = foutptr;
  653.     fcop = fcopy;
  654.     amidline = false;
  655.     for (;;) {
  656.         GETC(frew,c);
  657.         switch (c) {
  658.             case '\n':
  659.             ++editline;
  660.             ++rcsline;
  661.             amidline = false;
  662.             break;
  663.             case SDELIM:
  664.             GETC(frew,c);
  665.             if (c != SDELIM) {
  666.                 /* end of string */
  667.                 nextc = c;
  668.                 editline += amidline;
  669.                 uncache(fin);
  670.                 return;
  671.             }
  672.             /* fall into */
  673.             default:
  674.             amidline = true;
  675.             break;
  676.                 }
  677.         aputc(c,fcop);
  678.         }
  679. }
  680.  
  681.  
  682.     void
  683. enterstring()
  684. /* Like copystring, except the string is put into the edit data structure.  */
  685. {
  686. #if !large_memory
  687.     editfile = 0;
  688.     fedit = 0;
  689.     editline = linecorr = 0;
  690.     resultfile = maketemp(1);
  691.     if (!(fcopy = fopen_update_truncate(resultfile)))
  692.         efaterror(resultfile);
  693.     copystring();
  694. #else
  695.     register int c;
  696.     declarecache;
  697.     register FILE *frew;
  698.     register unsigned long e, oe;
  699.     register int amidline, oamidline;
  700.     register Iptr_type optr;
  701.     register RILE *fin;
  702.  
  703.     e = 0;
  704.     gap = 0;
  705.     gapsize = linelim;
  706.     fin = finptr;
  707.     setupcache(fin); cache(fin);
  708.     advise_access(fin, MADV_NORMAL);
  709.     frew = foutptr;
  710.     amidline = false;
  711.     for (;;) {
  712.         optr = cachetell();
  713.         GETC(frew,c);
  714.         oamidline = amidline;
  715.         oe = e;
  716.         switch (c) {
  717.             case '\n':
  718.             ++e;
  719.             ++rcsline;
  720.             amidline = false;
  721.             break;
  722.             case SDELIM:
  723.             GETC(frew,c);
  724.             if (c != SDELIM) {
  725.                 /* end of string */
  726.                 nextc = c;
  727.                 editline = e + amidline;
  728.                 linecorr = 0;
  729.                 uncache(fin);
  730.                 return;
  731.             }
  732.             /* fall into */
  733.             default:
  734.             amidline = true;
  735.             break;
  736.         }
  737.         if (!oamidline)
  738.             insertline(oe, optr);
  739.     }
  740. #endif
  741. }
  742.  
  743.  
  744.  
  745.  
  746.     void
  747. #if large_memory
  748. edit_string()
  749. #else
  750.   editstring(delta)
  751.     struct hshentry const *delta;
  752. #endif
  753. /*
  754.  * Read an edit script from finptr and applies it to the edit file.
  755. #if !large_memory
  756.  * The result is written to fcopy.
  757.  * If delta!=nil, keyword expansion is performed simultaneously.
  758.  * If running out of lines in fedit, fedit and fcopy are swapped.
  759.  * editfile is the name of the file that goes with fedit.
  760. #endif
  761.  * If foutptr is set, the edit script is also copied verbatim to foutptr.
  762.  * Assumes that all these files are open.
  763.  * resultfile is the name of the file that goes with fcopy.
  764.  * Assumes the next input character from finptr is the first character of
  765.  * the edit script. Resets nextc on exit.
  766.  */
  767. {
  768.         int ed; /* editor command */
  769.         register int c;
  770.     declarecache;
  771.     register FILE *frew;
  772. #    if !large_memory
  773.         register FILE *f;
  774.         unsigned long line_lim = ULONG_MAX;
  775.         register RILE *fe;
  776. #    endif
  777.     register unsigned long i;
  778.     register RILE *fin;
  779. #    if large_memory
  780.         register unsigned long j;
  781. #    endif
  782.     struct diffcmd dc;
  783.  
  784.         editline += linecorr; linecorr=0; /*correct line number*/
  785.     frew = foutptr;
  786.     fin = finptr;
  787.     setupcache(fin);
  788.     initdiffcmd(&dc);
  789.     while (0  <=  (ed = getdiffcmd(fin,true,frew,&dc)))
  790. #if !large_memory
  791.         if (line_lim <= dc.line1)
  792.             editLineNumberOverflow();
  793.         else
  794. #endif
  795.         if (!ed) {
  796.             copylines(dc.line1-1, delta);
  797.                         /* skip over unwanted lines */
  798.             i = dc.nlines;
  799.             linecorr -= i;
  800.             editline += i;
  801. #            if large_memory
  802.                 deletelines(editline+linecorr, i);
  803. #            else
  804.                 fe = fedit;
  805.                 do {
  806.                                 /*skip next line*/
  807.                 do {
  808.                     Igeteof(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } );
  809.                 } while (c != '\n');
  810.                 } while (--i);
  811. #            endif
  812.         } else {
  813.             copylines(dc.line1, delta); /*copy only; no delete*/
  814.             i = dc.nlines;
  815. #            if large_memory
  816.                 j = editline+linecorr;
  817. #            endif
  818.             linecorr += i;
  819. #if !large_memory
  820.             f = fcopy;
  821.             if (delta)
  822.                 do {
  823.                 switch (expandline(fin,f,delta,true,frew)) {
  824.                     case 0: case 1:
  825.                     if (i==1)
  826.                         return;
  827.                     /* fall into */
  828.                     case -1:
  829.                     editEndsPrematurely();
  830.                 }
  831.                 } while (--i);
  832.             else
  833. #endif
  834.             {
  835.                 cache(fin);
  836.                 do {
  837. #                if large_memory
  838.                     insertline(j++, cachetell());
  839. #                endif
  840.                 for (;;) {
  841.                     GETC(frew, c);
  842. #                    if !large_memory
  843.                     aputc(c, f);
  844. #                    endif
  845.                     if (c == '\n')
  846.                     break;
  847.                     if (c==SDELIM) {
  848.                     GETC(frew, c);
  849.                     if (c!=SDELIM) {
  850.                         if (--i)
  851.                         editEndsPrematurely();
  852.                         nextc = c;
  853.                         uncache(fin);
  854.                         return;
  855.                     }
  856.                     }
  857.                 }
  858.                 ++rcsline;
  859.                 } while (--i);
  860.                 uncache(fin);
  861.             }
  862.                 }
  863. }
  864.  
  865.  
  866.  
  867. /* The rest is for keyword expansion */
  868.  
  869.  
  870.  
  871.     int
  872. expandline(infile, outfile, delta, delimstuffed, frewfile)
  873.     RILE *infile;
  874.     FILE *outfile, *frewfile;
  875.     struct hshentry const *delta;
  876.     int delimstuffed;
  877. /*
  878.  * Read a line from INFILE and write it to OUTFILE.
  879.  * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM.
  880.  * Keyword expansion is performed with data from delta.
  881.  * If FREWFILE is set, copy the line unchanged to FREWFILE.
  882.  * DELIMSTUFFED must be true if FREWFILE is set.
  883.  * Yields -1 if no data is copied, 0 if an incomplete line is copied,
  884.  * 2 if a complete line is copied; adds 1 to yield if expansion occurred.
  885.  */
  886. {
  887.     register c;
  888.     declarecache;
  889.     register FILE *out, *frew;
  890.     register char * tp;
  891.     register int e, ds, r;
  892.     char const *tlim;
  893.     static struct buf keyval;
  894.         enum markers matchresult;
  895.  
  896.     setupcache(infile); cache(infile);
  897.     out = outfile;
  898.     frew = frewfile;
  899.     ds = delimstuffed;
  900.     bufalloc(&keyval, keylength+3);
  901.     e = 0;
  902.     r = -1;
  903.  
  904.         for (;;) {
  905.         if (ds) {
  906.         GETC(frew, c);
  907.         } else
  908.         cachegeteof(c, goto uncache_exit;);
  909.         for (;;) {
  910.         switch (c) {
  911.             case SDELIM:
  912.             if (ds) {
  913.                 GETC(frew, c);
  914.                 if (c != SDELIM) {
  915.                                 /* end of string */
  916.                                 nextc=c;
  917.                 goto uncache_exit;
  918.                 }
  919.             }
  920.             /* fall into */
  921.             default:
  922.             aputc(c,out);
  923.             r = 0;
  924.             break;
  925.  
  926.             case '\n':
  927.             rcsline += ds;
  928.             aputc(c,out);
  929.             r = 2;
  930.             goto uncache_exit;
  931.  
  932.             case KDELIM:
  933.             r = 0;
  934.                         /* check for keyword */
  935.                         /* first, copy a long enough string into keystring */
  936.             tp = keyval.string;
  937.             *tp++ = KDELIM;
  938.             for (;;) {
  939.                 if (ds) {
  940.                 GETC(frew, c);
  941.                 } else
  942.                 cachegeteof(c, goto keystring_eof;);
  943.                 if (tp < keyval.string+keylength+1)
  944.                 switch (ctab[c]) {
  945.                     case LETTER: case Letter:
  946.                     *tp++ = c;
  947.                     continue;
  948.                     default:
  949.                     break;
  950.                 }
  951.                 break;
  952.                         }
  953.             *tp++ = c; *tp = '\0';
  954.             matchresult = trymatch(keyval.string+1);
  955.             if (matchresult==Nomatch) {
  956.                 tp[-1] = 0;
  957.                 aputs(keyval.string, out);
  958.                 continue;   /* last c handled properly */
  959.             }
  960.  
  961.             /* Now we have a keyword terminated with a K/VDELIM */
  962.             if (c==VDELIM) {
  963.                   /* try to find closing KDELIM, and replace value */
  964.                   tlim = keyval.string + keyval.size;
  965.                   for (;;) {
  966.                       if (ds) {
  967.                     GETC(frew, c);
  968.                       } else
  969.                     cachegeteof(c, goto keystring_eof;);
  970.                       if (c=='\n' || c==KDELIM)
  971.                     break;
  972.                       *tp++ =c;
  973.                       if (tlim <= tp)
  974.                       tp = bufenlarge(&keyval, &tlim);
  975.                       if (c==SDELIM && ds) { /*skip next SDELIM */
  976.                         GETC(frew, c);
  977.                         if (c != SDELIM) {
  978.                             /* end of string before closing KDELIM or newline */
  979.                             nextc = c;
  980.                             goto keystring_eof;
  981.                         }
  982.                       }
  983.                   }
  984.                   if (c!=KDELIM) {
  985.                     /* couldn't find closing KDELIM -- give up */
  986.                     *tp = 0;
  987.                     aputs(keyval.string, out);
  988.                     continue;   /* last c handled properly */
  989.                   }
  990.             }
  991.             /* now put out the new keyword value */
  992.             keyreplace(matchresult,delta,out);
  993.             e = 1;
  994.             break;
  995.                 }
  996.         break;
  997.         }
  998.         }
  999.  
  1000.     keystring_eof:
  1001.     *tp = 0;
  1002.     aputs(keyval.string, out);
  1003.     uncache_exit:
  1004.     uncache(infile);
  1005.     return r + e;
  1006. }
  1007.  
  1008.  
  1009. char const ciklog[ciklogsize] = "checked in with -k by ";
  1010.  
  1011.     static void
  1012. keyreplace(marker,delta,out)
  1013.     enum markers marker;
  1014.     register struct hshentry const *delta;
  1015.     register FILE *out;
  1016. /* function: outputs the keyword value(s) corresponding to marker.
  1017.  * Attributes are derived from delta.
  1018.  */
  1019. {
  1020.     register char const *sp, *cp, *date;
  1021.     register char c;
  1022.     register size_t cs, cw, ls;
  1023.     char const *sp1;
  1024.     char datebuf[datesize];
  1025.     int RCSv;
  1026.  
  1027.     sp = Keyword[(int)marker];
  1028.  
  1029.     if (Expand == KEY_EXPAND) {
  1030.         aprintf(out, "%c%s%c", KDELIM, sp, KDELIM);
  1031.         return;
  1032.     }
  1033.  
  1034.         date= delta->date;
  1035.     RCSv = RCSversion;
  1036.  
  1037.     if (Expand == KEYVAL_EXPAND  ||  Expand == KEYVALLOCK_EXPAND)
  1038.         aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM,
  1039.             marker==Log && RCSv<VERSION(5)  ?  '\t'  :  ' '
  1040.         );
  1041.  
  1042.         switch (marker) {
  1043.         case Author:
  1044.         aputs(delta->author, out);
  1045.                 break;
  1046.         case Date:
  1047.         aputs(date2str(date,datebuf), out);
  1048.                 break;
  1049.         case Id:
  1050.     case Header:
  1051.         aprintf(out, "%s %s %s %s %s",
  1052.               marker==Id || RCSv<VERSION(4)
  1053.             ? basename(RCSfilename)
  1054.             : getfullRCSname(),
  1055.             delta->num,
  1056.             date2str(date, datebuf),
  1057.             delta->author,
  1058.               RCSv==VERSION(3) && delta->lockedby ? "Locked"
  1059.             : delta->state
  1060.         );
  1061.         if (delta->lockedby!=nil)
  1062.             if (VERSION(5) <= RCSv) {
  1063.             if (locker_expansion || Expand==KEYVALLOCK_EXPAND)
  1064.                 aprintf(out, " %s", delta->lockedby);
  1065.             } else if (RCSv == VERSION(4))
  1066.             aprintf(out, " Locker: %s", delta->lockedby);
  1067.                 break;
  1068.         case Locker:
  1069.         if (delta->lockedby)
  1070.             if (
  1071.                 locker_expansion
  1072.             ||    Expand == KEYVALLOCK_EXPAND
  1073.             ||    RCSv <= VERSION(4)
  1074.             )
  1075.             aputs(delta->lockedby, out);
  1076.                 break;
  1077.         case Log:
  1078.         case RCSfile:
  1079.         aputs(basename(RCSfilename), out);
  1080.                 break;
  1081.         case Revision:
  1082.         aputs(delta->num, out);
  1083.                 break;
  1084.         case Source:
  1085.         aputs(getfullRCSname(), out);
  1086.                 break;
  1087.         case State:
  1088.         aputs(delta->state, out);
  1089.                 break;
  1090.     default:
  1091.         break;
  1092.         }
  1093.     if (Expand == KEYVAL_EXPAND  ||  Expand == KEYVALLOCK_EXPAND) {
  1094.         afputc(' ', out);
  1095.         afputc(KDELIM, out);
  1096.     }
  1097.     if (marker == Log) {
  1098.         sp = delta->log.string;
  1099.         ls = delta->log.size;
  1100.         if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
  1101.             return;
  1102.         afputc('\n', out);
  1103.         cp = Comment.string;
  1104.         cw = cs = Comment.size;
  1105.         awrite(cp, cs, out);
  1106.         /* oddity: 2 spaces between date and time, not 1 as usual */
  1107.         sp1 = strchr(date2str(date,datebuf), ' ');
  1108.         aprintf(out, "Revision %s  %.*s %s  %s",
  1109.             delta->num, (int)(sp1-datebuf), datebuf, sp1, delta->author
  1110.         );
  1111.         /* Do not include state: it may change and is not updated.  */
  1112.         /* Comment is the comment leader.  */
  1113.         if (VERSION(5) <= RCSv)
  1114.             for (;  cw && (cp[cw-1]==' ' || cp[cw-1]=='\t');  --cw)
  1115.             ;
  1116.         for (;;) {
  1117.             afputc('\n', out);
  1118.             awrite(cp, cw, out);
  1119.             if (!ls)
  1120.             break;
  1121.             --ls;
  1122.             c = *sp++;
  1123.             if (c != '\n') {
  1124.             awrite(cp+cw, cs-cw, out);
  1125.             do {
  1126.                 afputc(c,out);
  1127.                 if (!ls)
  1128.                 break;
  1129.                 --ls;
  1130.                 c = *sp++;
  1131.             } while (c != '\n');
  1132.             }
  1133.         }
  1134.     }
  1135. }
  1136.  
  1137. #if has_readlink
  1138.     static int
  1139. resolve_symlink(L)
  1140.     struct buf *L;
  1141. /*
  1142.  * If L is a symbolic link, resolve it to the name that it points to.
  1143.  * If unsuccessful, set errno and yield -1.
  1144.  * If it points to an existing file, yield 1.
  1145.  * Otherwise, set errno=ENOENT and yield 0.
  1146.  */
  1147. {
  1148.     char *b, a[SIZEABLE_PATH];
  1149.     int e;
  1150.     size_t s;
  1151.     ssize_t r;
  1152.     struct buf bigbuf;
  1153.     unsigned linkcount = MAXSYMLINKS + 1;
  1154.  
  1155.     b = a;
  1156.     s = sizeof(a);
  1157.     bufautobegin(&bigbuf);
  1158.     while ((r = readlink(L->string,b,s))  !=  -1)
  1159.         if (r == s) {
  1160.         bufalloc(&bigbuf, s<<1);
  1161.         b = bigbuf.string;
  1162.         s = bigbuf.size;
  1163.         } else if (!--linkcount) {
  1164.         errno = ELOOP;
  1165.         return -1;
  1166.         } else {
  1167.         /* Splice symbolic link into L.  */
  1168.         b[r] = '\0';
  1169.         L->string[ROOTPATH(b) ? (size_t)0 : dirlen(L->string)]  =  '\0';
  1170.         bufscat(L, b);
  1171.         }
  1172.     e = errno;
  1173.     bufautoend(&bigbuf);
  1174.     errno = e;
  1175.     switch (e) {
  1176.         case ENXIO:
  1177.         case EINVAL: return 1;
  1178.         case ENOENT: return 0;
  1179.         default: return -1;
  1180.     }
  1181. }
  1182. #endif
  1183.  
  1184.     RILE *
  1185. rcswriteopen(RCSbuf, status, mustread)
  1186.     struct buf *RCSbuf;
  1187.     struct stat *status;
  1188.     int mustread;
  1189. /*
  1190.  * Create the lock file corresponding to RCSNAME.
  1191.  * Then try to open RCSNAME for reading and yield its FILE* descriptor.
  1192.  * Put its status into *STATUS too.
  1193.  * MUSTREAD is true if the file must already exist, too.
  1194.  * If all goes well, discard any previously acquired locks,
  1195.  * and set frewrite to the FILE* descriptor of the lock file,
  1196.  * which will eventually turn into the new RCS file.
  1197.  */
  1198. {
  1199.     register char *tp;
  1200.     register char const *sp, *RCSname, *x;
  1201.     RILE *f;
  1202.     size_t l;
  1203.     int e, exists, fdesc, previouslock, r;
  1204.     struct buf *dirt;
  1205.     struct stat statbuf;
  1206.  
  1207.     previouslock  =  frewrite != 0;
  1208.     exists =
  1209. #        if has_readlink
  1210.             resolve_symlink(RCSbuf);
  1211. #        else
  1212.                 stat(RCSbuf->string, &statbuf) == 0  ?  1
  1213.             :   errno==ENOENT ? 0 : -1;
  1214. #        endif
  1215.     if (exists < (mustread|previouslock))
  1216.         /*
  1217.          * There's an unusual problem with the RCS file;
  1218.          * or the RCS file doesn't exist,
  1219.          * and we must read or we already have a lock elsewhere.
  1220.          */
  1221.         return 0;
  1222.  
  1223.     RCSname = RCSbuf->string;
  1224.     sp = basename(RCSname);
  1225.     l = sp - RCSname;
  1226.     dirt = &dirtfname[previouslock];
  1227.     bufscpy(dirt, RCSname);
  1228.     tp = dirt->string + l;
  1229.     x = rcssuffix(RCSname);
  1230. #    if has_readlink
  1231.         if (!x) {
  1232.         error("symbolic link to non RCS filename `%s'", RCSname);
  1233.         errno = EINVAL;
  1234.         return 0;
  1235.         }
  1236. #    endif
  1237.     if (*sp == *x) {
  1238.         error("RCS filename `%s' incompatible with suffix `%s'", sp, x);
  1239.         errno = EINVAL;
  1240.         return 0;
  1241.     }
  1242.     /* Create a lock file whose name is a function of the RCS filename.  */
  1243.     if (*x) {
  1244.         /*
  1245.          * The suffix is nonempty.
  1246.          * The lock filename is the first char of of the suffix,
  1247.          * followed by the RCS filename with last char removed.  E.g.:
  1248.          *    foo,v    RCS filename with suffix ,v
  1249.          *    ,foo,    lock filename
  1250.          */
  1251.         *tp++ = *x;
  1252.         while (*sp)
  1253.             *tp++ = *sp++;
  1254.         *--tp = 0;
  1255.     } else {
  1256.         /*
  1257.          * The suffix is empty.
  1258.          * The lock filename is the RCS filename
  1259.          * with last char replaced by '_'.
  1260.          */
  1261.         while ((*tp++ = *sp++))
  1262.             ;
  1263.         tp -= 2;
  1264.         if (*tp == '_') {
  1265.             error("RCS filename `%s' ends with `%c'", RCSname, *tp);
  1266.             errno = EINVAL;
  1267.             return 0;
  1268.         }
  1269.         *tp = '_';
  1270.     }
  1271.  
  1272.     sp = tp = dirt->string;
  1273.  
  1274.     f = 0;
  1275.  
  1276.     /*
  1277.     * good news:
  1278.     *    open(f, O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) is atomic
  1279.     *    according to Posix 1003.1-1990.
  1280.     * bad news:
  1281.     *    NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
  1282.     * good news:
  1283.     *    (O_TRUNC,READONLY) normally guarantees atomicity even with NFS.
  1284.     * bad news:
  1285.     *    If you're root, (O_TRUNC,READONLY) doesn't guarantee atomicity.
  1286.     * good news:
  1287.     *    Root-over-the-wire NFS access is rare for security reasons.
  1288.     *    This bug has never been reported in practice with RCS.
  1289.     * So we don't worry about this bug.
  1290.     *
  1291.     * An even rarer NFS bug can occur when clients retry requests.
  1292.     * Suppose client A renames the lock file ",f," to "f,v"
  1293.     * at about the same time that client B creates ",f,",
  1294.     * and suppose A's first rename request is delayed, so A reissues it.
  1295.     * The sequence of events might be:
  1296.     *    A sends rename(",f,", "f,v")
  1297.     *    B sends create(",f,")
  1298.     *    A sends retry of rename(",f,", "f,v")
  1299.     *    server receives, does, and acknowledges A's first rename()
  1300.     *    A receives acknowledgment, and its RCS program exits
  1301.     *    server receives, does, and acknowledges B's create()
  1302.     *    server receives, does, and acknowledges A's retry of rename()
  1303.     * This not only wrongly deletes B's lock, it removes the RCS file!
  1304.     * Most NFS implementations have idempotency caches that usually prevent
  1305.     * this scenario, but such caches are finite and can be overrun.
  1306.     * This problem afflicts programs that use the traditional
  1307.     * Unix method of using link() and unlink() to get and release locks,
  1308.     * as well as RCS's method of using open() and rename().
  1309.     * There is no easy workaround for either link-unlink or open-rename.
  1310.     * Any new method based on lockf() seemingly would be incompatible with
  1311.     * the old methods; besides, lockf() is notoriously buggy under NFS.
  1312.     * Since this problem afflicts scads of Unix programs, but is so rare
  1313.     * that nobody seems to be worried about it, we won't worry either.
  1314.     */
  1315. #    define READONLY (S_IRUSR|S_IRGRP|S_IROTH)
  1316. #    if !open_can_creat
  1317. #        define create(f) creat(f, READONLY)
  1318. #    else
  1319. #        define create(f) open(f, O_BINARY|O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY)
  1320. #    endif
  1321.  
  1322.     catchints();
  1323.     ignoreints();
  1324.  
  1325.     /*
  1326.      * Create a lock file for an RCS file.  This should be atomic, i.e.
  1327.      * if two processes try it simultaneously, at most one should succeed.
  1328.      */
  1329.     seteid();
  1330.     fdesc = create(sp);
  1331.     e = errno;
  1332.     setrid();
  1333.  
  1334.     if (fdesc < 0) {
  1335.         if (e == EACCES  &&  stat(tp,&statbuf) == 0)
  1336.             /* The RCS file is busy.  */
  1337.             e = EEXIST;
  1338.     } else {
  1339.         dirtfmaker[0] = effective;
  1340.         e = ENOENT;
  1341.         if (exists) {
  1342.             f = Iopen(RCSname, FOPEN_R, status);
  1343.             e = errno;
  1344.             if (f && previouslock) {
  1345.             /* Discard the previous lock in favor of this one.  */
  1346.             Ozclose(&frewrite);
  1347.             seteid();
  1348.             if ((r = un_link(newRCSfilename)) != 0)
  1349.                 e = errno;
  1350.             setrid();
  1351.             if (r != 0)
  1352.                 enfaterror(e, newRCSfilename);
  1353.             bufscpy(&dirtfname[0], tp);
  1354.             }
  1355.         }
  1356.         if (!(frewrite = fdopen(fdesc, FOPEN_W))) {
  1357.             efaterror(newRCSfilename);
  1358.         }
  1359.     }
  1360.  
  1361.     restoreints();
  1362.  
  1363.     errno = e;
  1364.     return f;
  1365. }
  1366.  
  1367.     void
  1368. keepdirtemp(name)
  1369.     char const *name;
  1370. /* Do not unlink name, either because it's not there any more,
  1371.  * or because it has already been unlinked.
  1372.  */
  1373. {
  1374.     register int i;
  1375.     for (i=DIRTEMPNAMES; 0<=--i; )
  1376.         if (dirtfname[i].string == name) {
  1377.             dirtfmaker[i] = notmade;
  1378.             return;
  1379.         }
  1380.     faterror("keepdirtemp");
  1381. }
  1382.  
  1383.     char const *
  1384. makedirtemp(name, n)
  1385.     register char const *name;
  1386.     int n;
  1387. /*
  1388.  * Have maketemp() do all the work if name is null.
  1389.  * Otherwise, create a unique filename in name's dir using n and name
  1390.  * and store it into the dirtfname[n].
  1391.  * Because of storage in tfnames, dirtempunlink() can unlink the file later.
  1392.  * Return a pointer to the filename created.
  1393.  */
  1394. {
  1395.     register char *tp, *np;
  1396.     register size_t dl;
  1397.     register struct buf *bn;
  1398.  
  1399.     if (!name)
  1400.         return maketemp(n);
  1401.     dl = dirlen(name);
  1402.     bn = &dirtfname[n];
  1403.     bufalloc(bn,
  1404. #        if has_mktemp
  1405.             dl + 9
  1406. #        else
  1407.             strlen(name) + 3
  1408. #        endif
  1409.     );
  1410.     bufscpy(bn, name);
  1411.     np = tp = bn->string;
  1412.     tp += dl;
  1413.     *tp++ = '_';
  1414.     *tp++ = '0'+n;
  1415.     catchints();
  1416. #    if has_mktemp
  1417.         VOID strcpy(tp, "XXXXXX");
  1418.         if (!mktemp(np) || !*np)
  1419.             faterror("can't make temporary file name `%.*s%c_%cXXXXXX'",
  1420.             (int)dl, name, SLASH, '0'+n
  1421.             );
  1422. #    else
  1423.         /*
  1424.          * Posix 1003.1-1990 has no reliable way
  1425.          * to create a unique file in a named directory.
  1426.          * We fudge here.  If the working file name is abcde,
  1427.          * the temp filename is _Ncde where N is a digit.
  1428.          */
  1429.         name += dl;
  1430.         if (*name) name++;
  1431.         if (*name) name++;
  1432.         VOID strcpy(tp, name);
  1433. #    endif
  1434.     dirtfmaker[n] = real;
  1435.     return np;
  1436. }
  1437.  
  1438.     void
  1439. dirtempunlink()
  1440. /* Clean up makedirtemp() files.  May be invoked by signal handler. */
  1441. {
  1442.     register int i;
  1443.     enum maker m;
  1444.  
  1445.     for (i = DIRTEMPNAMES;  0 <= --i;  )
  1446.         if ((m = dirtfmaker[i]) != notmade) {
  1447.         if (m == effective)
  1448.             seteid();
  1449.         VOID un_link(dirtfname[i].string);
  1450.         if (m == effective)
  1451.             setrid();
  1452.         dirtfmaker[i] = notmade;
  1453.         }
  1454. }
  1455.  
  1456.  
  1457.     int
  1458. #if has_prototypes
  1459. chnamemod(FILE **fromp, char const *from, char const *to, mode_t mode)
  1460.   /* The `#if has_prototypes' is needed because mode_t might promote to int.  */
  1461. #else
  1462.   chnamemod(fromp,from,to,mode) FILE **fromp; char const *from,*to; mode_t mode;
  1463. #endif
  1464. /*
  1465.  * Rename a file (with optional stream pointer *FROMP) from FROM to TO.
  1466.  * FROM already exists.
  1467.  * Change its mode to MODE, before renaming if possible.
  1468.  * If FROMP, close and clear *FROMP before renaming it.
  1469.  * Unlink TO if it already exists.
  1470.  * Return -1 on error (setting errno), 0 otherwise.
  1471.  */
  1472. {
  1473. #    if bad_a_rename
  1474.         /*
  1475.          * This host is brain damaged.  A race condition is possible
  1476.          * while the lock file is temporarily writable.
  1477.          * There doesn't seem to be a workaround.
  1478.          */
  1479.         mode_t mode_while_renaming = mode|S_IWUSR;
  1480. #    else
  1481. #        define mode_while_renaming mode
  1482. #    endif
  1483.     if (fromp) {
  1484. #        if has_fchmod
  1485.             if (fchmod(fileno(*fromp), mode_while_renaming) != 0)
  1486.                 return -1;
  1487. #        endif
  1488.         Ozclose(fromp);
  1489.     }
  1490. #    if has_fchmod
  1491.         else
  1492. #    endif
  1493.         if (chmod(from, mode_while_renaming) != 0)
  1494.         return -1;
  1495.  
  1496. #    if !has_rename || bad_b_rename
  1497.         VOID un_link(to);
  1498.         /*
  1499.          * We need not check the result;
  1500.          * link() or rename() will catch it.
  1501.          * No harm is done if TO does not exist.
  1502.          * However, there's a short window of inconsistency
  1503.          * during which TO does not exist.
  1504.          */
  1505. #    endif
  1506.  
  1507.     return
  1508. #        if !has_rename
  1509.         do_link(from,to) != 0  ?  -1  :  un_link(from)
  1510. #        else
  1511.             rename(from, to) != 0
  1512. #            if has_NFS
  1513.             && errno != ENOENT
  1514. #            endif
  1515.         ?  -1
  1516. #        if bad_a_rename
  1517.         :  mode != mode_while_renaming  ?  chmod(to, mode)
  1518. #        endif
  1519.         :  0
  1520. #        endif
  1521.     ;
  1522.  
  1523. #    undef mode_while_renaming
  1524. }
  1525.  
  1526.  
  1527.  
  1528.     int
  1529. findlock(delete, target)
  1530.     int delete;
  1531.     struct hshentry **target;
  1532. /*
  1533.  * Find the first lock held by caller and return a pointer
  1534.  * to the locked delta; also removes the lock if DELETE.
  1535.  * If one lock, put it into *TARGET.
  1536.  * Return 0 for no locks, 1 for one, 2 for two or more.
  1537.  */
  1538. {
  1539.     register struct lock *next, **trail, **found;
  1540.  
  1541.     found = 0;
  1542.     for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
  1543.         if (strcmp(getcaller(), next->login)  ==  0) {
  1544.             if (found) {
  1545.                 error("multiple revisions locked by %s; please specify one", getcaller());
  1546.                 return 2;
  1547.             }
  1548.             found = trail;
  1549.         }
  1550.     if (!found)
  1551.         return 0;
  1552.     next = *found;
  1553.     *target = next->delta;
  1554.     if (delete) {
  1555.         next->delta->lockedby = nil;
  1556.         *found = next->nextlock;
  1557.     }
  1558.     return 1;
  1559. }
  1560.  
  1561.     int
  1562. addlock(delta)
  1563.     struct hshentry * delta;
  1564. /*
  1565.  * Add a lock held by caller to DELTA and yield 1 if successful.
  1566.  * Print an error message and yield -1 if no lock is added because
  1567.  * DELTA is locked by somebody other than caller.
  1568.  * Return 0 if the caller already holds the lock.
  1569.  */
  1570. {
  1571.     register struct lock *next;
  1572.  
  1573.     next=Locks;
  1574.     for (next = Locks;  next;  next = next->nextlock)
  1575.         if (cmpnum(delta->num, next->delta->num) == 0)
  1576.             if (strcmp(getcaller(), next->login) == 0)
  1577.                 return 0;
  1578.             else {
  1579.                 error("revision %s already locked by %s",
  1580.                       delta->num, next->login
  1581.                 );
  1582.                 return -1;
  1583.             }
  1584.     next = ftalloc(struct lock);
  1585.     delta->lockedby = next->login = getcaller();
  1586.     next->delta = delta;
  1587.     next->nextlock = Locks;
  1588.     Locks = next;
  1589.     return 1;
  1590. }
  1591.  
  1592.  
  1593.     int
  1594. addsymbol(num, name, rebind)
  1595.     char const *num, *name;
  1596.     int rebind;
  1597. /*
  1598.  * Associate with revision NUM the new symbolic NAME.
  1599.  * If NAME already exists and REBIND is set, associate NAME with NUM;
  1600.  * otherwise, print an error message and return false;
  1601.  * Return true if successful.
  1602.  */
  1603. {
  1604.     register struct assoc *next;
  1605.  
  1606.     for (next = Symbols;  next;  next = next->nextassoc)
  1607.         if (strcmp(name, next->symbol)  ==  0)
  1608.             if (rebind  ||  strcmp(next->num,num) == 0) {
  1609.                 next->num = num;
  1610.                 return true;
  1611.             } else {
  1612.                 error("symbolic name %s already bound to %s",
  1613.                     name, next->num
  1614.                 );
  1615.                 return false;
  1616.             }
  1617.     next = ftalloc(struct assoc);
  1618.     next->symbol = name;
  1619.     next->num = num;
  1620.     next->nextassoc = Symbols;
  1621.     Symbols = next;
  1622.     return true;
  1623. }
  1624.  
  1625.  
  1626.  
  1627.     char const *
  1628. getcaller()
  1629. /* Get the caller's login name.  */
  1630. {
  1631. #    if has_setuid
  1632.         return getusername(euid()!=ruid());
  1633. #    else
  1634.         return getusername(false);
  1635. #    endif
  1636. }
  1637.  
  1638.  
  1639.     int
  1640. checkaccesslist()
  1641. /*
  1642.  * Return true if caller is the superuser, the owner of the
  1643.  * file, the access list is empty, or caller is on the access list.
  1644.  * Otherwise, print an error message and return false.
  1645.  */
  1646. {
  1647.     register struct access const *next;
  1648.  
  1649.     if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
  1650.         return true;
  1651.  
  1652.     next = AccessList;
  1653.     do {
  1654.         if (strcmp(getcaller(), next->login)  ==  0)
  1655.             return true;
  1656.     } while ((next = next->nextaccess));
  1657.  
  1658.     error("user %s not on the access list", getcaller());
  1659.     return false;
  1660. }
  1661.  
  1662.  
  1663.     int
  1664. dorewrite(lockflag, changed)
  1665.     int lockflag, changed;
  1666. /*
  1667.  * Do nothing if LOCKFLAG is zero.
  1668.  * Prepare to rewrite an RCS file if CHANGED is positive.
  1669.  * Stop rewriting if CHANGED is zero, because there won't be any changes.
  1670.  * Fail if CHANGED is negative.
  1671.  * Return true on success.
  1672.  */
  1673. {
  1674.     int r, e;
  1675.  
  1676.     if (lockflag)
  1677.         if (changed) {
  1678.             if (changed < 0)
  1679.                 return false;
  1680.             putadmin(frewrite);
  1681.             puttree(Head, frewrite);
  1682.             aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
  1683.             foutptr = frewrite;
  1684.         } else {
  1685.             Ozclose(&frewrite);
  1686.             seteid();
  1687.             ignoreints();
  1688.             r = un_link(newRCSfilename);
  1689.             e = errno;
  1690.             keepdirtemp(newRCSfilename);
  1691.             restoreints();
  1692.             setrid();
  1693.             if (r != 0) {
  1694.                 enerror(e, RCSfilename);
  1695.                 return false;
  1696.             }
  1697.         }
  1698.     return true;
  1699. }
  1700.  
  1701.     int
  1702. donerewrite(changed)
  1703.     int changed;
  1704. /*
  1705.  * Finish rewriting an RCS file if CHANGED is nonzero.
  1706.  * Return true on success.
  1707.  */
  1708. {
  1709.     int r, e;
  1710.  
  1711.     if (changed && !nerror) {
  1712.         if (finptr) {
  1713.             fastcopy(finptr, frewrite);
  1714.             Izclose(&finptr);
  1715.         }
  1716.         if (1 < RCSstat.st_nlink)
  1717.             warn("breaking hard link to %s", RCSfilename);
  1718.         seteid();
  1719.         ignoreints();
  1720.         r = chnamemod(&frewrite, newRCSfilename, RCSfilename,
  1721.             RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH)
  1722.         );
  1723.         e = errno;
  1724.         keepdirtemp(newRCSfilename);
  1725.         restoreints();
  1726.         setrid();
  1727.         if (r != 0) {
  1728.             enerror(e, RCSfilename);
  1729.             error("saved in %s", newRCSfilename);
  1730.             dirtempunlink();
  1731.             return false;
  1732.         }
  1733.     }
  1734.     return true;
  1735. }
  1736.  
  1737.     void
  1738. aflush(f)
  1739.     FILE *f;
  1740. {
  1741.     if (fflush(f) != 0)
  1742.         Oerror();
  1743. }
  1744. @
  1745.  
  1746.  
  1747. 5.11.1.1
  1748. log
  1749. @Start of the AMIGA port of RCS 5.6. I call it HWGRCS now ;^)
  1750. @
  1751. text
  1752. @d2 1
  1753. a2 1
  1754.  *               RCS stream editor
  1755. d5 3
  1756. a7 3
  1757.  *             edits the input file according to a
  1758.  *             script from stdin, generated by diff -n
  1759.  *             performs keyword expansion
  1760. d39 1
  1761. a39 1
  1762.  * Revision 5.11  1991/11/03  01:11:44    eggert
  1763. d42 1
  1764. a42 1
  1765.  * Revision 5.10  1991/10/07  17:32:46    eggert
  1766. d55 1
  1767. a55 1
  1768.  * Fix setuid bug.  Support new link behavior.    Work around broken "w+" fopen.
  1769. d85 1
  1770. a85 1
  1771.  *
  1772. d88 1
  1773. a88 1
  1774.  *
  1775. d91 1
  1776. a91 1
  1777.  *
  1778. d96 1
  1779. a96 1
  1780.  *
  1781. d100 1
  1782. a100 1
  1783.  *
  1784. d102 1
  1785. a102 1
  1786.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
  1787. d104 1
  1788. a104 1
  1789.  *
  1790. d111 1
  1791. a111 1
  1792.  *
  1793. d114 1
  1794. a114 1
  1795.  *
  1796. d119 1
  1797. a119 1
  1798.  *
  1799. d124 1
  1800. a124 1
  1801.  *
  1802. d128 1
  1803. a128 1
  1804.  *
  1805. d171 2
  1806. a172 2
  1807.            /*used to correct editline in case file is not rewound after */
  1808.            /* applying one delta                        */
  1809. d444 1
  1810. a444 1
  1811.     tmpptr=editfile; editfile=resultfile; resultfile=tmpptr;
  1812. d474 1
  1813. a474 1
  1814.         if (delta!=nil) {
  1815. d477 1
  1816. a477 1
  1817.         } else {
  1818. d479 1
  1819. a479 1
  1820.         }
  1821. d481 1
  1822. a481 1
  1823.     }
  1824. d508 1
  1825. a508 1
  1826.         /* swap files */
  1827. d510 2
  1828. a511 2
  1829.         /* assumes edit only during last pass, from the beginning*/
  1830.     }
  1831. d589 1
  1832. a589 1
  1833.         }
  1834. d591 1
  1835. a591 1
  1836.     }
  1837. d681 2
  1838. a682 2
  1839.     int ed; /* editor command */
  1840.     register int c;
  1841. d697 1
  1842. a697 1
  1843.     editline += linecorr; linecorr=0; /*correct line number*/
  1844. d710 1
  1845. a710 1
  1846.             /* skip over unwanted lines */
  1847. d719 1
  1848. a719 1
  1849.                 /*skip next line*/
  1850. d775 1
  1851. a775 1
  1852.         }
  1853. d807 1
  1854. a807 1
  1855.     enum markers matchresult;
  1856. d817 1
  1857. a817 1
  1858.     for (;;) {
  1859. d828 2
  1860. a829 2
  1861.                 /* end of string */
  1862.                 nextc=c;
  1863. d847 2
  1864. a848 2
  1865.             /* check for keyword */
  1866.             /* first, copy a long enough string into keystring */
  1867. d865 1
  1868. a865 1
  1869.             }
  1870. d901 1
  1871. a901 1
  1872.                     continue;    /* last c handled properly */
  1873. d908 1
  1874. a908 1
  1875.         }
  1876. d911 1
  1877. a911 1
  1878.     }
  1879. d947 1
  1880. a947 1
  1881.     date= delta->date;
  1882. d955 2
  1883. a956 2
  1884.     switch (marker) {
  1885.     case Author:
  1886. d958 2
  1887. a959 2
  1888.         break;
  1889.     case Date:
  1890. d961 2
  1891. a962 2
  1892.         break;
  1893.     case Id:
  1894. d980 2
  1895. a981 2
  1896.         break;
  1897.     case Locker:
  1898. d989 3
  1899. a991 3
  1900.         break;
  1901.     case Log:
  1902.     case RCSfile:
  1903. d993 2
  1904. a994 2
  1905.         break;
  1906.     case Revision:
  1907. d996 2
  1908. a997 2
  1909.         break;
  1910.     case Source:
  1911. d999 2
  1912. a1000 2
  1913.         break;
  1914.     case State:
  1915. d1002 1
  1916. a1002 1
  1917.         break;
  1918. d1005 1
  1919. a1005 1
  1920.     }
  1921. d1239 1
  1922. a1239 1
  1923.      * Create a lock file for an RCS file.    This should be atomic, i.e.
  1924. a1247 7
  1925. #ifdef AMIGA
  1926. /* Workaround for a bug in SAS/C 6.1!! */
  1927.         if(!e)
  1928.         {
  1929.             e = EACCES;
  1930.         } /* if */
  1931. #endif
  1932. a1361 11
  1933. #ifdef AMIGA
  1934.         /* Could this be a still open frewrite file? */
  1935.         if((i == 0) && (frewrite != NULL))
  1936.         {
  1937.             /* This should be Ozclose, but Ozclose does
  1938.                too much error checking for this. This should be
  1939.                just a try to get rid of this file, too! */
  1940.             fclose(frewrite);
  1941.             frewrite = NULL;
  1942.         } /* if */
  1943. #endif
  1944. d1388 1
  1945. a1388 1
  1946.          * This host is brain damaged.    A race condition is possible
  1947. d1430 1
  1948. a1430 1
  1949.         :  mode != mode_while_renaming    ?  chmod(to, mode)
  1950. @
  1951.  
  1952.  
  1953. 5.11.1.2
  1954. log
  1955. @Removed the SAS/C create patch, as the problem has been fixed for 6.3.
  1956. @
  1957. text
  1958. @d2 1
  1959. a2 1
  1960.  *                     RCS stream editor
  1961. d5 3
  1962. a7 3
  1963.  *                       edits the input file according to a
  1964.  *                       script from stdin, generated by diff -n
  1965.  *                       performs keyword expansion
  1966. d39 1
  1967. a39 4
  1968.  * Revision 5.11.1.1  1993/01/18  14:38:13  heinz
  1969.  * Start of the AMIGA port of RCS 5.6. I call it HWGRCS now ;^)
  1970.  *
  1971.  * Revision 5.11  1991/11/03  01:11:44  eggert
  1972. d42 1
  1973. a42 1
  1974.  * Revision 5.10  1991/10/07  17:32:46  eggert
  1975. d55 1
  1976. a55 1
  1977.  * Fix setuid bug.  Support new link behavior.  Work around broken "w+" fopen.
  1978. d157 1
  1979. a157 1
  1980. libId(editId, "$Id: rcsedit.c,v 5.11.1.1 1993/01/18 14:38:13 heinz Exp heinz $")
  1981. d162 3
  1982. a164 3
  1983. FILE *fcopy;             /* result file descriptor                          */
  1984. char const *resultfile;  /* result file name                                */
  1985. int locker_expansion;    /* should the locker name be appended to Id val?   */
  1986. d166 2
  1987. a167 2
  1988.         static RILE *fedit; /* edit file descriptor */
  1989.         static char const *editfile; /* edit pathname */
  1990. d170 3
  1991. a172 3
  1992. static long linecorr; /* #adds - #deletes in each edit run.                 */
  1993.                /*used to correct editline in case file is not rewound after */
  1994.                /* applying one delta                                        */
  1995. d176 2
  1996. a177 2
  1997. struct buf dirtfname[DIRTEMPNAMES];             /* unlink these when done */
  1998. static enum maker volatile dirtfmaker[DIRTEMPNAMES];    /* if these are set */
  1999. d181 1
  2000. a181 1
  2001.         int
  2002. d183 1
  2003. a183 1
  2004.         char const *s;
  2005. d189 19
  2006. a207 19
  2007. #       if bad_unlink
  2008.                 int e;
  2009.                 if (unlink(s) == 0)
  2010.                         return 0;
  2011.                 e = errno;
  2012. #               if has_NFS
  2013.                         if (e == ENOENT)
  2014.                                 return 0;
  2015. #               endif
  2016.                 if (chmod(s, S_IWUSR) != 0) {
  2017.                         errno = e;
  2018.                         return -1;
  2019.                 }
  2020. #       endif
  2021. #       if has_NFS
  2022.                 return unlink(s)==0 || errno==ENOENT  ?  0  :  -1;
  2023. #       else
  2024.                 return unlink(s);
  2025. #       endif
  2026. d213 1
  2027. a213 1
  2028. #       define do_link(s,t) link(s,t)
  2029. d215 1
  2030. a215 1
  2031.         static int
  2032. d217 1
  2033. a217 1
  2034.         char const *s, *t;
  2035. d220 1
  2036. a220 1
  2037.         struct stat sb, tb;
  2038. d222 13
  2039. a234 13
  2040.         if (link(s,t) == 0)
  2041.                 return 0;
  2042.         if (errno != EEXIST)
  2043.                 return -1;
  2044.         if (
  2045.             stat(s, &sb) == 0  &&
  2046.             stat(t, &tb) == 0  &&
  2047.             sb.st_ino == tb.st_ino  &&
  2048.             sb.st_dev == tb.st_dev
  2049.         )
  2050.                 return 0;
  2051.         errno = EEXIST;
  2052.         return -1;
  2053. d240 1
  2054. a240 1
  2055.         static exiting void
  2056. d243 1
  2057. a243 1
  2058.         fatserror("edit script ends prematurely");
  2059. d246 1
  2060. a246 1
  2061.         static exiting void
  2062. d249 1
  2063. a249 1
  2064.         fatserror("edit script refers to line past end of file");
  2065. d256 1
  2066. a256 1
  2067. #       define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
  2068. d258 1
  2069. a258 1
  2070.         static void
  2071. d260 15
  2072. a274 15
  2073.         register Iptr_type *s1;
  2074.         register Iptr_type const *s2;
  2075.         register unsigned long n;
  2076. {
  2077.         if (s1 < s2)
  2078.                 do {
  2079.                         *s1++ = *s2++;
  2080.                 } while (--n);
  2081.         else {
  2082.                 s1 += n;
  2083.                 s2 += n;
  2084.                 do {
  2085.                         *--s1 = *--s2;
  2086.                 } while (--n);
  2087.         }
  2088. d291 1
  2089. a291 1
  2090.         static void
  2091. d293 2
  2092. a294 2
  2093.         unsigned long n;
  2094.         Iptr_type l;
  2095. d297 18
  2096. a314 18
  2097.         if (linelim-gapsize < n)
  2098.             editLineNumberOverflow();
  2099.         if (!gapsize)
  2100.             line =
  2101.                 !linelim ?
  2102.                         tnalloc(Iptr_type, linelim = gapsize = 1024)
  2103.                 : (
  2104.                         gap = gapsize = linelim,
  2105.                         trealloc(Iptr_type, line, linelim <<= 1)
  2106.                 );
  2107.         if (n < gap)
  2108.             movelines(line+n+gapsize, line+n, gap-n);
  2109.         else if (gap < n)
  2110.             movelines(line+gap, line+gap+gapsize, n-gap);
  2111.  
  2112.         line[n] = l;
  2113.         gap = n + 1;
  2114.         gapsize--;
  2115. d317 1
  2116. a317 1
  2117.         static void
  2118. d319 1
  2119. a319 1
  2120.         unsigned long n, nlines;
  2121. d322 7
  2122. a328 7
  2123.         unsigned long l = n + nlines;
  2124.         if (linelim-gapsize < l  ||  l < n)
  2125.             editLineNumberOverflow();
  2126.         if (l < gap)
  2127.             movelines(line+l+gapsize, line+l, gap-l);
  2128.         else if (gap < n)
  2129.             movelines(line+gap, line+gap+gapsize, n-gap);
  2130. d330 2
  2131. a331 2
  2132.         gap = n;
  2133.         gapsize += nlines;
  2134. d334 1
  2135. a334 1
  2136.         static void
  2137. d336 2
  2138. a337 2
  2139.         register FILE *f;
  2140.         register Iptr_type l;
  2141. d339 6
  2142. a344 6
  2143.         register int c;
  2144.         do {
  2145.                 if ((c = *l++) == SDELIM  &&  *l++ != SDELIM)
  2146.                         return;
  2147.                 aputc(c, f);
  2148.         } while (c != '\n');
  2149. d347 1
  2150. a347 1
  2151.         void
  2152. d349 1
  2153. a349 1
  2154.         FILE *f;
  2155. d352 5
  2156. a356 5
  2157.         register Iptr_type *p, *lim, *l=line;
  2158.         for (p=l, lim=l+gap;  p<lim;  )
  2159.                 snapshotline(f, *p++);
  2160.         for (p+=gapsize, lim=l+linelim;  p<lim;  )
  2161.                 snapshotline(f, *p++);
  2162. d359 1
  2163. a359 1
  2164.         static void
  2165. d361 4
  2166. a364 4
  2167.         RILE *fin;
  2168.         FILE *fout;
  2169.         Iptr_type l;
  2170.         struct hshentry const *delta;
  2171. d366 3
  2172. a368 3
  2173.         Iseek(fin, l);
  2174.         if (expandline(fin, fout, delta, true, (FILE*)0)  <  0)
  2175.                 faterror("finisheditline internal error");
  2176. d371 1
  2177. a371 1
  2178.         void
  2179. d373 3
  2180. a375 3
  2181.         struct hshentry const *delta;
  2182.         FILE *outfile;
  2183.         int done;
  2184. d381 16
  2185. a396 16
  2186.         if (done) {
  2187.                 openfcopy(outfile);
  2188.                 outfile = fcopy;
  2189.                 if (!delta)
  2190.                         snapshotedit(outfile);
  2191.                 else {
  2192.                         register Iptr_type *p, *lim, *l = line;
  2193.                         register RILE *fin = finptr;
  2194.                         Iptr_type here = Itell(fin);
  2195.                         for (p=l, lim=l+gap;  p<lim;  )
  2196.                                 finisheditline(fin, outfile, *p++, delta);
  2197.                         for (p+=gapsize, lim=l+linelim;  p<lim;  )
  2198.                                 finisheditline(fin, outfile, *p++, delta);
  2199.                         Iseek(fin, here);
  2200.                 }
  2201.         }
  2202. d406 5
  2203. a410 5
  2204. #       if bad_fopen_wplus
  2205.                 if (un_link(filename) != 0)
  2206.                         efaterror(filename);
  2207. #       endif
  2208.         return fopen(filename, FOPEN_WPLUS_WORK);
  2209. d415 1
  2210. a415 1
  2211.         void
  2212. d417 1
  2213. a417 1
  2214.         FILE *f;
  2215. d419 6
  2216. a424 6
  2217.         if (!(fcopy = f)) {
  2218.                 if (!resultfile)
  2219.                         resultfile = maketemp(2);
  2220.                 if (!(fcopy = fopen_update_truncate(resultfile)))
  2221.                         efaterror(resultfile);
  2222.         }
  2223. d430 1
  2224. a430 1
  2225.         static void
  2226. d432 1
  2227. a432 1
  2228.         FILE *outfile;
  2229. d438 1
  2230. a438 1
  2231.         char const *tmpptr;
  2232. d440 6
  2233. a445 6
  2234.         editline = 0;  linecorr = 0;
  2235.         if (fseek(fcopy, 0L, SEEK_SET) != 0)
  2236.                 Oerror();
  2237.         fedit = fcopy;
  2238.         tmpptr=editfile; editfile=resultfile; resultfile=tmpptr;
  2239.         openfcopy(outfile);
  2240. d448 1
  2241. a448 1
  2242.         void
  2243. d450 1
  2244. a450 1
  2245.         FILE *f;
  2246. d453 3
  2247. a455 3
  2248.         finishedit((struct hshentry *)nil, (FILE*)0, false);
  2249.         fastcopy(fedit, f);
  2250.         Irewind(fedit);
  2251. d458 1
  2252. a458 1
  2253.         void
  2254. d460 3
  2255. a462 3
  2256.         struct hshentry const *delta;
  2257.         FILE *outfile;
  2258.         int done;
  2259. d468 2
  2260. a469 2
  2261.         register RILE *fe;
  2262.         register FILE *fc;
  2263. d471 13
  2264. a483 13
  2265.         fe = fedit;
  2266.         if (fe) {
  2267.                 fc = fcopy;
  2268.                 if (delta!=nil) {
  2269.                         while (1 < expandline(fe,fc,delta,false,(FILE*)0))
  2270.                                 ;
  2271.                 } else {
  2272.                         fastcopy(fe,fc);
  2273.                 }
  2274.                 Ifclose(fe);
  2275.         }
  2276.         if (!done)
  2277.                 swapeditfiles(outfile);
  2278. d490 1
  2279. a490 1
  2280. #       define copylines(upto,delta) (editline = (upto))
  2281. d492 1
  2282. a492 1
  2283.         static void
  2284. d494 2
  2285. a495 2
  2286.         register unsigned long upto;
  2287.         struct hshentry const *delta;
  2288. d502 28
  2289. a529 28
  2290.         register int c;
  2291.         declarecache;
  2292.         register FILE *fc;
  2293.         register RILE *fe;
  2294.  
  2295.         if (upto < editline) {
  2296.                 /* swap files */
  2297.                 finishedit((struct hshentry *)nil, (FILE*)0, false);
  2298.                 /* assumes edit only during last pass, from the beginning*/
  2299.         }
  2300.         fe = fedit;
  2301.         fc = fcopy;
  2302.         if (editline < upto)
  2303.             if (delta)
  2304.                 do {
  2305.                         if (expandline(fe,fc,delta,false,(FILE*)0) <= 1)
  2306.                                 editLineNumberOverflow();
  2307.                 } while (++editline < upto);
  2308.             else {
  2309.                 setupcache(fe); cache(fe);
  2310.                 do {
  2311.                         do {
  2312.                                 cachegeteof(c, editLineNumberOverflow(););
  2313.                                 aputc(c, fc);
  2314.                         } while (c != '\n');
  2315.                 } while (++editline < upto);
  2316.                 uncache(fe);
  2317.             }
  2318. d535 1
  2319. a535 1
  2320.         void
  2321. d537 1
  2322. a537 1
  2323.         struct hshentry const *delta;
  2324. d544 2
  2325. a545 2
  2326.         while (1 < expandline(finptr,fcopy,delta,true,foutptr))
  2327.                 ;
  2328. d549 1
  2329. a549 1
  2330.         void
  2331. d557 35
  2332. a591 35
  2333. {       register c;
  2334.         declarecache;
  2335.         register FILE *frew, *fcop;
  2336.         register int amidline;
  2337.         register RILE *fin;
  2338.  
  2339.         fin = finptr;
  2340.         setupcache(fin); cache(fin);
  2341.         frew = foutptr;
  2342.         fcop = fcopy;
  2343.         amidline = false;
  2344.         for (;;) {
  2345.                 GETC(frew,c);
  2346.                 switch (c) {
  2347.                     case '\n':
  2348.                         ++editline;
  2349.                         ++rcsline;
  2350.                         amidline = false;
  2351.                         break;
  2352.                     case SDELIM:
  2353.                         GETC(frew,c);
  2354.                         if (c != SDELIM) {
  2355.                                 /* end of string */
  2356.                                 nextc = c;
  2357.                                 editline += amidline;
  2358.                                 uncache(fin);
  2359.                                 return;
  2360.                         }
  2361.                         /* fall into */
  2362.                     default:
  2363.                         amidline = true;
  2364.                         break;
  2365.                 }
  2366.                 aputc(c,fcop);
  2367.         }
  2368. d595 1
  2369. a595 1
  2370.         void
  2371. d600 7
  2372. a606 7
  2373.         editfile = 0;
  2374.         fedit = 0;
  2375.         editline = linecorr = 0;
  2376.         resultfile = maketemp(1);
  2377.         if (!(fcopy = fopen_update_truncate(resultfile)))
  2378.                 efaterror(resultfile);
  2379.         copystring();
  2380. d608 45
  2381. a652 45
  2382.         register int c;
  2383.         declarecache;
  2384.         register FILE *frew;
  2385.         register unsigned long e, oe;
  2386.         register int amidline, oamidline;
  2387.         register Iptr_type optr;
  2388.         register RILE *fin;
  2389.  
  2390.         e = 0;
  2391.         gap = 0;
  2392.         gapsize = linelim;
  2393.         fin = finptr;
  2394.         setupcache(fin); cache(fin);
  2395.         advise_access(fin, MADV_NORMAL);
  2396.         frew = foutptr;
  2397.         amidline = false;
  2398.         for (;;) {
  2399.                 optr = cachetell();
  2400.                 GETC(frew,c);
  2401.                 oamidline = amidline;
  2402.                 oe = e;
  2403.                 switch (c) {
  2404.                     case '\n':
  2405.                         ++e;
  2406.                         ++rcsline;
  2407.                         amidline = false;
  2408.                         break;
  2409.                     case SDELIM:
  2410.                         GETC(frew,c);
  2411.                         if (c != SDELIM) {
  2412.                                 /* end of string */
  2413.                                 nextc = c;
  2414.                                 editline = e + amidline;
  2415.                                 linecorr = 0;
  2416.                                 uncache(fin);
  2417.                                 return;
  2418.                         }
  2419.                         /* fall into */
  2420.                     default:
  2421.                         amidline = true;
  2422.                         break;
  2423.                 }
  2424.                 if (!oamidline)
  2425.                         insertline(oe, optr);
  2426.         }
  2427. d659 1
  2428. a659 1
  2429.         void
  2430. d664 1
  2431. a664 1
  2432.         struct hshentry const *delta;
  2433. d681 22
  2434. a702 22
  2435.         int ed; /* editor command */
  2436.         register int c;
  2437.         declarecache;
  2438.         register FILE *frew;
  2439. #       if !large_memory
  2440.                 register FILE *f;
  2441.                 unsigned long line_lim = ULONG_MAX;
  2442.                 register RILE *fe;
  2443. #       endif
  2444.         register unsigned long i;
  2445.         register RILE *fin;
  2446. #       if large_memory
  2447.                 register unsigned long j;
  2448. #       endif
  2449.         struct diffcmd dc;
  2450.  
  2451.         editline += linecorr; linecorr=0; /*correct line number*/
  2452.         frew = foutptr;
  2453.         fin = finptr;
  2454.         setupcache(fin);
  2455.         initdiffcmd(&dc);
  2456.         while (0  <=  (ed = getdiffcmd(fin,true,frew,&dc)))
  2457. d704 3
  2458. a706 3
  2459.                 if (line_lim <= dc.line1)
  2460.                         editLineNumberOverflow();
  2461.                 else
  2462. d708 24
  2463. a731 24
  2464.                 if (!ed) {
  2465.                         copylines(dc.line1-1, delta);
  2466.                         /* skip over unwanted lines */
  2467.                         i = dc.nlines;
  2468.                         linecorr -= i;
  2469.                         editline += i;
  2470. #                       if large_memory
  2471.                             deletelines(editline+linecorr, i);
  2472. #                       else
  2473.                             fe = fedit;
  2474.                             do {
  2475.                                 /*skip next line*/
  2476.                                 do {
  2477.                                     Igeteof(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } );
  2478.                                 } while (c != '\n');
  2479.                             } while (--i);
  2480. #                       endif
  2481.                 } else {
  2482.                         copylines(dc.line1, delta); /*copy only; no delete*/
  2483.                         i = dc.nlines;
  2484. #                       if large_memory
  2485.                                 j = editline+linecorr;
  2486. #                       endif
  2487.                         linecorr += i;
  2488. d733 13
  2489. a745 13
  2490.                         f = fcopy;
  2491.                         if (delta)
  2492.                             do {
  2493.                                 switch (expandline(fin,f,delta,true,frew)) {
  2494.                                     case 0: case 1:
  2495.                                         if (i==1)
  2496.                                             return;
  2497.                                         /* fall into */
  2498.                                     case -1:
  2499.                                         editEndsPrematurely();
  2500.                                 }
  2501.                             } while (--i);
  2502.                         else
  2503. d747 29
  2504. a775 29
  2505.                         {
  2506.                             cache(fin);
  2507.                             do {
  2508. #                               if large_memory
  2509.                                     insertline(j++, cachetell());
  2510. #                               endif
  2511.                                 for (;;) {
  2512.                                     GETC(frew, c);
  2513. #                                   if !large_memory
  2514.                                         aputc(c, f);
  2515. #                                   endif
  2516.                                     if (c == '\n')
  2517.                                         break;
  2518.                                     if (c==SDELIM) {
  2519.                                         GETC(frew, c);
  2520.                                         if (c!=SDELIM) {
  2521.                                             if (--i)
  2522.                                                 editEndsPrematurely();
  2523.                                             nextc = c;
  2524.                                             uncache(fin);
  2525.                                             return;
  2526.                                         }
  2527.                                     }
  2528.                                 }
  2529.                                 ++rcsline;
  2530.                             } while (--i);
  2531.                             uncache(fin);
  2532.                         }
  2533.                 }
  2534. d784 1
  2535. a784 1
  2536.         int
  2537. d786 4
  2538. a789 4
  2539.         RILE *infile;
  2540.         FILE *outfile, *frewfile;
  2541.         struct hshentry const *delta;
  2542.         int delimstuffed;
  2543. d800 112
  2544. a911 112
  2545.         register c;
  2546.         declarecache;
  2547.         register FILE *out, *frew;
  2548.         register char * tp;
  2549.         register int e, ds, r;
  2550.         char const *tlim;
  2551.         static struct buf keyval;
  2552.         enum markers matchresult;
  2553.  
  2554.         setupcache(infile); cache(infile);
  2555.         out = outfile;
  2556.         frew = frewfile;
  2557.         ds = delimstuffed;
  2558.         bufalloc(&keyval, keylength+3);
  2559.         e = 0;
  2560.         r = -1;
  2561.  
  2562.         for (;;) {
  2563.             if (ds) {
  2564.                 GETC(frew, c);
  2565.             } else
  2566.                 cachegeteof(c, goto uncache_exit;);
  2567.             for (;;) {
  2568.                 switch (c) {
  2569.                     case SDELIM:
  2570.                         if (ds) {
  2571.                             GETC(frew, c);
  2572.                             if (c != SDELIM) {
  2573.                                 /* end of string */
  2574.                                 nextc=c;
  2575.                                 goto uncache_exit;
  2576.                             }
  2577.                         }
  2578.                         /* fall into */
  2579.                     default:
  2580.                         aputc(c,out);
  2581.                         r = 0;
  2582.                         break;
  2583.  
  2584.                     case '\n':
  2585.                         rcsline += ds;
  2586.                         aputc(c,out);
  2587.                         r = 2;
  2588.                         goto uncache_exit;
  2589.  
  2590.                     case KDELIM:
  2591.                         r = 0;
  2592.                         /* check for keyword */
  2593.                         /* first, copy a long enough string into keystring */
  2594.                         tp = keyval.string;
  2595.                         *tp++ = KDELIM;
  2596.                         for (;;) {
  2597.                             if (ds) {
  2598.                                 GETC(frew, c);
  2599.                             } else
  2600.                                 cachegeteof(c, goto keystring_eof;);
  2601.                             if (tp < keyval.string+keylength+1)
  2602.                                 switch (ctab[c]) {
  2603.                                     case LETTER: case Letter:
  2604.                                         *tp++ = c;
  2605.                                         continue;
  2606.                                     default:
  2607.                                         break;
  2608.                                 }
  2609.                             break;
  2610.                         }
  2611.                         *tp++ = c; *tp = '\0';
  2612.                         matchresult = trymatch(keyval.string+1);
  2613.                         if (matchresult==Nomatch) {
  2614.                                 tp[-1] = 0;
  2615.                                 aputs(keyval.string, out);
  2616.                                 continue;   /* last c handled properly */
  2617.                         }
  2618.  
  2619.                         /* Now we have a keyword terminated with a K/VDELIM */
  2620.                         if (c==VDELIM) {
  2621.                               /* try to find closing KDELIM, and replace value */
  2622.                               tlim = keyval.string + keyval.size;
  2623.                               for (;;) {
  2624.                                       if (ds) {
  2625.                                         GETC(frew, c);
  2626.                                       } else
  2627.                                         cachegeteof(c, goto keystring_eof;);
  2628.                                       if (c=='\n' || c==KDELIM)
  2629.                                         break;
  2630.                                       *tp++ =c;
  2631.                                       if (tlim <= tp)
  2632.                                           tp = bufenlarge(&keyval, &tlim);
  2633.                                       if (c==SDELIM && ds) { /*skip next SDELIM */
  2634.                                                 GETC(frew, c);
  2635.                                                 if (c != SDELIM) {
  2636.                                                         /* end of string before closing KDELIM or newline */
  2637.                                                         nextc = c;
  2638.                                                         goto keystring_eof;
  2639.                                                 }
  2640.                                       }
  2641.                               }
  2642.                               if (c!=KDELIM) {
  2643.                                     /* couldn't find closing KDELIM -- give up */
  2644.                                     *tp = 0;
  2645.                                     aputs(keyval.string, out);
  2646.                                     continue;   /* last c handled properly */
  2647.                               }
  2648.                         }
  2649.                         /* now put out the new keyword value */
  2650.                         keyreplace(matchresult,delta,out);
  2651.                         e = 1;
  2652.                         break;
  2653.                 }
  2654.                 break;
  2655.             }
  2656.         }
  2657. d914 2
  2658. a915 2
  2659.         *tp = 0;
  2660.         aputs(keyval.string, out);
  2661. d917 2
  2662. a918 2
  2663.         uncache(infile);
  2664.         return r + e;
  2665. d924 1
  2666. a924 1
  2667.         static void
  2668. d926 3
  2669. a928 3
  2670.         enum markers marker;
  2671.         register struct hshentry const *delta;
  2672.         register FILE *out;
  2673. d933 115
  2674. a1047 115
  2675.         register char const *sp, *cp, *date;
  2676.         register char c;
  2677.         register size_t cs, cw, ls;
  2678.         char const *sp1;
  2679.         char datebuf[datesize];
  2680.         int RCSv;
  2681.  
  2682.         sp = Keyword[(int)marker];
  2683.  
  2684.         if (Expand == KEY_EXPAND) {
  2685.                 aprintf(out, "%c%s%c", KDELIM, sp, KDELIM);
  2686.                 return;
  2687.         }
  2688.  
  2689.         date= delta->date;
  2690.         RCSv = RCSversion;
  2691.  
  2692.         if (Expand == KEYVAL_EXPAND  ||  Expand == KEYVALLOCK_EXPAND)
  2693.                 aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM,
  2694.                         marker==Log && RCSv<VERSION(5)  ?  '\t'  :  ' '
  2695.                 );
  2696.  
  2697.         switch (marker) {
  2698.         case Author:
  2699.                 aputs(delta->author, out);
  2700.                 break;
  2701.         case Date:
  2702.                 aputs(date2str(date,datebuf), out);
  2703.                 break;
  2704.         case Id:
  2705.         case Header:
  2706.                 aprintf(out, "%s %s %s %s %s",
  2707.                           marker==Id || RCSv<VERSION(4)
  2708.                         ? basename(RCSfilename)
  2709.                         : getfullRCSname(),
  2710.                         delta->num,
  2711.                         date2str(date, datebuf),
  2712.                         delta->author,
  2713.                           RCSv==VERSION(3) && delta->lockedby ? "Locked"
  2714.                         : delta->state
  2715.                 );
  2716.                 if (delta->lockedby!=nil)
  2717.                     if (VERSION(5) <= RCSv) {
  2718.                         if (locker_expansion || Expand==KEYVALLOCK_EXPAND)
  2719.                             aprintf(out, " %s", delta->lockedby);
  2720.                     } else if (RCSv == VERSION(4))
  2721.                         aprintf(out, " Locker: %s", delta->lockedby);
  2722.                 break;
  2723.         case Locker:
  2724.                 if (delta->lockedby)
  2725.                     if (
  2726.                                 locker_expansion
  2727.                         ||      Expand == KEYVALLOCK_EXPAND
  2728.                         ||      RCSv <= VERSION(4)
  2729.                     )
  2730.                         aputs(delta->lockedby, out);
  2731.                 break;
  2732.         case Log:
  2733.         case RCSfile:
  2734.                 aputs(basename(RCSfilename), out);
  2735.                 break;
  2736.         case Revision:
  2737.                 aputs(delta->num, out);
  2738.                 break;
  2739.         case Source:
  2740.                 aputs(getfullRCSname(), out);
  2741.                 break;
  2742.         case State:
  2743.                 aputs(delta->state, out);
  2744.                 break;
  2745.         default:
  2746.                 break;
  2747.         }
  2748.         if (Expand == KEYVAL_EXPAND  ||  Expand == KEYVALLOCK_EXPAND) {
  2749.                 afputc(' ', out);
  2750.                 afputc(KDELIM, out);
  2751.         }
  2752.         if (marker == Log) {
  2753.                 sp = delta->log.string;
  2754.                 ls = delta->log.size;
  2755.                 if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
  2756.                         return;
  2757.                 afputc('\n', out);
  2758.                 cp = Comment.string;
  2759.                 cw = cs = Comment.size;
  2760.                 awrite(cp, cs, out);
  2761.                 /* oddity: 2 spaces between date and time, not 1 as usual */
  2762.                 sp1 = strchr(date2str(date,datebuf), ' ');
  2763.                 aprintf(out, "Revision %s  %.*s %s  %s",
  2764.                     delta->num, (int)(sp1-datebuf), datebuf, sp1, delta->author
  2765.                 );
  2766.                 /* Do not include state: it may change and is not updated.  */
  2767.                 /* Comment is the comment leader.  */
  2768.                 if (VERSION(5) <= RCSv)
  2769.                     for (;  cw && (cp[cw-1]==' ' || cp[cw-1]=='\t');  --cw)
  2770.                         ;
  2771.                 for (;;) {
  2772.                     afputc('\n', out);
  2773.                     awrite(cp, cw, out);
  2774.                     if (!ls)
  2775.                         break;
  2776.                     --ls;
  2777.                     c = *sp++;
  2778.                     if (c != '\n') {
  2779.                         awrite(cp+cw, cs-cw, out);
  2780.                         do {
  2781.                             afputc(c,out);
  2782.                             if (!ls)
  2783.                                 break;
  2784.                             --ls;
  2785.                             c = *sp++;
  2786.                         } while (c != '\n');
  2787.                     }
  2788.                 }
  2789.         }
  2790. d1051 1
  2791. a1051 1
  2792.         static int
  2793. d1053 1
  2794. a1053 1
  2795.         struct buf *L;
  2796. d1061 33
  2797. a1093 33
  2798.         char *b, a[SIZEABLE_PATH];
  2799.         int e;
  2800.         size_t s;
  2801.         ssize_t r;
  2802.         struct buf bigbuf;
  2803.         unsigned linkcount = MAXSYMLINKS + 1;
  2804.  
  2805.         b = a;
  2806.         s = sizeof(a);
  2807.         bufautobegin(&bigbuf);
  2808.         while ((r = readlink(L->string,b,s))  !=  -1)
  2809.             if (r == s) {
  2810.                 bufalloc(&bigbuf, s<<1);
  2811.                 b = bigbuf.string;
  2812.                 s = bigbuf.size;
  2813.             } else if (!--linkcount) {
  2814.                 errno = ELOOP;
  2815.                 return -1;
  2816.             } else {
  2817.                 /* Splice symbolic link into L.  */
  2818.                 b[r] = '\0';
  2819.                 L->string[ROOTPATH(b) ? (size_t)0 : dirlen(L->string)]  =  '\0';
  2820.                 bufscat(L, b);
  2821.             }
  2822.         e = errno;
  2823.         bufautoend(&bigbuf);
  2824.         errno = e;
  2825.         switch (e) {
  2826.             case ENXIO:
  2827.             case EINVAL: return 1;
  2828.             case ENOENT: return 0;
  2829.             default: return -1;
  2830.         }
  2831. d1097 1
  2832. a1097 1
  2833.         RILE *
  2834. d1099 3
  2835. a1101 3
  2836.         struct buf *RCSbuf;
  2837.         struct stat *status;
  2838.         int mustread;
  2839. d1112 168
  2840. a1279 161
  2841.         register char *tp;
  2842.         register char const *sp, *RCSname, *x;
  2843.         RILE *f;
  2844.         size_t l;
  2845.         int e, exists, fdesc, previouslock, r;
  2846.         struct buf *dirt;
  2847.         struct stat statbuf;
  2848.  
  2849.         previouslock  =  frewrite != 0;
  2850.         exists =
  2851. #               if has_readlink
  2852.                         resolve_symlink(RCSbuf);
  2853. #               else
  2854.                             stat(RCSbuf->string, &statbuf) == 0  ?  1
  2855.                         :   errno==ENOENT ? 0 : -1;
  2856. #               endif
  2857.         if (exists < (mustread|previouslock))
  2858.                 /*
  2859.                  * There's an unusual problem with the RCS file;
  2860.                  * or the RCS file doesn't exist,
  2861.                  * and we must read or we already have a lock elsewhere.
  2862.                  */
  2863.                 return 0;
  2864.  
  2865.         RCSname = RCSbuf->string;
  2866.         sp = basename(RCSname);
  2867.         l = sp - RCSname;
  2868.         dirt = &dirtfname[previouslock];
  2869.         bufscpy(dirt, RCSname);
  2870.         tp = dirt->string + l;
  2871.         x = rcssuffix(RCSname);
  2872. #       if has_readlink
  2873.             if (!x) {
  2874.                 error("symbolic link to non RCS filename `%s'", RCSname);
  2875.                 errno = EINVAL;
  2876.                 return 0;
  2877.             }
  2878. #       endif
  2879.         if (*sp == *x) {
  2880.                 error("RCS filename `%s' incompatible with suffix `%s'", sp, x);
  2881.                 errno = EINVAL;
  2882.                 return 0;
  2883.         }
  2884.         /* Create a lock file whose name is a function of the RCS filename.  */
  2885.         if (*x) {
  2886.                 /*
  2887.                  * The suffix is nonempty.
  2888.                  * The lock filename is the first char of of the suffix,
  2889.                  * followed by the RCS filename with last char removed.  E.g.:
  2890.                  *      foo,v   RCS filename with suffix ,v
  2891.                  *      ,foo,   lock filename
  2892.                  */
  2893.                 *tp++ = *x;
  2894.                 while (*sp)
  2895.                         *tp++ = *sp++;
  2896.                 *--tp = 0;
  2897.         } else {
  2898.                 /*
  2899.                  * The suffix is empty.
  2900.                  * The lock filename is the RCS filename
  2901.                  * with last char replaced by '_'.
  2902.                  */
  2903.                 while ((*tp++ = *sp++))
  2904.                         ;
  2905.                 tp -= 2;
  2906.                 if (*tp == '_') {
  2907.                         error("RCS filename `%s' ends with `%c'", RCSname, *tp);
  2908.                         errno = EINVAL;
  2909.                         return 0;
  2910.                 }
  2911.                 *tp = '_';
  2912.         }
  2913.  
  2914.         sp = tp = dirt->string;
  2915.  
  2916.         f = 0;
  2917.  
  2918.         /*
  2919.         * good news:
  2920.         *       open(f, O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) is atomic
  2921.         *       according to Posix 1003.1-1990.
  2922.         * bad news:
  2923.         *       NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
  2924.         * good news:
  2925.         *       (O_TRUNC,READONLY) normally guarantees atomicity even with NFS.
  2926.         * bad news:
  2927.         *       If you're root, (O_TRUNC,READONLY) doesn't guarantee atomicity.
  2928.         * good news:
  2929.         *       Root-over-the-wire NFS access is rare for security reasons.
  2930.         *       This bug has never been reported in practice with RCS.
  2931.         * So we don't worry about this bug.
  2932.         *
  2933.         * An even rarer NFS bug can occur when clients retry requests.
  2934.         * Suppose client A renames the lock file ",f," to "f,v"
  2935.         * at about the same time that client B creates ",f,",
  2936.         * and suppose A's first rename request is delayed, so A reissues it.
  2937.         * The sequence of events might be:
  2938.         *       A sends rename(",f,", "f,v")
  2939.         *       B sends create(",f,")
  2940.         *       A sends retry of rename(",f,", "f,v")
  2941.         *       server receives, does, and acknowledges A's first rename()
  2942.         *       A receives acknowledgment, and its RCS program exits
  2943.         *       server receives, does, and acknowledges B's create()
  2944.         *       server receives, does, and acknowledges A's retry of rename()
  2945.         * This not only wrongly deletes B's lock, it removes the RCS file!
  2946.         * Most NFS implementations have idempotency caches that usually prevent
  2947.         * this scenario, but such caches are finite and can be overrun.
  2948.         * This problem afflicts programs that use the traditional
  2949.         * Unix method of using link() and unlink() to get and release locks,
  2950.         * as well as RCS's method of using open() and rename().
  2951.         * There is no easy workaround for either link-unlink or open-rename.
  2952.         * Any new method based on lockf() seemingly would be incompatible with
  2953.         * the old methods; besides, lockf() is notoriously buggy under NFS.
  2954.         * Since this problem afflicts scads of Unix programs, but is so rare
  2955.         * that nobody seems to be worried about it, we won't worry either.
  2956.         */
  2957. #       define READONLY (S_IRUSR|S_IRGRP|S_IROTH)
  2958. #       if !open_can_creat
  2959. #               define create(f) creat(f, READONLY)
  2960. #       else
  2961. #               define create(f) open(f, O_BINARY|O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY)
  2962. #       endif
  2963.  
  2964.         catchints();
  2965.         ignoreints();
  2966.  
  2967.         /*
  2968.          * Create a lock file for an RCS file.  This should be atomic, i.e.
  2969.          * if two processes try it simultaneously, at most one should succeed.
  2970.          */
  2971.         seteid();
  2972.         fdesc = create(sp);
  2973.         e = errno;
  2974.         setrid();
  2975.  
  2976.         if (fdesc < 0) {
  2977.                 if (e == EACCES  &&  stat(tp,&statbuf) == 0)
  2978.                         /* The RCS file is busy.  */
  2979.                         e = EEXIST;
  2980.         } else {
  2981.                 dirtfmaker[0] = effective;
  2982.                 e = ENOENT;
  2983.                 if (exists) {
  2984.                     f = Iopen(RCSname, FOPEN_R, status);
  2985.                     e = errno;
  2986.                     if (f && previouslock) {
  2987.                         /* Discard the previous lock in favor of this one.  */
  2988.                         Ozclose(&frewrite);
  2989.                         seteid();
  2990.                         if ((r = un_link(newRCSfilename)) != 0)
  2991.                             e = errno;
  2992.                         setrid();
  2993.                         if (r != 0)
  2994.                             enfaterror(e, newRCSfilename);
  2995.                         bufscpy(&dirtfname[0], tp);
  2996.                     }
  2997.                 }
  2998.                 if (!(frewrite = fdopen(fdesc, FOPEN_W))) {
  2999.                     efaterror(newRCSfilename);
  3000.                 }
  3001.         }
  3002. d1281 1
  3003. a1281 1
  3004.         restoreints();
  3005. d1283 2
  3006. a1284 2
  3007.         errno = e;
  3008.         return f;
  3009. d1287 1
  3010. a1287 1
  3011.         void
  3012. d1289 1
  3013. a1289 1
  3014.         char const *name;
  3015. d1294 7
  3016. a1300 7
  3017.         register int i;
  3018.         for (i=DIRTEMPNAMES; 0<=--i; )
  3019.                 if (dirtfname[i].string == name) {
  3020.                         dirtfmaker[i] = notmade;
  3021.                         return;
  3022.                 }
  3023.         faterror("keepdirtemp");
  3024. d1303 1
  3025. a1303 1
  3026.         char const *
  3027. d1305 2
  3028. a1306 2
  3029.         register char const *name;
  3030.         int n;
  3031. d1315 41
  3032. a1355 41
  3033.         register char *tp, *np;
  3034.         register size_t dl;
  3035.         register struct buf *bn;
  3036.  
  3037.         if (!name)
  3038.                 return maketemp(n);
  3039.         dl = dirlen(name);
  3040.         bn = &dirtfname[n];
  3041.         bufalloc(bn,
  3042. #               if has_mktemp
  3043.                         dl + 9
  3044. #               else
  3045.                         strlen(name) + 3
  3046. #               endif
  3047.         );
  3048.         bufscpy(bn, name);
  3049.         np = tp = bn->string;
  3050.         tp += dl;
  3051.         *tp++ = '_';
  3052.         *tp++ = '0'+n;
  3053.         catchints();
  3054. #       if has_mktemp
  3055.                 VOID strcpy(tp, "XXXXXX");
  3056.                 if (!mktemp(np) || !*np)
  3057.                     faterror("can't make temporary file name `%.*s%c_%cXXXXXX'",
  3058.                         (int)dl, name, SLASH, '0'+n
  3059.                     );
  3060. #       else
  3061.                 /*
  3062.                  * Posix 1003.1-1990 has no reliable way
  3063.                  * to create a unique file in a named directory.
  3064.                  * We fudge here.  If the working file name is abcde,
  3065.                  * the temp filename is _Ncde where N is a digit.
  3066.                  */
  3067.                 name += dl;
  3068.                 if (*name) name++;
  3069.                 if (*name) name++;
  3070.                 VOID strcpy(tp, name);
  3071. #       endif
  3072.         dirtfmaker[n] = real;
  3073.         return np;
  3074. d1358 1
  3075. a1358 1
  3076.         void
  3077. d1362 2
  3078. a1363 2
  3079.         register int i;
  3080.         enum maker m;
  3081. d1365 4
  3082. a1368 4
  3083.         for (i = DIRTEMPNAMES;  0 <= --i;  )
  3084.             if ((m = dirtfmaker[i]) != notmade) {
  3085.                 if (m == effective)
  3086.                     seteid();
  3087. d1370 9
  3088. a1378 9
  3089.                 /* Could this be a still open frewrite file? */
  3090.                 if((i == 0) && (frewrite != NULL))
  3091.                 {
  3092.                     /* This should be Ozclose, but Ozclose does
  3093.                        too much error checking for this. This should be
  3094.                        just a try to get rid of this file, too! */
  3095.                     fclose(frewrite);
  3096.                     frewrite = NULL;
  3097.                 } /* if */
  3098. d1380 5
  3099. a1384 5
  3100.                 VOID un_link(dirtfname[i].string);
  3101.                 if (m == effective)
  3102.                     setrid();
  3103.                 dirtfmaker[i] = notmade;
  3104.             }
  3105. d1388 1
  3106. a1388 1
  3107.         int
  3108. d1404 49
  3109. a1452 49
  3110. #       if bad_a_rename
  3111.                 /*
  3112.                  * This host is brain damaged.  A race condition is possible
  3113.                  * while the lock file is temporarily writable.
  3114.                  * There doesn't seem to be a workaround.
  3115.                  */
  3116.                 mode_t mode_while_renaming = mode|S_IWUSR;
  3117. #       else
  3118. #               define mode_while_renaming mode
  3119. #       endif
  3120.         if (fromp) {
  3121. #               if has_fchmod
  3122.                         if (fchmod(fileno(*fromp), mode_while_renaming) != 0)
  3123.                                 return -1;
  3124. #               endif
  3125.                 Ozclose(fromp);
  3126.         }
  3127. #       if has_fchmod
  3128.             else
  3129. #       endif
  3130.             if (chmod(from, mode_while_renaming) != 0)
  3131.                 return -1;
  3132.  
  3133. #       if !has_rename || bad_b_rename
  3134.                 VOID un_link(to);
  3135.                 /*
  3136.                  * We need not check the result;
  3137.                  * link() or rename() will catch it.
  3138.                  * No harm is done if TO does not exist.
  3139.                  * However, there's a short window of inconsistency
  3140.                  * during which TO does not exist.
  3141.                  */
  3142. #       endif
  3143.  
  3144.         return
  3145. #           if !has_rename
  3146.                 do_link(from,to) != 0  ?  -1  :  un_link(from)
  3147. #           else
  3148.                     rename(from, to) != 0
  3149. #                   if has_NFS
  3150.                         && errno != ENOENT
  3151. #                   endif
  3152.                 ?  -1
  3153. #               if bad_a_rename
  3154.                 :  mode != mode_while_renaming  ?  chmod(to, mode)
  3155. #               endif
  3156.                 :  0
  3157. #           endif
  3158.         ;
  3159. d1454 1
  3160. a1454 1
  3161. #       undef mode_while_renaming
  3162. d1459 1
  3163. a1459 1
  3164.         int
  3165. d1461 2
  3166. a1462 2
  3167.         int delete;
  3168.         struct hshentry **target;
  3169. d1470 1
  3170. a1470 1
  3171.         register struct lock *next, **trail, **found;
  3172. d1472 18
  3173. a1489 18
  3174.         found = 0;
  3175.         for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
  3176.                 if (strcmp(getcaller(), next->login)  ==  0) {
  3177.                         if (found) {
  3178.                                 error("multiple revisions locked by %s; please specify one", getcaller());
  3179.                                 return 2;
  3180.                         }
  3181.                         found = trail;
  3182.                 }
  3183.         if (!found)
  3184.                 return 0;
  3185.         next = *found;
  3186.         *target = next->delta;
  3187.         if (delete) {
  3188.                 next->delta->lockedby = nil;
  3189.                 *found = next->nextlock;
  3190.         }
  3191.         return 1;
  3192. d1492 1
  3193. a1492 1
  3194.         int
  3195. d1494 1
  3196. a1494 1
  3197.         struct hshentry * delta;
  3198. d1502 1
  3199. a1502 1
  3200.         register struct lock *next;
  3201. d1504 17
  3202. a1520 17
  3203.         next=Locks;
  3204.         for (next = Locks;  next;  next = next->nextlock)
  3205.                 if (cmpnum(delta->num, next->delta->num) == 0)
  3206.                         if (strcmp(getcaller(), next->login) == 0)
  3207.                                 return 0;
  3208.                         else {
  3209.                                 error("revision %s already locked by %s",
  3210.                                       delta->num, next->login
  3211.                                 );
  3212.                                 return -1;
  3213.                         }
  3214.         next = ftalloc(struct lock);
  3215.         delta->lockedby = next->login = getcaller();
  3216.         next->delta = delta;
  3217.         next->nextlock = Locks;
  3218.         Locks = next;
  3219.         return 1;
  3220. d1524 1
  3221. a1524 1
  3222.         int
  3223. d1526 2
  3224. a1527 2
  3225.         char const *num, *name;
  3226.         int rebind;
  3227. d1535 1
  3228. a1535 1
  3229.         register struct assoc *next;
  3230. d1537 17
  3231. a1553 17
  3232.         for (next = Symbols;  next;  next = next->nextassoc)
  3233.                 if (strcmp(name, next->symbol)  ==  0)
  3234.                         if (rebind  ||  strcmp(next->num,num) == 0) {
  3235.                                 next->num = num;
  3236.                                 return true;
  3237.                         } else {
  3238.                                 error("symbolic name %s already bound to %s",
  3239.                                         name, next->num
  3240.                                 );
  3241.                                 return false;
  3242.                         }
  3243.         next = ftalloc(struct assoc);
  3244.         next->symbol = name;
  3245.         next->num = num;
  3246.         next->nextassoc = Symbols;
  3247.         Symbols = next;
  3248.         return true;
  3249. d1558 1
  3250. a1558 1
  3251.         char const *
  3252. d1562 5
  3253. a1566 5
  3254. #       if has_setuid
  3255.                 return getusername(euid()!=ruid());
  3256. #       else
  3257.                 return getusername(false);
  3258. #       endif
  3259. d1570 1
  3260. a1570 1
  3261.         int
  3262. d1578 1
  3263. a1578 1
  3264.         register struct access const *next;
  3265. d1580 2
  3266. a1581 2
  3267.         if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
  3268.                 return true;
  3269. d1583 5
  3270. a1587 5
  3271.         next = AccessList;
  3272.         do {
  3273.                 if (strcmp(getcaller(), next->login)  ==  0)
  3274.                         return true;
  3275.         } while ((next = next->nextaccess));
  3276. d1589 2
  3277. a1590 2
  3278.         error("user %s not on the access list", getcaller());
  3279.         return false;
  3280. d1594 1
  3281. a1594 1
  3282.         int
  3283. d1596 1
  3284. a1596 1
  3285.         int lockflag, changed;
  3286. d1605 1
  3287. a1605 1
  3288.         int r, e;
  3289. d1607 23
  3290. a1629 23
  3291.         if (lockflag)
  3292.                 if (changed) {
  3293.                         if (changed < 0)
  3294.                                 return false;
  3295.                         putadmin(frewrite);
  3296.                         puttree(Head, frewrite);
  3297.                         aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
  3298.                         foutptr = frewrite;
  3299.                 } else {
  3300.                         Ozclose(&frewrite);
  3301.                         seteid();
  3302.                         ignoreints();
  3303.                         r = un_link(newRCSfilename);
  3304.                         e = errno;
  3305.                         keepdirtemp(newRCSfilename);
  3306.                         restoreints();
  3307.                         setrid();
  3308.                         if (r != 0) {
  3309.                                 enerror(e, RCSfilename);
  3310.                                 return false;
  3311.                         }
  3312.                 }
  3313.         return true;
  3314. d1632 1
  3315. a1632 1
  3316.         int
  3317. d1634 1
  3318. a1634 1
  3319.         int changed;
  3320. d1640 1
  3321. a1640 1
  3322.         int r, e;
  3323. d1642 24
  3324. a1665 24
  3325.         if (changed && !nerror) {
  3326.                 if (finptr) {
  3327.                         fastcopy(finptr, frewrite);
  3328.                         Izclose(&finptr);
  3329.                 }
  3330.                 if (1 < RCSstat.st_nlink)
  3331.                         warn("breaking hard link to %s", RCSfilename);
  3332.                 seteid();
  3333.                 ignoreints();
  3334.                 r = chnamemod(&frewrite, newRCSfilename, RCSfilename,
  3335.                         RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH)
  3336.                 );
  3337.                 e = errno;
  3338.                 keepdirtemp(newRCSfilename);
  3339.                 restoreints();
  3340.                 setrid();
  3341.                 if (r != 0) {
  3342.                         enerror(e, RCSfilename);
  3343.                         error("saved in %s", newRCSfilename);
  3344.                         dirtempunlink();
  3345.                         return false;
  3346.                 }
  3347.         }
  3348.         return true;
  3349. d1668 1
  3350. a1668 1
  3351.         void
  3352. d1670 1
  3353. a1670 1
  3354.         FILE *f;
  3355. d1672 2
  3356. a1673 2
  3357.         if (fflush(f) != 0)
  3358.                 Oerror();
  3359. @
  3360.  
  3361.  
  3362. 5.11.1.3
  3363. log
  3364. @Changed all checks for AMIGA to _AMIGA. This is more standard like
  3365. and helps future updates. Major patch cleanup on the way.
  3366. [Note: Added a VOID_CLOSEDIR define for this global change, too!]
  3367. @
  3368. text
  3369. @a38 3
  3370.  * Revision 5.11.1.2  1993/12/04  08:40:36  heinz
  3371.  * Removed the SAS/C create patch, as the problem has been fixed for 6.3.
  3372.  *
  3373. d160 1
  3374. a160 1
  3375. libId(editId, "$Id: rcsedit.c,v 5.11.1.2 1993/12/04 08:40:36 heinz Exp heinz $")
  3376. d1365 1
  3377. a1365 3
  3378. #ifdef _AMIGA
  3379.                 /* AMIGA fix for delete before close! */
  3380.  
  3381. d1375 1
  3382. a1375 1
  3383. #endif /* _AMIGA */
  3384. @
  3385.  
  3386.  
  3387. 5.11.1.4
  3388. log
  3389. @Support for the new keywords VER, AmigaREV, and AmigaProtection.
  3390. @
  3391. text
  3392. @a38 5
  3393.  * Revision 5.11.1.3  1993/12/18  16:12:22  heinz
  3394.  * Changed all checks for AMIGA to _AMIGA. This is more standard like
  3395.  * and helps future updates. Major patch cleanup on the way.
  3396.  * [Note: Added a VOID_CLOSEDIR define for this global change, too!]
  3397.  *
  3398. d163 1
  3399. a163 1
  3400. libId(editId, "$Id: rcsedit.c,v 5.11.1.3 1993/12/18 16:12:22 heinz Exp heinz $")
  3401. a813 6
  3402. #ifdef _AMIGA
  3403. /* We need this bufferfor a CBMVersion string increment. This might not be
  3404.    a very nice way to do all this. But it is easy for now. */
  3405.  
  3406.         char CBMbuf[BUFSIZ];
  3407. #endif /* _AMIGA */
  3408. a902 21
  3409. #ifdef _AMIGA
  3410. /* Only with CBMVersion, a newline end is allowed. We want
  3411.    to be able to specify C= style version strings without a trailing `$'.
  3412. */
  3413.  
  3414.                               if(matchresult == CBMVersion)
  3415.                               {
  3416.                                   /* We keep the delimiting KDELIM for CBMVersion! */
  3417.                                   if(c == KDELIM)
  3418.                                   {
  3419.                                       *tp++ = c;
  3420.                                       if(tlim <= tp)
  3421.                                       {
  3422.                                           tp = bufenlarge(&keyval, &tlim);
  3423.                                       } /* if */
  3424.                                   } /* if */
  3425.  
  3426.                                   /* We are ok! */
  3427.                                   c = KDELIM;
  3428.                               } /* if */
  3429. #endif /* _AMIGA */
  3430. a909 266
  3431. #ifdef _AMIGA
  3432. /* This is really ugly. For Amiga keywords, we need to take their current
  3433.    value und set it in the delta unless there is already a value set. The
  3434.    ugly thing about it is that the delta is const. So we have to cast the
  3435.    pointer to non const for modifying the delta contents. Hmpfh.
  3436. */
  3437.                         /* Terminate the string for sure. */
  3438.                         *tp = 0;
  3439.                         if(matchresult == CBMVersion ||
  3440.                            matchresult == CBMRevision ||
  3441.                            matchresult == CBMProtection)
  3442.                         {
  3443.                             char *vp = strchr(keyval.string + 1, VDELIM);
  3444.                             char **dp;
  3445.                             int incflag = delta->CBMVersionIncFlag;
  3446.  
  3447.                             if(vp)
  3448.                             {
  3449.                                 vp++;
  3450.  
  3451.                                 /* Skip leading space. */
  3452.                                 while(*vp == ' ' || *vp == '\t')
  3453.                                 {
  3454.                                     vp++;
  3455.                                 } /* while */
  3456.  
  3457.                                 if(*vp == 0)
  3458.                                 {
  3459.                                     /* No entry, no nothing! */
  3460.                                     vp = NULL;
  3461.                                 } /* if */
  3462.                             } /* if */
  3463.  
  3464.                             switch(matchresult)
  3465.                             {
  3466.                                 case CBMVersion:
  3467.                                     dp = (char **)&delta->CBMVersion;
  3468.                                     if(!vp)
  3469.                                     {
  3470.                                         strcpy(CBMbuf, workfilename);
  3471.                                         strcat(CBMbuf, " 0.0  (xx.xx.xx)  $");
  3472.  
  3473.                                         vp = CBMbuf;
  3474.  
  3475.                                         /* Force expansion of the fragment */
  3476.                                         incflag = true;
  3477.                                     } /* if */
  3478.  
  3479.                                     if(incflag)
  3480.                                     {
  3481.                                         /* Ouch. Now it gets tricky.
  3482.                                            We don't use sscanf here to be sure of
  3483.                                            what we do. */
  3484.                                         const char *s;
  3485.                                         char *d;
  3486.                                         int ver, rev, l;
  3487.                                         int stcd_i(const char *, int *);
  3488.  
  3489.                                         s = vp;
  3490.                                         d = CBMbuf;
  3491.  
  3492.                                         /* Read the prg name */
  3493.                                         while(*s && *s != ' ' && *s != '\t')
  3494.                                         {
  3495.                                             *d++ = *s++;
  3496.                                         } /* while */
  3497.  
  3498.                                         /* Skip space after prg name */
  3499.                                         while(*s == ' ' || *s == '\t')
  3500.                                         {
  3501.                                             s++;
  3502.                                         } /* while */
  3503.  
  3504.                                         l = stcd_i(s, &ver);
  3505.                                         s += l;
  3506.                                         if(!l || *s++ != '.' || ver < 0)
  3507.                                         {
  3508.                                             break;
  3509.                                         } /* if */
  3510.  
  3511.                                         l = stcd_i(s, &rev);
  3512.                                         s += l;
  3513.  
  3514.                                         /* Skip space after ver.rev */
  3515.                                         while(*s == ' ' || *s == '\t')
  3516.                                         {
  3517.                                             s++;
  3518.                                         } /* while */
  3519.  
  3520.                                         if(!l || *s != '(' || rev < 0)
  3521.                                         {
  3522.                                             break;
  3523.                                         } /* if */
  3524.  
  3525.                                         /* Skip over to ')' and skip trailing space */
  3526.                                         while(*s && *s != ')')
  3527.                                         {
  3528.                                             s++;
  3529.                                         } /* while */
  3530.  
  3531.                                         if(*s == ')')
  3532.                                         {
  3533.                                             s++;
  3534.                                         } /* if */
  3535.  
  3536.                                         /* Skip space after date */
  3537.                                         while(*s == ' ' || *s == '\t')
  3538.                                         {
  3539.                                             s++;
  3540.                                         } /* while */
  3541.  
  3542.                                         sprintf(d, " %d.%d (", ver, rev + 1);
  3543.                                         d += strlen(d);
  3544.  
  3545.                                         /* We always insert the current date! */
  3546.                                         {
  3547.                                             time_t t;
  3548.  
  3549.                                             time(&t);
  3550.  
  3551.                                             l = strftime(d, 11, "%d.%m.%y) ", localtime(&t));
  3552.                                             d += l;
  3553.                                         }
  3554.  
  3555.                                         /* For VERSION FULL */
  3556.                                         strcpy(d, s);
  3557.  
  3558.                                         vp = CBMbuf;
  3559.                                     } /* if */
  3560.                                     break;
  3561.                                 case CBMRevision:
  3562.                                     dp = (char **)&delta->CBMRevision;
  3563.                                     if(!vp)
  3564.                                     {
  3565.                                         strcpy(CBMbuf, "0");
  3566.  
  3567.                                         vp = CBMbuf;
  3568.  
  3569.                                         /* Force expansion of the fragment */
  3570.                                         incflag = true;
  3571.                                     } /* if */
  3572.  
  3573.                                     if(incflag)
  3574.                                     {
  3575.                                         /* Ouch. Now it gets tricky.
  3576.                                            We don't use sscanf here to be sure of
  3577.                                            what we do. */
  3578.                                         const char *s;
  3579.                                         char *d;
  3580.                                         int ver, rev, l;
  3581.                                         int stcd_i(const char *, int *);
  3582.  
  3583.                                         s = vp;
  3584.                                         d = CBMbuf;
  3585.  
  3586.                                         /* Skip to a digit */
  3587.                                         while(*s && !isdigit(*s))
  3588.                                         {
  3589.                                             *d++ = *s++;
  3590.                                         } /* while */
  3591.  
  3592.                                         l = stcd_i(s, &rev);
  3593.                                         s += l;
  3594.  
  3595.                                         if(!l || rev < 0)
  3596.                                         {
  3597.                                             break;
  3598.                                         } /* if */
  3599.  
  3600.                                         {
  3601.                                             char numbuf[12];
  3602.  
  3603.                                             sprintf(numbuf, "%d", rev + 1);
  3604.                                             l = strlen(numbuf);
  3605.  
  3606.                                             memmove(d + l, s, strlen(s) + 1);
  3607.                                             memmove(d, numbuf, l);
  3608.                                         }
  3609.  
  3610.                                         /* Trim off trailing space */
  3611.                                         l = strlen(CBMbuf);
  3612.                                         while(l &&
  3613.                                               (CBMbuf[l - 1] == ' ' ||
  3614.                                                CBMbuf[l - 1] == '\t'))
  3615.                                         {
  3616.                                             CBMbuf[--l] = 0;
  3617.                                         } /* while */
  3618.  
  3619.                                         vp = CBMbuf;
  3620.                                     } /* if */
  3621.                                     break;
  3622.                                 case CBMProtection:
  3623.                                     dp = (char **)&delta->CBMProtection;
  3624.                                     if(!vp)
  3625.                                     {
  3626.                                         char *d = CBMbuf;
  3627.  
  3628.                                         if(RCSstat.st_mode & S_IPURE)
  3629.                                         {
  3630.                                             *d ++ = 'P';
  3631.                                         } /* if */
  3632.  
  3633.                                         if(RCSstat.st_mode & S_ISCRIPT)
  3634.                                         {
  3635.                                             *d ++ = 'S';
  3636.                                         } /* if */
  3637.  
  3638.                                         *d = 0;
  3639.                                     }
  3640.                                     else
  3641.                                     {
  3642.                                         char *s, *d;
  3643.  
  3644.                                         s = vp;
  3645.  
  3646.                                         /* Here we have the most ugly hack
  3647.                                            today. Not much I can do about it.
  3648.                                            Unless there can be more information
  3649.                                            of generic value kept per revision,
  3650.                                            I have to hack around. */
  3651.                                         RCSstat.st_mode &= ~(S_IPURE|S_ISCRIPT);
  3652.                                         d = CBMbuf;
  3653.                                         while(*d = *s++)
  3654.                                         {
  3655.                                             if(*d != ' ' && *d != '\t')
  3656.                                             {
  3657.                                                 switch(*d)
  3658.                                                 {
  3659.                                                     case 'P':
  3660.                                                     case 'p':
  3661.                                                         RCSstat.st_mode |= S_IPURE;
  3662.                                                         break;
  3663.                                                     case 'S':
  3664.                                                     case 's':
  3665.                                                         RCSstat.st_mode |= S_ISCRIPT;
  3666.                                                         break;
  3667.                                                     default:
  3668.                                                         break;
  3669.                                                 } /* switch */
  3670.                                                 d++;
  3671.                                             } /* if */
  3672.                                         } /* while */
  3673.                                     } /* if */
  3674.  
  3675.                                     if(!CBMbuf[0])
  3676.                                     {
  3677.                                         strcpy(CBMbuf, "--");
  3678.                                     } /* if */
  3679.  
  3680.                                     vp = CBMbuf;
  3681.                                     break;
  3682.                                 default:
  3683.                                     dp = NULL;
  3684.                                     break;
  3685.                             } /* switch */
  3686.  
  3687.                             /* Do we have a value? */
  3688.                             if(dp)
  3689.                             {
  3690.                                 /* Set the value in the delta.
  3691.                                    This is just for keyreplace below. */
  3692.                                 *dp = vp;
  3693.                             } /* if */
  3694.                         } /* if */
  3695.  
  3696. #endif /* _AMIGA */
  3697. a1008 12
  3698. #ifdef _AMIGA
  3699. /* The Amiga keywords CBMVersion and CBMProtection! */
  3700.         case CBMVersion:
  3701.                 aputs(delta->CBMVersion, out);
  3702.                 break;
  3703.         case CBMRevision:
  3704.                 aputs(delta->CBMRevision, out);
  3705.                 break;
  3706.         case CBMProtection:
  3707.                 aputs(delta->CBMProtection, out);
  3708.                 break;
  3709. #endif /* _AMIGA */
  3710. a1012 21
  3711. #ifdef _AMIGA
  3712. /* Only with CBMVersion, a newline end is allowed. We want
  3713.    to be able to specify C= style version strings without a trailing `$'.
  3714.    So the trailing KDELIM is included in the version string. We don't have
  3715.    to add it here!
  3716. */
  3717.                 if(marker != CBMVersion)
  3718.                 {
  3719.                     afputc(' ', out);
  3720.                     afputc(KDELIM, out);
  3721.                 }
  3722.                 else
  3723.                 {
  3724.                     int l = strlen(delta->CBMVersion);
  3725.  
  3726.                     if(!l || delta->CBMVersion[l - 1] != KDELIM)
  3727.                     {
  3728.                         afputc('\n', out);
  3729.                     } /* if */
  3730.                 } /* if */
  3731. #else
  3732. a1014 1
  3733. #endif /* _AMIGA */
  3734. @
  3735.  
  3736.  
  3737. 5.11.1.5
  3738. log
  3739. @First version of Amiga keywords implemented. Ugly, but acceptable for now.
  3740. @
  3741. text
  3742. @a38 3
  3743.  * Revision 5.11.1.4  1994/02/12  19:20:21  heinz
  3744.  * Support for the new keywords VER, AmigaREV, and AmigaProtection.
  3745.  *
  3746. d168 1
  3747. a168 1
  3748. libId(editId, "$Id: rcsedit.c,v 5.11.1.4 1994/02/12 19:20:21 heinz Exp heinz $")
  3749. a171 10
  3750. #ifdef _AMIGA
  3751. /* We need a flag for ci that tells us when to increment
  3752.    our special revision numbers. This flag is in the delta structure to be
  3753.    specific about what delta to "increment".
  3754.    There is also a global variable to pass
  3755.    the string we create fromexpandline to keyreplace. I don't want to patch
  3756.    into the functions declarations. */
  3757.  
  3758. static char *AMIGA_specialstring;
  3759. #endif /*_AMIGA */
  3760. a951 1
  3761.                            matchresult == CBMDate ||
  3762. d955 2
  3763. a956 1
  3764.                             int incflag = delta->CBMRevIncFlag;
  3765. d978 1
  3766. d982 1
  3767. a982 1
  3768.                                         strcat(CBMbuf, " 0.0  (xx.xx.xx) $");
  3769. d1036 1
  3770. a1036 1
  3771.                                         /* Skip over to ')' and skip it, too */
  3772. d1047 6
  3773. d1062 1
  3774. a1062 1
  3775.                                             l = strftime(d, 11, "%d.%m.%y)", localtime(&t));
  3776. d1073 1
  3777. d1091 1
  3778. a1091 1
  3779.                                         int rev, l;
  3780. d1097 1
  3781. a1097 1
  3782.                                         /* Copy up to a digit */
  3783. a1132 81
  3784.                                 case CBMDate:
  3785.                                     if(!vp)
  3786.                                     {
  3787.                                         strcpy(CBMbuf, "00.00.00");
  3788.  
  3789.                                         vp = CBMbuf;
  3790.  
  3791.                                         /* Force expansion of the fragment */
  3792.                                         incflag = true;
  3793.                                     } /* if */
  3794.  
  3795.                                     if(incflag)
  3796.                                     {
  3797.                                         /* Ouch. Now it gets tricky.
  3798.                                            We don't use sscanf here to be sure of
  3799.                                            what we do. */
  3800.                                         const char *s;
  3801.                                         char *d;
  3802.                                         int l;
  3803.  
  3804.                                         s = vp;
  3805.                                         d = CBMbuf;
  3806.  
  3807.                                         /* Copy up to a digit */
  3808.                                         while(*s && !isdigit(*s))
  3809.                                         {
  3810.                                             *d++ = *s++;
  3811.                                         } /* while */
  3812.  
  3813.                                         /* Skip over day digits */
  3814.                                         while(*s && isdigit(*s))
  3815.                                         {
  3816.                                             s++;
  3817.                                         } /* while */
  3818.  
  3819.                                         if(*s == '.')
  3820.                                         {
  3821.                                             s++;
  3822.                                         } /* if */
  3823.  
  3824.                                         /* Skip over month digits */
  3825.                                         while(*s && isdigit(*s))
  3826.                                         {
  3827.                                             s++;
  3828.                                         } /* while */
  3829.  
  3830.                                         if(*s == '.')
  3831.                                         {
  3832.                                             s++;
  3833.                                         } /* if */
  3834.  
  3835.                                         /* Skip over year digits */
  3836.                                         while(*s && isdigit(*s))
  3837.                                         {
  3838.                                             s++;
  3839.                                         } /* while */
  3840.  
  3841.                                         {
  3842.                                             char datebuf[12];
  3843.                                             time_t t;
  3844.  
  3845.                                             time(&t);
  3846.  
  3847.                                             l = strftime(datebuf, 11, "%d.%m.%y", localtime(&t));
  3848.  
  3849.                                             memmove(d + l, s, strlen(s) + 1);
  3850.                                             memmove(d, datebuf, l);
  3851.                                         }
  3852.  
  3853.                                         /* Trim off trailing space */
  3854.                                         l = strlen(CBMbuf);
  3855.                                         while(l &&
  3856.                                               (CBMbuf[l - 1] == ' ' ||
  3857.                                                CBMbuf[l - 1] == '\t'))
  3858.                                         {
  3859.                                             CBMbuf[--l] = 0;
  3860.                                         } /* while */
  3861.  
  3862.                                         vp = CBMbuf;
  3863.                                     } /* if */
  3864.                                     break;
  3865. d1134 1
  3866. d1194 1
  3867. a1194 1
  3868.                                     vp = NULL;
  3869. d1198 7
  3870. a1204 3
  3871.                             /* Set the value in the delta.
  3872.                                This is just for keyreplace below. */
  3873.                             AMIGA_specialstring = (vp) ? vp : "";
  3874. d1206 1
  3875. d1308 1
  3876. a1308 1
  3877. /* The Amiga keywords use only one special variable set in expandline! */
  3878. d1310 2
  3879. d1313 2
  3880. d1316 1
  3881. a1316 2
  3882.         case CBMDate:
  3883.                 aputs(AMIGA_specialstring, out);
  3884. d1336 1
  3885. a1336 1
  3886.                     int l = strlen(AMIGA_specialstring);
  3887. d1338 1
  3888. a1338 1
  3889.                     if(!l || AMIGA_specialstring[l - 1] != KDELIM)
  3890. @
  3891.  
  3892.  
  3893. 5.11.1.6
  3894. log
  3895. @Auto increment support for the new AMiga keywords should work better now.
  3896. Somme comment clarifications.
  3897. @
  3898. text
  3899. @a38 3
  3900.  * Revision 5.11.1.5  1994/02/14  19:29:45  heinz
  3901.  * First version of Amiga keywords implemented. Ugly, but acceptable for now.
  3902.  *
  3903. d171 1
  3904. a171 1
  3905. libId(editId, "$Id: rcsedit.c,v 5.11.1.5 1994/02/14 19:29:45 heinz Exp heinz $")
  3906. d177 3
  3907. a179 4
  3908.    our special revision numbers in what delta. */
  3909. struct hshentry *CBMRevIncDelta;
  3910.  
  3911. /* There is also a global variable to pass
  3912. d969 1
  3913. a969 1
  3914.                             int incflag = (delta == CBMRevIncDelta);
  3915. d1778 1
  3916. a1778 3
  3917. /* AMIGA fix for delete before close! Unlinking an open file is not
  3918.    possible with any non Unix file system I know. So this problem affects
  3919.    the Amiga, too! */
  3920. @
  3921.  
  3922.  
  3923. 5.11.1.7
  3924. log
  3925. @Fixed NULL read enforcer hit when rcssuffix returns NULL.
  3926. @
  3927. text
  3928. @a38 4
  3929.  * Revision 5.11.1.6  1994/03/14  09:19:05  heinz
  3930.  * Auto increment support for the new AMiga keywords should work better now.
  3931.  * Somme comment clarifications.
  3932.  *
  3933. d174 1
  3934. a174 1
  3935. libId(editId, "$Id: rcsedit.c,v 5.11.1.6 1994/03/14 09:19:05 heinz Exp heinz $")
  3936. a1568 16
  3937. #ifdef _AMIGA
  3938. /* rcssuffix may well return 0 and this is not caught without
  3939.    readlink being available. Actually this happened here because of the
  3940.    Amiga RCS RCS_link soft link emulation when the specified filename had
  3941.    no suitable suffix and its path was different from the current
  3942.    directories RCS directory choice via RCS_link. So rcssuffix did not find
  3943.    anything and returned NULL.
  3944.  
  3945.    So we need to do something about it. We choose the least destructive
  3946.    option.
  3947. */
  3948.         if(!x)
  3949.         {
  3950.             x = "";
  3951.         } /* if */
  3952. #endif /* _AMIGA */
  3953. @
  3954.  
  3955.  
  3956. 5.11.1.8
  3957. log
  3958. @Fixed handling of a newline terminated CBMVersion string.
  3959. @
  3960. text
  3961. @a38 3
  3962.  * Revision 5.11.1.7  1994/03/31  18:38:48  heinz
  3963.  * Fixed NULL read enforcer hit when rcssuffix returns NULL.
  3964.  *
  3965. d178 1
  3966. a178 1
  3967. libId(editId, "$Id: rcsedit.c,v 5.11.1.7 1994/03/31 18:38:48 heinz Exp heinz $")
  3968. d1002 1
  3969. a1002 3
  3970.                                         strcat(CBMbuf, " 0.0  (xx.xx.xx) ");
  3971.                                         CBMbuf[strlen(CBMbuf)] = KDELIM;
  3972.                                         CBMbuf[strlen(CBMbuf)] = 0;
  3973. a1298 19
  3974. #ifdef _AMIGA
  3975. /* This is even more ugly. For the CBMVersion keyword we allow a trailing
  3976.    newline to end the keyword value, too. But we also need this newline for
  3977.    standard RCS processing. Otherwise the line count would be lost. So if
  3978.    for CBMVersion the keyword value is not delimited by KDELIM, it has
  3979.    been a newline end. We now need to start processing of this "left over"
  3980.    newline.
  3981. */
  3982.                         if(matchresult == CBMVersion)
  3983.                         {
  3984.                             int l = strlen(AMIGA_specialstring);
  3985.  
  3986.                             if(!l || AMIGA_specialstring[l - 1] != KDELIM)
  3987.                             {
  3988.                                 c = '\n';
  3989.                                 continue;   /* last c handled properly */
  3990.                             } /* if */
  3991.                         } /* if */
  3992. #endif /* _AMIGA */
  3993. d1409 4
  3994. a1412 2
  3995. /* For CBMVersion, we don't need to add anything as this is handled
  3996.    separately in expandline().
  3997. d1418 9
  3998. @
  3999.  
  4000.  
  4001. 5.11.1.9
  4002. log
  4003. @Cleaned up handling of terminating char for CBMVersion.
  4004. There was a bug in setting up a value for the first time.
  4005. @
  4006. text
  4007. @a38 3
  4008.  * Revision 5.11.1.8  1994/08/01  20:53:56  heinz
  4009.  * Fixed handling of a newline terminated CBMVersion string.
  4010.  *
  4011. d181 1
  4012. a181 1
  4013. libId(editId, "$Id: rcsedit.c,v 5.11.1.8 1994/08/01 20:53:56 heinz Exp heinz $")
  4014. d191 2
  4015. a192 2
  4016.    the string we create from expandline to keyreplace. I don't want to patch
  4017.    into the function declarations. */
  4018. d844 2
  4019. a845 5
  4020. /* We need this buffer for a CBMVersion string increment. This might not be
  4021.    a very nice way to do all this. But it is easy for now. We also need a
  4022.    place to remember the terminating char for CBMVersion to count the
  4023.    newlines correctly!
  4024. */
  4025. a847 1
  4026.         int lastc;
  4027. d955 1
  4028. a955 4
  4029.                                   /* Save the actual delimiter for a later check! */
  4030.                                   lastc = c;
  4031.  
  4032.                                   /* Fake an OK! */
  4033. a1003 2
  4034.                                         int l;
  4035.  
  4036. d1006 2
  4037. a1007 4
  4038.  
  4039.                                         l = strlen(CBMbuf);
  4040.                                         CBMbuf[l] = lastc;
  4041.                                         CBMbuf[l + 1] = 0;
  4042. d1307 4
  4043. a1310 2
  4044.    standard RCS processing. Otherwise the line count would be lost. We now
  4045.    might need to start processing this "left over" newline.
  4046. d1314 3
  4047. a1316 1
  4048.                             if((c = lastc) == '\n')
  4049. d1318 1
  4050. @
  4051.  
  4052.  
  4053. 5.11.1.10
  4054. log
  4055. @lastc wasn't always set correctly.
  4056. @
  4057. text
  4058. @a38 4
  4059.  * Revision 5.11.1.9  1994/08/05  16:43:13  heinz
  4060.  * Cleaned up handling of terminating char for CBMVersion.
  4061.  * There was a bug in setting up a value for the first time.
  4062.  *
  4063. d184 1
  4064. a184 1
  4065. libId(editId, "$Id: rcsedit.c,v 5.11.1.9 1994/08/05 16:43:13 heinz Exp heinz $")
  4066. a921 6
  4067. #ifdef _AMIGA
  4068. /* Preinit lastc with the current c. We definitely need this for CBMVersion,
  4069.    and it does not hurt otherwise.
  4070. */
  4071.                         lastc = c;
  4072. #endif /* _AMIGA */
  4073. @
  4074.