diff options
Diffstat (limited to 'contrib/cvs')
-rw-r--r-- | contrib/cvs/FREEBSD-upgrade | 55 | ||||
-rw-r--r-- | contrib/cvs/README.VMS | 159 | ||||
-rw-r--r-- | contrib/cvs/doc/DIFFUTILS-2.7-BUG | 263 | ||||
-rw-r--r-- | contrib/cvs/lib/getwd.c | 33 | ||||
-rw-r--r-- | contrib/cvs/lib/strdup.c | 39 | ||||
-rw-r--r-- | contrib/cvs/lib/strippath.c | 80 | ||||
-rw-r--r-- | contrib/cvs/src/NOTES | 60 | ||||
-rw-r--r-- | contrib/cvs/src/README-rm-add | 31 | ||||
-rw-r--r-- | contrib/cvs/src/commit.c | 132 | ||||
-rw-r--r-- | contrib/cvs/src/cvs.h | 16 | ||||
-rw-r--r-- | contrib/cvs/src/diff.c | 25 | ||||
-rw-r--r-- | contrib/cvs/src/import.c | 213 | ||||
-rw-r--r-- | contrib/cvs/src/lock.c | 19 | ||||
-rw-r--r-- | contrib/cvs/src/logmsg.c | 67 | ||||
-rw-r--r-- | contrib/cvs/src/main.c | 83 | ||||
-rw-r--r-- | contrib/cvs/src/mkmodules.c | 6 | ||||
-rw-r--r-- | contrib/cvs/src/rcs.c | 2617 | ||||
-rw-r--r-- | contrib/cvs/src/rcs.h | 11 | ||||
-rw-r--r-- | contrib/cvs/src/rcscmds.c | 179 | ||||
-rw-r--r-- | contrib/cvs/src/recurse.c | 11 | ||||
-rw-r--r-- | contrib/cvs/src/server.c | 93 | ||||
-rw-r--r-- | contrib/cvs/src/update.c | 652 |
22 files changed, 1329 insertions, 3515 deletions
diff --git a/contrib/cvs/FREEBSD-upgrade b/contrib/cvs/FREEBSD-upgrade new file mode 100644 index 0000000..cb99959 --- /dev/null +++ b/contrib/cvs/FREEBSD-upgrade @@ -0,0 +1,55 @@ +This directory contains the virgin CVS source on the vendor branch. Do +not under any circumstances commit new versions onto the mainline, new +versions or official-patch versions must be imported. + +To prepare a new cvs dist for import, extract it into a fresh directory; + +The following generated files were deleted: +doc/*.ps +doc/*.info* +doc/*.aux* +doc/CVSvn.texi +lib/getdate.c +tools/pcl-cvs/*.ps +tools/pcl-cvs/*.aux +tools/pcl-cvs/texinfo.tex +cvsnt.* +cvs.spec +build.com +src/build_src.com +lib/build_lib.com +diff/build_diff.com +README.VMS + +The following non-freebsd-specific directories were deleted: +macintosh +os2 +emx +windows-NT +vms +contrib/elib (this has a .tar.Z in it) +zlib + +CVS has RCS Id, Name and Header tags. It needs to be imported with -ko. + +It is imported from it's top level directory something like this: + cvs -n import -ko src/contrib/cvs CYCLIC v<version> + +The -n option is "dont do anything" so you can see what is about to happen +first. Remove it when it looks ok. + +The initial import was done with: + cvs import -ko src/contrib/cvs CYCLIC v1_8_1 + +When new versions are imported, cvs will give instructions on how to merge +the local and vendor changes when/if conflicts arise.. + +The developers can be reached at: <devel-cvs@cyclic.com>. Local changes +that are suitable for public consumption should be submitted for inclusion +in future releases. + +peter@freebsd.org - 20 Aug 1996 + +Current local changes: + - CVS_LOCAL_BRANCH_NUM environment variable support for choosing the + magic branch number. (for CVSup local-commit support) diff --git a/contrib/cvs/README.VMS b/contrib/cvs/README.VMS deleted file mode 100644 index b32ed8f..0000000 --- a/contrib/cvs/README.VMS +++ /dev/null @@ -1,159 +0,0 @@ - CVS port to VMS - -DISCLAIMER: This port must be considered experimental. Although -previous versions have been in use at one large site since about -October, 1995, and the port is believed to be quite usable, various -VMS-specific quirks are known and the port cannot be considered as -mature as the ports to, say, Windows NT or unix. As always, future -progress of this port will depend on volunteer and customer interest. - -This port is of the CVS client only. Or in other words, the port -implements the full set of CVS commands, but cannot access -repositories located on the local machine. The repository must live -on another machine (a Unix box) which runs a complete port of CVS. - -Most (all?) work to date has been done on OpenVMS/AXP 6.2. Other VMS -variants might work too. - -You will also need GNU patch installed on your system. Here's a list -of ftp servers which have VMS GNU resources, taken from - - ftp://prep.ai.mit.edu/pub/gnu/vms.README - - mvb.saic.com - wuarchive.wustl.edu - ftp.wku.edu - ftp.spc.edu - ftp.stacken.kth.se - -Please send bug reports to bug-cvs@prep.ai.mit.edu. - -As of CVS 1.5.something, this port passed most of the tests in -[.src]sanity.sh. I say "most" because some tests to not apply to the -CVS client. The tests were run by hand because the VMS POSIX shell -was incapable of running the script. The tests that sanity.sh -provides are not conclusive but at least provides some assurance that -the client is usable. - -To compile, you will need DEC C (CC), DEC UCX, and of course DCL -installed on your machine. Just type "@build" in the top level -directory. This will build the sources in each subdirectory, and link -the executable [.src]cvs.exe - -Copy the executable to an appropriate directory, and define the symbol "CVS" -in a .COM file which everyone running CVS will need to run. Here's an example -of what needs to be done. - -$ CVS :== $YOUR_DEVICE:[YOUR.DIRECTORY.CVS]CVS.EXE - -Accessing a remote repository can happen in several ways. - -1. pserver -2. rsh - privileged (default) -3. rsh - unprivileged (on VMS side) - -Here's how to do each of the above: - -------------------------------------------------------------------------------- -1. pserver. This is the preferred way. It works just as it is -documented in the CVS manual (see the README file in the CVS -distribution for more information on the manual). - -------------------------------------------------------------------------------- -2. Using CVS internal rsh support (privileged) - -VMS's RSH is unusable for CVS's purposes (that is, the one in UCX. -Don't know about Multinet). However, there is code within CVS to -emulate RSH for purposes of contacting a CVS server "in the usual way" -via rshd. Unfortunately, this requires the VMS CVS client to be -installed with OPER privilege, by your system administrator. - -RSH uses privileged ports and trusted software/hosts to determine -which user on the client side is trying to connect. Part of this -security is due to the fact that on VMS or UNIX, a non privileged -process is not permitted to bind a socket to a privileged port. - -If rshd receives a connection on a non-privileged port, the connection is -immediately aborted. Only connections arriving from a privileged port will -be authenticated and served. The CVS client will therefore need privileges -under VMS to produce such a connection. - -*** Please note that no careful examination has been done of the security - implications of installing CVS with the OPER privilege. If some hole - exists, then by doing so, you will enable users who are already on - your system to gain unauthorized privileges *** - -------------------------------------------------------------------------------- -3. Using CVS internal rsh support (non-privileged) - -There is a workaround, but this is one case where I think the cure is worse -than the disease. If you patch an rshd to not care that the RSH originating -port is "non-privileged", the CVS VMS client will allow you to define the -logical CVS_RCMD_PORT to the port number where this patched rshd will be -listening. I leave the talk of patching rshd to the gentle reader and his/her -friendly system administrator. - -If I put an entry in my /etc/services file: - -cvs_rcmd 4381/tcp cvs_rcmd - -And add a line to /etc/inetd.conf, then restart inetd via "kill -1" - -cvs_rcmd stream tcp nowait root /usr/sbin/tcpd /usr/local/sbin/cvs_rcmd - -On the VMS side, you will have to do this: - -$ define CVS_RCMD_PORT 4381 - -Then run CVS in the "usual way". - -Note that the patched rshd will need to be invoked via inetd as root, so it can -authenticate and _become_ the intended user, the same as the regular rshd. - -***Please note that you will be installing a security hole by doing this.*** - -Please also note that this security hole is no larger than allowing a -Macintosh, PC (OS/2, NT, etc.) to have it's hostname in any .rhosts file, -as any user can create a privileged socket without authentication, under these -environments. In fact, existing ports of CVS to these environment use this -to their advantage. - -------------------------------------------------------------------------------- -Wildcard expansion is not yet implemented (i.e. CVS COMMIT *.c won't -work.) I think that expand_wild should be calling lib$findfile -(util.c in gzip is said to provide an example), but noone has gotten -around to implementing this. - -Log messages must be entered on the command line using -m or -F. You -can use -e or define the logical EDITOR to cause CVS to try other -editors (TPU.EXE or any other editor which wants DCL command parsing -will not work) if you want to test what's available on your system. I -haven't tested this, but if you install vi or emacs, chances are it -will probably work. Just make sure the .EXE files are in a directory -listed in VAXC$PATH (is this a typo for DCL$PATH? Also, will a -logical name work?). If someone gets around to implementing it, we -should probably be using the callable editors (e.g. TPU$TPU), although -of course we also need interface(s) which are not locked into any -particular editors. - ----------------------------------------- - -Notes regarding compiling on VAX/VMS 6.2 (not Alpha) (These are items -which hopefully will have cleaner solutions in the future, but here is -how to get around them for now): - -* Need to compile lib/getdate.c with vaxc instead of decc to avoid a -compiler bugcheck. Therefore one must add SYS$LIBRARY:VAXCRTL/LIBRARY -to the link. - -* In src/ignore.c, change lstat to stat. In vms/filesubr.c, change -"#ifdef S_ISLNK" to "#if 0". - -* Ignore the warnings in vms/vmsmunch.c; the system include file -declares something as an int when it should be void *. Not *our* -fault! - -Credits: - -Initial VMS port by Benjamin J. Lee <benjamin@cyclic.com>, Cyclic -Software, October 1, 1995 (Update March 1, 1996). diff --git a/contrib/cvs/doc/DIFFUTILS-2.7-BUG b/contrib/cvs/doc/DIFFUTILS-2.7-BUG deleted file mode 100644 index f258ee7..0000000 --- a/contrib/cvs/doc/DIFFUTILS-2.7-BUG +++ /dev/null @@ -1,263 +0,0 @@ -The enclosed two messages describe a bug in GNU diff 2.6 and 2.7 which -may cause CVS to perform an erroneous merge. You may wish to use GNU -diff 2.5 or apply the patch supplied by Loren James Rittle below. It -would be nice to add this to the CVS testsuite, but I haven't done so -because probably a lot of people who would like to run the testsuite -are using the buggy diff. - -From: friedman@splode.com (Noah Friedman) -To: bug-gnu-utils@prep.ai.mit.edu -Cc: info-cvs@prep.ai.mit.edu -Subject: diffutils 2.7 -- diff3 merge bug -Date: Tue, 29 Oct 96 17:02:54 CST - -I believe a change first introduced in GNU diff 2.6 causes diff3 sometimes -to produce incorrect merges. - -Since this is a not a bug in CVS itself but can cause commits to CVS -repositories to be incorrect, am warning info-cvs@prep.ai.mit.edu as -well as reporting the bug to bug-gnu-utils@prep.ai.mit.edu. - -I am including a simple test case as well as some sample outputs from -different versions of `diff' and `diff3' in the enclosed shar archive. -In addition, the file DESCRIPTION in that archive describes the problem -more fully. - -If anyone has any advice for how to fix or to work around this bug, I would -appreciate it. Using diff 2.5 seems like the most immediately obvious -solution, but I don't know if it will introduce other problems. -I do not understand the algorithms used by GNU diff well enough to suggest -any patches. - -Unshar and enjoy. ;-) - -begin 666 merge-testcase.shar.gz -M'XL(",&,=C("`VUE<F=E+71E<W1C87-E+G-H87(`[5Q[;]M&$O_[^"FF;E#9 -M@41)?C!GYX&V;MPS<$F+V+FZP.&:%;F26)-<E;NTHC[NL]_,+%^29;USUP-L -M&)9$[CQW]C?#W;$^_ZS="Y.V'CJ?P_4PU("_`O101A&(U!^&=Q+V1ZD*,E\& -MT)O`MV_?XVV19B:,-!R[W0.72!7(CR85O@$SE-`/(ZFAGZH8/Q)'RZD)6B"_ -M$`<IT"J6<''Y]]=-2&6L[B2RD7<RG2!%,H">[*M4,K</A8X-B,)$@N@IXH6W -M$C"3$0[00^;40%60RQL12%`)=$]/O5:WTSH\A:YW=G(,YU?79,*+?AK*(!;) -MEWH4J4"ZOHI?D157*DM]"4&82M^H=`)CH>%#.]-I.U*^B-HZ]=N#)&L'8;_/ -M#FC',AW(EI':^$++7('7'T-MR`CKAW&(SGR:*/,4K0+4/1VGH3&H?9;@?131 -M\AOD>#V2?MA'W2P;G@_R-?@J,2),]!E>C60R,$.(47&P/XF(R7DM_LE?9MXN -M^T%R_#DZ]:"5CO-?F+:M7;QI3=!-VE(\ZQRM0A'CM%D"[_1X%0(5!3*U%,<G -MAPLHWHA;25[FL8?=PV<+QG[S^NK\W>7WUY??O<U9=Q;92Y-\Z)Y87>I&>R>K -MD)'-=:I5A'ESA!T_6X'LOK"3%52LYH?N!ZM,:2%P'FFW>[1HJJ[>OWGSU;L? -M,;2-ROPAM$0,W<,C_#TY/3V%)T]<>_U5.Y!W[23#17/XZHNN$_:!>,!GT.K7 -M";[XPE['JP7M<P8%!WC5_,277O)?1T9:3E\_PX_2'ZK\!1H_?/7N[>7;;\\` -M5RI"DD8$H#5LPAC?BWBD78!SE>@09P@&TO`*%TG0*#CLX0HU(HKH.L'D/S^P -MI$83$46;-.QE!C$T3/CF!08MO$<,"4THM>NZ>X4^_=!)XQE;"P,9%U[6?Q:O -MTNFQ+^N^#*`Q`UZE]ZP_/D(+_%0*MK/"Q!DB'![?XMU[W,B.0AP:TUBH9Z.< -MS9N])]T]^.PEOFGY>W-4TK?A:$0J+39\GU`!1(0&!!-,3C@#^J!1Q$'%+D]; -M*S`T./2`[-4XBPW=_M=-N]V`%R^@<?6WK][]]/J[BP:\6L%.I]V&KS-C5.+^ -M+.Z$<^.,A'\K!A)2C"85NV(TBD(?_:Z2YW@WC$<J-<5-2CON4[H^RGHX#/Q( -M8`JQ#)W?G!N`]E.XI$A,3"A0/.9S>Q<3$28.C.H!QN!^IPF=`XI?^%6F"N\% -M>),^#F4X&!J7.,%3^%%E$&<X,9C_L"1(,'XQ8D44(A5ZS`Q50-D<2X213*-) -M[3;G[MQ0RZQ-+[G:N4K[!W2-U4;'9L@#+Y%U?.$G$YI(GJM(I?`2^-7M1>BM -MY_E]7%BB%\G@^OZX02HFS^ML+C")XEUZ<0/9%UED^!++`_C#^<,I)A(G^4D% -M%1:I.H>GW9-##U/D"E,,X`\Q0T/'\Y:/__WW,B(M[&"1T%\2C7V!X<W0PWKZ -M*DO,R[T/8Q]:/KQ8)O(#@0TO-T[Y\A?8>U+QV;,:%:BVD-59'D\B`DUSCOR: -MX&=I*M&S?*7.F#!A5?RBE/80?"W"$Z+;)9RP'CM$$\MO2S`IC'S$DI6Q!#VU -M#$YHR&J(4C);"BHWN1>Q<KB322@3?,#`<A[K@8SR*>!3#H2EA[FDJ+LX]UP/ -MET^@X6,3)DWKWF;=OP"O?\G".Q'1JC,J)Z*?OE*H7B+'E9.>3]]VR<NP/\7: -MLBU&SG-UB(*0A%XF]B4GI;<Y>6TV^"FP$`U+)*Z&Q,\6(7&Y/E8#8CM\/1SF -M=;PI#+/`"H6YYMX,A8G3+`@CNQV!,#^.;(+"3+A+&+::[!"'<X9;`G%IYR,2 -M_TFKND<`_D0`O+`4KM;%:@B<CU\/@NT2WA2#K<@*A'EK:C,09E:S*(S\MD/A -M8F]K#?PM2':`O*7T[3&W8K49VM:M<KZYO+@XPB5%.U%'_"G_@"N=Z<Z<F[^T -MGNSSN`-HO>:8G4[;,Q$TL[-YPWNO:'(ZR&*</,T8@8[32#E68/"&Q*MCB>$9 -M2"-38LJ[X[CX9.K+$7D!N;".#6U1%@.9/C-L(.",I/65F9*$ZW*$R,];P*3$ -MQ.X>WXET@J)&,@F(!@&`R,)DE!G>9'8=8EW?/20O6">@#P2T6D.,SE]5TJ(M -M=/VRV\%+B_V`L9)F$KW!K*L=QII_Y[/N3K&>=GB=\S*(\;K=.1`S%0P+P:4: -MN1*LE%&Z)J"48BHHX3WKM:"D8#(+(LAI.Q"I;7JO@2,UJAU`25V'[=%DBMMF -M@#)CGF-/6J2AJ+!G-KCHL#Q*\TJ+S2=*&`]YT=.Z9KU(O_QX3#MAXJN4]DEI -M^Q@+$4W#$\ATL2-<GAO!H>OA3%-=(%.L'E@!7V0:N?@JCD.DQ?KK_!]7R&JD -M=$B;T9*O]6C=YW*:H!5<`BZ81)FP/R$YYQ,?ZP3G2O7-6-A`)ZQ`);&DBD$0 -M;E'-IZ&7#5J#)&NQ1E^.4)`K0A=ENS+(L+ASD+/)]ZVG>*`6?,JGHDB-<06* -M9$">"7'Q8I4K42IO<S/:H:%-A[`-ZU["2#2CSP4ELWC[^H<KL(N.(5:8:J\< -M"<Z*DG'/==WO16J04M"$6/\V2Z<BQJK,$!JB'VG_7G[$X,'1)$2+&"-.6T[V -M7-&%]SI#4)X0X/*19P_?Z!@OR;2)GC%\/LE[_U-#(L+^U-TCQ9PWDUIDU".F -M\@[-B/9E(M)0Y=9\90W.S_1X`!V%^G2@1U$PE/XM>@KMH8Q""6<DU8@P_@8X -M+_7#%(5B4:TQ#P0RDC9(^UGBD],XP11!Q'%E)\C.&C$I0VI2\M02]0D*ID3- -MZ8B4&*'?*8H$^G5(]59=#@:(C$<<K<3*BJU)I4.3K\D2R=%MHQ'O<FQ@`/(Q -MKEWA$"C)DV=5I"/A/.KD7:@R'4U:UMB@4B$_8I[1OU%))UY7J@BW/@.KKT9E -M`%:NL)K<DS"DA2(1Z%@ISAVT-*[+4^ZI1-><2:G-F<Q'9]UHC^;'!)R,6;6) -MI56D3`/V$OG:S,Q],=Y!AEQ-W,EH4E<.'CCHXW!.\J<>GR=%0`%==B[L0V7? -M(9EFF$H)=R@3'8(FE.T`/*B.:2=N$:'H&>?^22B;,>=<==HK/`)#-TMNZ8X( -M$%AI*5C4I<=2F>(BCLO0P34TQVQOKMEV]G-0K`";1U2F.?=,\W+3[!HH)#S- -M@X"`5N!LJ0$&:K[2PK3P`'"VEBFM)`I)AQZ_N:XCV/9MF-]*:R'F%YJ3/#ZM -M$V1,_0)<81;^H6'&R2>EK"T)12LF%+EF+*E-8JH:XV":BDM&>RN1P:S@A\H_ -ML"Z$G2HL2`MGLNNI@R1Q"'G4.&'M65H!Q[$PB&^ZOF1Y3"ZO5-A94>$RQ5C5 -M[:(GSJ1C/BWH9G2F`,K"D?5H,\]6ER@O"N6==&*LORS2QP@(.NSE*:F,.4-) -MTU+Q-#)O*S!@_?B>4]W#B,+E6#C.75[M'I[.J79G*Y6%!>_4X)5JWGHMM6;9 -M6Q=65;ZV!6.MTK?&:+;Z)6[;E;_SP&;U,G@.]0[*X7DZ;5\6S^6Z67G\@-G. -MC>M6K4\M',&?-GR\=`X[S:-.T#UU7@#^;K5'1^3K;]+E5`_NTDW?7[A-9X>V -M^763C3JB^\URJ;;J[,=E8I=OU7F=><CRT"0O1)BY1"LAS;SP7!-QY@FO/7-W -MUCW)GL/OWN-WQ]L-_M1JH/7AIR+>(?K4--H=^-29;H<]TS:O`SU+MY^<KM?L -M_M7GO_DB6WPX7(U9>C(QPV[1Z02U7;Z")0<B^8"E<NN,%@E]!-T_!>C.1/=* -MF%NG60MR:XMR0\2MB:Z=EYQL"KBUO>39,Y.3G>"MMU6]YWV2>L_[)/6>M[MZ -MSUM:[WF/]=[_!?1T.P]"C[=)O>=M7.]YV]=[WH[K/>^_4.]YV]1[WJ>H][Q/ -M4>]Y.ZOWO&7UGK=EO7=X^C^O]^SB?02_3<'O?U(O;X>UZY=YWJ9EGK=UF><] -M4.8=/]L09Q>4><<[VM:;W>%?_]EZAL,.'[!G==O=4_8]SML]:L]QP6,WXY^] -MKWR5,O!PP1/HO$E?Z3'T'N%:SZ*SH;OA`^FL$O6R\&3#I](9GO=+PY/=E(;; -M8I;WR3#+^V28Y>T6L[Q'S'K\7YC'5NP5@-[;%.B]K8#>VPW0>PN`?OW_F7F` -MY\[_>R;_1_<U@#VGV`&0%[*W!^Z2TV9`73/)N;E(57P&<[YV`_;?*C&$B_S. -M@7.MSA;W)3KG_ADNFKYJ^7?W;UYEO9^E;\ZF6G>>T09!U;(I28#S3HZB28O$ -MS5'+^08!_@RN,]F$PU/XSC?\92+\-2)'9]UC:'6\3@?VSZ^N#YP+'S7Z=UL: -MOQUCD+>1NZ[V+O"QE1R8MP-JN_IIYP*?9LOV$VI+LDUZMK\+@:/HL<J_+J!H -M=,F;1'-SRAY%QZ8*(IEM:N)6F:LPX3:CX@M>J$52D!^(/W69AD;+J,^-CSXF -M(]NL-]V)ZBSN1$4T&HN4^QD?FAYJPBR:3XE96C;`DR;$<86.U)B$1EE@TX/& -M+![)6AMFK;^5VRJUX`&V.]1^)XU3]D^536T$9!^XD913R0?;N%\T86&BBA1% -M/W\92_YE-B[U'XD@"*F>:%8=4/66&J87IOPFG4!J/PU[>0-4WD[KQ(2D_2RR -MS7N7U&DY48GDUD-\BT+NJ$F,LN-0C<E/_?`C=0_CN[%*;Y$]I40[O=Q3>8G7 -MLRAPL.+!R:%J!2<8,^-[;;_+P79VX8*6,07CK:TI8D6A%\<R(`JL.52/&R\= -MK:+,6DGQ<0F!2AH&;A-4!D$M-+:7K0S:O$<N-P_CCR@XY%!+=+@IFAE%-,!@ -M,D-4(M/5UPNQ?CR),E'98,C?%I0-,)2-0_X8V28R<M;[Q$X)M9@E/ZL)VOB\ -M=;`\8YX<S<F8=<A:F"'+@2MEQ`)(U\R`A9`JX]GO5UDKY>5,9E,<<5J2XS!E --&.@X_P&I*Y$W($H``"'+ -` -end - -Date: Wed, 30 Oct 96 14:54:13 CST -From: Loren James Rittle <rittle@comm.mot.com> -To: friedman@splode.com -Cc: bug-gnu-utils@prep.ai.mit.edu, info-cvs@prep.ai.mit.edu -Subject: Re: diffutils 2.7 -- diff3 merge bug - -Noah, - -I have seen the problem you discuss in your e-mail, however I fail to -see how this situation is as critical as might be implied since it can -never arise without at least some user involvement (an update that -caused a merge is never automatically followed by a commit --- the -user has a chance to inspect the merged file). However, I will agree -that I don't always look very closely at non-conflicted merges before -checking them back in. - -You didn't give the exact CVS commands used to create a lossage, -but I added the following to your Makefile, to help me see the problem -in a CVS usage context: - -t-older: testcase-older - cp testcase-older t-older - -t-yours: testcase-yours - cp testcase-yours t-yours - -t-mine: testcase-mine - cp testcase-mine t-mine - -# Assume cvs-1.9 -cvs-test: t-older t-yours t-mine - rm -rf /tmp/cvs-test-root x x2 - cvs -d /tmp/cvs-test-root init - mkdir x - cp t-older x/testcase - cd x; cvs -d /tmp/cvs-test-root import -m '' x X X1 - rm -rf x - cvs -d /tmp/cvs-test-root co x - cvs -d /tmp/cvs-test-root co -d x2 x - cp t-yours x/testcase - cp t-mine x2/testcase - cd x; cvs ci -m '' - -cd x2; cvs ci -m '' - cd x2; cvs update - cat x2/testcase # at this point, user may commit blindly - -It looks like whomever added shift_boundaries() in analyze.c, which -seems to be the source of the diff3 induced mischief, already provided -a means to disable the boundary shifting optimization (at least with -a recompile). - -Here is the patch I applied to diff to disable this (currently) -overaggressive optimization: - -[ rittle@supra ]; diff -c analyze.c-old analyze.c -*** analyze.c-old Wed Oct 30 14:10:27 1996 ---- analyze.c Wed Oct 30 13:48:57 1996 -*************** -*** 616,622 **** - but usually it is cleaner to consider the following identical line - to be the "change". */ - -! int inhibit; - - static void - shift_boundaries (filevec) ---- 616,622 ---- - but usually it is cleaner to consider the following identical line - to be the "change". */ - -! int inhibit = 1; - - static void - shift_boundaries (filevec) - -Now, diff-2.7 with the above patch produces: - -[ rittle@supra ]; make diff-mine-yours 'DIFF=/usr/src/diffutils-2.7/diff' -/usr/src/diffutils-2.7/diff -a --horizon-lines=11 -- testcase-mine testcase-yours; true -16,18c16,18 -< // _titleColor = Color.black; -< // _disabledTitleColor = Color.gray; -< // _titleFont = Font.defaultFont (); ---- -> _titleColor = Color.black; -> _disabledTitleColor = Color.gray; -> _titleFont = Font.defaultFont (); -20,30d19 -< -< /* Convenience constructor for instantiating a Button with -< * bounds x, y, width, and height. Equivalent to -< * foo = new Button (); -< * foo.init (x, y, width, height); -< */ -< public Button (int x, int y, int width, int height) -< { -< this (); -< init (x, y, width, height); -< } - -Whereas, stock diff-2.7 produces: - -[ rittle@supra ]; make diff-mine-yours -diff -a --horizon-lines=11 -- testcase-mine testcase-yours; true -16,29c16,18 -< // _titleColor = Color.black; -< // _disabledTitleColor = Color.gray; -< // _titleFont = Font.defaultFont (); -< } -< -< /* Convenience constructor for instantiating a Button with -< * bounds x, y, width, and height. Equivalent to -< * foo = new Button (); -< * foo.init (x, y, width, height); -< */ -< public Button (int x, int y, int width, int height) -< { -< this (); -< init (x, y, width, height); ---- -> _titleColor = Color.black; -> _disabledTitleColor = Color.gray; -> _titleFont = Font.defaultFont (); - -A better solution might be to disable the boundary shifting code -unless explicitly turned on via command line argument. That way -programs, like diff3, expecting unoptimized diff regions will work -correctly, yet users can get smaller diffs, if desired. The problem -is that diff3 doesn't properly track changes once they have been -optimized. - -BTW, I never did like the look of the `optimized diff regions', so I -consider this a good change for other reasons... :-) - -Enjoy! - -Regards, -Loren --- -Loren J. Rittle (rittle@comm.mot.com) PGP KeyIDs: 1024/B98B3249 2048/ADCE34A5 -Systems Technology Research (IL02/2240) FP1024:6810D8AB3029874DD7065BC52067EAFD -Motorola, Inc. FP2048:FDC0292446937F2A240BC07D42763672 -(847) 576-7794 Call for verification of fingerprints. diff --git a/contrib/cvs/lib/getwd.c b/contrib/cvs/lib/getwd.c deleted file mode 100644 index 5707dcb..0000000 --- a/contrib/cvs/lib/getwd.c +++ /dev/null @@ -1,33 +0,0 @@ -/* getwd.c -- get current working directory pathname - Copyright (C) 1992 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. */ - -/* Some systems which include both getwd() and getcwd() have an implementation - of getwd() which is much faster than getcwd(). As a result, we use the - system's getwd() if it is available */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "system.h" - -/* Get the current working directory into PATHNAME */ - -char * -getwd (pathname) - char *pathname; -{ - char *getcwd(); - - return (getcwd(pathname, PATH_MAX)); -} diff --git a/contrib/cvs/lib/strdup.c b/contrib/cvs/lib/strdup.c deleted file mode 100644 index c81969d..0000000 --- a/contrib/cvs/lib/strdup.c +++ /dev/null @@ -1,39 +0,0 @@ -/* strdup.c -- return a newly allocated copy of a string - Copyright (C) 1990 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef STDC_HEADERS -#include <string.h> -#include <stdlib.h> -#else -char *malloc (); -char *strcpy (); -#endif - -/* Return a newly allocated copy of STR, - or 0 if out of memory. */ - -char * -strdup (str) - char *str; -{ - char *newstr; - - newstr = (char *) malloc (strlen (str) + 1); - if (newstr) - strcpy (newstr, str); - return newstr; -} diff --git a/contrib/cvs/lib/strippath.c b/contrib/cvs/lib/strippath.c deleted file mode 100644 index 39687f9..0000000 --- a/contrib/cvs/lib/strippath.c +++ /dev/null @@ -1,80 +0,0 @@ -/* strippath.c -- remove unnecessary components from a path specifier - Copyright (C) 1992 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#if STDC_HEADERS || HAVE_STRING_H -#include <string.h> -/* An ANSI string.h and pre-ANSI memory.h might conflict. */ -#if !STDC_HEADERS && HAVE_MEMORY_H -#include <memory.h> -#endif /* not STDC_HEADERS and HAVE_MEMORY_H */ -#else /* not STDC_HJEADERS and not HAVE_STRING_H */ -#include <strings.h> -/* memory.h and strings.h conflict on some systems. */ -#endif /* not STDC_HEADERS and not HAVE_STRING_H */ - -#include <stdio.h> - -#if __STDC__ -static void remove_component(char *beginc, char *endc); -void strip_trailing_slashes(char *path); -#else -static void remove_component(); -void strip_trailing_slashes(); -#endif /* __STDC__ */ - -/* Remove unnecessary components from PATH. */ - -void -strip_path (path) - char *path; -{ - int stripped = 0; - char *cp, *slash; - - for (cp = path; (slash = strchr(cp, '/')) != NULL; cp = slash) - { - *slash = '\0'; - if ((!*cp && (cp != path || stripped)) || - strcmp(cp, ".") == 0 || strcmp(cp, "/") == 0) - { - stripped = 1; - remove_component(cp, slash); - slash = cp; - } - else - { - *slash++ = '/'; - } - } - strip_trailing_slashes(path); -} - -/* Remove the component delimited by BEGINC and ENDC from the path */ - -static void -remove_component (beginc, endc) - char *beginc; - char *endc; -{ - for (endc++; *endc; endc++) - *beginc++ = *endc; - *beginc = '\0'; -} diff --git a/contrib/cvs/src/NOTES b/contrib/cvs/src/NOTES deleted file mode 100644 index 646ebdf..0000000 --- a/contrib/cvs/src/NOTES +++ /dev/null @@ -1,60 +0,0 @@ -wishlist - Tue Nov 2 15:22:58 PST 1993 - -* bcopy -> memcpy & friends. - ** done 12/18/93 - -* remove static buffers. -* replace list & node cache with recursive obstacks, (xmalloc, - getnode, getlist) -* check all io functions for error return codes. also check all - system calls. -* error check mkdir. - ---- -Old notes... - -* All sizing limits are gone. The rest of these items were incidental - in that effort. - -* login name from history was duplicated. taught existing routine to - cache and use that instead. Also add routines to cache uid, pid, - etc. - -* ign strings were never freed. Now they are. - -* there was a printf("... %s ...", cp) vs *cp bug in history.c. Now - fixed. - -* The environment variables TMPDIR, HOME, and LOGNAME were not - honored. Now they are. - -* extra line inserted by do_editor() is gone. Then obviated. Editor - is now called exactly once per checkin. - -* revised editor behaviour. Never use /dev/tty. If the editor - session fails, we haven't yet done anything. Therefor the user can - safely rerun cvs and we should just fail. Also use the editor for - initial log messages on added files. Also omit the confirmation - when adding directories. Adding directories will require an - explicit "commit" step soon. Make it possible to prevent null login - messages using #define REQUIRE_LOG_MESSAGES - -* prototypes for all callbacks. - -* all callbacks get ref pointers. - -* do_recursion/start_recursion now use recusion_frame's rather than a - list of a lot of pointers and global variables. - -* corrected types on status_dirproc(). - -* CONFIRM_DIRECTORY_ADDS - -* re_comp was innappropriate in a few places. I've eliminated it. - -* FORCE_MESSAGE_ON_ADD - -* So I built a regression test. Let's call it a sanity check to be - less ambitious. It exposed that cvs is difficult to call from - scripts. - diff --git a/contrib/cvs/src/README-rm-add b/contrib/cvs/src/README-rm-add deleted file mode 100644 index 87fd7c6..0000000 --- a/contrib/cvs/src/README-rm-add +++ /dev/null @@ -1,31 +0,0 @@ -WHAT THE "DEATH SUPPORT" FEATURES DO: - -(Some of the death support stuff is documented in the main manual, but -this file is for stuff which noone has gotten around to adding to the -main manual yet). - -CVS with death support can record when a file is active, or alive, and -when it is removed, or dead. With this facility you can record the -history of a file, including the fact that at some point in its life -the file was removed and then later added. - -Files can now be added or removed in a branch and later merged -into the trunk. - - cvs update -A - touch a b c - cvs add a b c ; cvs ci -m "added" a b c - cvs tag -b branchtag - cvs update -r branchtag - touch d ; cvs add d - rm a ; cvs rm a - cvs ci -m "added d, removed a" - cvs update -A - cvs update -jbranchtag - -Added and removed files may also be merged between branches. - -Files removed in the trunk may be merged into branches. - -Files added on the trunk are a special case. They cannot be merged -into a branch. Instead, simply branch the file by hand. diff --git a/contrib/cvs/src/commit.c b/contrib/cvs/src/commit.c index 4aa2438..71b491f 100644 --- a/contrib/cvs/src/commit.c +++ b/contrib/cvs/src/commit.c @@ -19,7 +19,6 @@ #include "getline.h" #include "edit.h" #include "fileattr.h" -#include "hardlink.h" static Dtype check_direntproc PROTO ((void *callerdat, char *dir, char *repos, char *update_dir, @@ -82,6 +81,7 @@ static List *mulist; static char *message; static time_t last_register_time; + static const char *const commit_usage[] = { "Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n", @@ -505,7 +505,7 @@ commit (argc, argv) /* Run the user-defined script to verify/check information in *the log message */ - do_verify (message, (char *)NULL); + do_verify (&message, (char *)NULL); /* We always send some sort of message, even if empty. */ /* FIXME: is that true? There seems to be some code in do_editor @@ -622,23 +622,10 @@ commit (argc, argv) lock_tree_for_write (argc, argv, local, aflag); /* - * Set up the master update list and hard link list + * Set up the master update list */ mulist = getlist (); -#ifdef PRESERVE_PERMISSIONS_SUPPORT - if (preserve_perms) - { - hardlist = getlist (); - - /* - * We need to save the working directory so that - * check_fileproc can construct a full pathname for each file. - */ - working_dir = xgetwd(); - } -#endif - /* * Run the recursion processor to verify the files are all up-to-date */ @@ -651,17 +638,6 @@ commit (argc, argv) error (1, 0, "correct above errors first!"); } -#ifdef PRESERVE_PERMISSIONS_SUPPORT - if (preserve_perms) - { - /* hardlist now includes a complete index of the files - to be committed, indexed by inode. For each inode, - compile a list of the files that are linked to it, - and save this list in each file's hardlink_info node. */ - (void) walklist (hardlist, cache_hardlinks_proc, NULL); - } -#endif - /* * Run the recursion processor to commit the files */ @@ -1016,43 +992,6 @@ warning: file `%s' seems to still contain conflict indicators", ci->options = xstrdup(vers->options); p->data = (char *) ci; (void) addnode (cilist, p); - -#ifdef PRESERVE_PERMISSIONS_SUPPORT - if (preserve_perms) - { - /* Add this file to hardlist, indexed on its inode. When - we are done, we can find out what files are hardlinked - to a given file by looking up its inode in hardlist. */ - char *fullpath; - Node *linkp; - struct hardlink_info *hlinfo; - - /* Get the full pathname of the current file. */ - fullpath = xmalloc (strlen(working_dir) + - strlen(finfo->fullname) + 2); - sprintf (fullpath, "%s/%s", working_dir, finfo->fullname); - - /* To permit following links in subdirectories, files - are keyed on finfo->fullname, not on finfo->name. */ - linkp = lookup_file_by_inode (fullpath); - - /* If linkp is NULL, the file doesn't exist... maybe - we're doing a remove operation? */ - if (linkp != NULL) - { - /* Create a new hardlink_info node, which will record - the current file's status and the links listed in its - `hardlinks' delta field. We will append this - hardlink_info node to the appropriate hardlist entry. */ - hlinfo = (struct hardlink_info *) - xmalloc (sizeof (struct hardlink_info)); - hlinfo->status = status; - hlinfo->links = NULL; - linkp->data = (char *) hlinfo; - } - } -#endif - break; case T_UNKNOWN: error (0, 0, "nothing known about `%s'", finfo->fullname); @@ -1239,7 +1178,7 @@ commit_fileproc (callerdat, finfo) got_message = 1; if (use_editor) do_editor (finfo->update_dir, &message, finfo->repository, ulist); - do_verify (message, finfo->repository); + do_verify (&message, finfo->repository); } p = findnode (cilist, finfo->file); @@ -1341,9 +1280,8 @@ commit_fileproc (callerdat, finfo) /* Doesn't matter, it won't get checked. */ SERVER_UPDATED, - (mode_t) -1, - (unsigned char *) NULL, - (struct buffer *) NULL); + (struct stat *) NULL, + (unsigned char *) NULL); } #endif } @@ -1530,7 +1468,7 @@ commit_direntproc (callerdat, dir, repos, update_dir, entries) got_message = 1; if (use_editor) do_editor (update_dir, &message, real_repos, ulist); - do_verify (message, real_repos); + do_verify (&message, real_repos); free (real_repos); return (R_PROCESS); } @@ -1707,8 +1645,9 @@ remove_file (finfo, tag, message) (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { - error (0, 0, - "failed to check out `%s'", finfo->fullname); + if (!quiet) + error (0, retcode == -1 ? errno : 0, + "failed to check out `%s'", finfo->fullname); return (1); } @@ -2042,21 +1981,13 @@ internal error: `%s' didn't move out of the attic", if (tag && newfile) { char *tmp; - FILE *fp; /* move the new file out of the way. */ fname = xmalloc (strlen (file) + sizeof (CVSADM) + sizeof (CVSPREFIX) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); rename_file (file, fname); - - /* Create empty FILE. Can't use copy_file with a DEVNULL - argument -- copy_file now ignores device files. */ - fp = fopen (file, "w"); - if (fp == NULL) - error (1, errno, "cannot open %s for writing", file); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", file); + copy_file (DEVNULL, file); tmp = xmalloc (strlen (file) + strlen (tag) + 80); /* commit a dead revision. */ @@ -2128,6 +2059,8 @@ internal error: `%s' didn't move out of the attic", char *head; char *magicrev; + fixbranch(rcsfile, sbranch); + head = RCS_getversion (rcsfile, NULL, NULL, 0, (int *) NULL); magicrev = RCS_magicrev (rcsfile, head); @@ -2225,31 +2158,18 @@ lock_RCS (user, rcs, rev, repository) { (void) RCS_lock(rcs, rev, 1); } - - /* We used to call RCS_rewrite here, and that might seem - appropriate in order to write out the locked revision - information. However, such a call would actually serve no - purpose. CVS locks will prevent any interference from other - CVS processes. The comment above rcs_internal_lockfile - explains that it is already unsafe to use RCS and CVS - simultaneously. It follows that writing out the locked - revision information here would add no additional security. - - If we ever do care about it, the proper fix is to create the - RCS lock file before calling this function, and maintain it - until the checkin is complete. - - The call to RCS_lock is still required at present, since in - some cases RCS_checkin will determine which revision to check - in by looking for a lock. FIXME: This is rather roundabout, - and a more straightforward approach would probably be easier to - understand. */ + RCS_rewrite (rcs, NULL, NULL); if (err == 0) { if (sbranch != NULL) free (sbranch); - sbranch = branch; + if (branch) + { + sbranch = branch; + } + else + sbranch = NULL; return (0); } @@ -2264,8 +2184,7 @@ lock_RCS (user, rcs, rev, repository) /* Called when "add"ing files to the RCS respository. It doesn't seem to be possible to get RCS to use the right mode, so we change it after - the fact. TODO: now that RCS has been librarified, we have the power - to change this. */ + the fact. */ static void fix_rcs_modes (rcs, user) @@ -2275,12 +2194,6 @@ fix_rcs_modes (rcs, user) struct stat sb; mode_t rcs_mode; -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* Do ye nothing to the modes on a symbolic link. */ - if (preserve_perms && islink (user)) - return; -#endif - if (CVS_STAT (user, &sb) < 0) { /* FIXME: Should be ->fullname. */ @@ -2290,9 +2203,6 @@ fix_rcs_modes (rcs, user) /* Now we compute the new mode. - TODO: decide whether this whole thing can/should be skipped - when `preserve_perms' is set. Almost certainly so. -twp - The algorithm that we use is: Write permission is always off (this is what RCS and CVS have always diff --git a/contrib/cvs/src/cvs.h b/contrib/cvs/src/cvs.h index 3a7e9e4..7e68639 100644 --- a/contrib/cvs/src/cvs.h +++ b/contrib/cvs/src/cvs.h @@ -187,6 +187,7 @@ extern int errno; #define CVSROOTADM_WRITERS "writers" #define CVSROOTADM_PASSWD "passwd" #define CVSROOTADM_CONFIG "config" +#define CVSROOTADM_OPTIONS "options" #define CVSNULLREPOS "Emptydir" /* an empty directory */ @@ -256,6 +257,8 @@ extern int errno; #define CVSREAD_ENV "CVSREAD" /* make files read-only */ #define CVSREAD_DFLT 0 /* writable files by default */ +#define CVSREADONLYFS_ENV "CVSREADONLYFS" /* repository is read-only */ + #define TMPDIR_ENV "TMPDIR" /* Temporary directory */ /* #define TMPDIR_DFLT Set by options.h */ @@ -363,6 +366,7 @@ extern int really_quiet, quiet; extern int use_editor; extern int cvswrite; extern mode_t cvsumask; +extern char *RCS_citag; /* Access method specified in CVSroot. */ typedef enum { @@ -383,6 +387,7 @@ extern char *emptydir_name PROTO ((void)); extern int trace; /* Show all commands */ extern int noexec; /* Don't modify disk anywhere */ +extern int readonlyfs; /* fail on all write locks; succeed all read locks */ extern int logoff; /* Don't write history entry */ #ifdef AUTH_SERVER_SUPPORT @@ -402,7 +407,6 @@ int RCS_merge PROTO((RCSNode *, char *, char *, char *, char *, char *)); #define RCS_FLAGS_DEAD 2 #define RCS_FLAGS_QUIET 4 #define RCS_FLAGS_MODTIME 8 -#define RCS_FLAGS_KEEPFILE 16 extern int RCS_exec_rcsdiff PROTO ((RCSNode *rcsfile, char *opts, char *options, @@ -423,7 +427,7 @@ DBM *open_module PROTO((void)); FILE *open_file PROTO((const char *, const char *)); List *Find_Directories PROTO((char *repository, int which, List *entries)); void Entries_Close PROTO((List *entries)); -List *Entries_Open PROTO ((int aflag, char *update_dir)); +List *Entries_Open PROTO((int aflag)); void Subdirs_Known PROTO((List *entries)); void Subdir_Register PROTO((List *, const char *, const char *)); void Subdir_Deregister PROTO((List *, const char *, const char *)); @@ -462,15 +466,14 @@ int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup)); int isdir PROTO((const char *file)); int isfile PROTO((const char *file)); int islink PROTO((const char *file)); -int isdevice PROTO ((const char *)); int isreadable PROTO((const char *file)); int iswritable PROTO((const char *file)); int isaccessible PROTO((const char *file, const int mode)); int isabsolute PROTO((const char *filename)); -char *xreadlink PROTO((const char *link)); char *last_component PROTO((char *path)); char *get_homedir PROTO ((void)); char *cvs_temp_name PROTO ((void)); +void parseopts PROTO ((const char *root)); int numdots PROTO((const char *s)); char *increment_revnum PROTO ((const char *)); @@ -558,7 +561,7 @@ void Update_Logfile PROTO((char *repository, char *xmessage, FILE * xlogfp, void do_editor PROTO((char *dir, char **messagep, char *repository, List * changes)); -void do_verify PROTO((char *message, char *repository)); +void do_verify PROTO((char **messagep, char *repository)); typedef int (*CALLBACKPROC) PROTO((int *pargc, char *argv[], char *where, char *mwhere, char *mfile, int shorten, int local_specified, @@ -730,9 +733,6 @@ void freevers_ts PROTO ((Vers_TS ** versp)); int Checkin PROTO ((int type, struct file_info *finfo, char *rcs, char *rev, char *tag, char *options, char *message)); int No_Difference PROTO ((struct file_info *finfo, Vers_TS *vers)); -/* TODO: can the finfo argument to special_file_mismatch be changed? -twp */ -int special_file_mismatch PROTO ((struct file_info *finfo, - char *rev1, char *rev2)); /* CVSADM_BASEREV stuff, from entries.c. */ extern char *base_get PROTO ((struct file_info *)); diff --git a/contrib/cvs/src/diff.c b/contrib/cvs/src/diff.c index 7a4c105..4690ed8 100644 --- a/contrib/cvs/src/diff.c +++ b/contrib/cvs/src/diff.c @@ -434,14 +434,16 @@ diff_fileproc (callerdat, finfo) exists = 0; /* special handling for TAG_HEAD */ if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0) - exists = vers->vn_rcs != NULL; + exists = (vers->vn_rcs != NULL && /* XXX ? */ + !RCS_isdead (vers->srcfile, vers->vn_rcs)); /*XXX*/ else { Vers_TS *xvers; xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0); - exists = xvers->vn_rcs != NULL; + exists = (vers->vn_rcs != NULL && + !RCS_isdead (vers->srcfile, vers->vn_rcs)); /*XXX*/ freevers_ts (&xvers); } if (exists) @@ -636,11 +638,13 @@ RCS file: ", 0); : vers->options), tmp, (RCSCHECKOUTPROC) NULL, (void *) NULL); - if (retcode != 0) + if (retcode == -1) { - diff_mark_errors (err); - return err; + (void) CVS_UNLINK (tmp); + error (1, errno, "fork failed during checkout of %s", + vers->srcfile->path); } + /* FIXME: what if retcode > 0? */ status = diff_exec (DEVNULL, tmp, opts, RUN_TTY); } @@ -655,11 +659,13 @@ RCS file: ", 0); *options ? options : vers->options, tmp, (RCSCHECKOUTPROC) NULL, (void *) NULL); - if (retcode != 0) + if (retcode == -1) { - diff_mark_errors (err); - return err; + (void) CVS_UNLINK (tmp); + error (1, errno, "fork failed during checkout of %s", + vers->srcfile->path); } + /* FIXME: what if retcode > 0? */ status = diff_exec (tmp, DEVNULL, opts, RUN_TTY); } @@ -715,8 +721,7 @@ RCS file: ", 0); if (empty_file == DIFF_REMOVED || (empty_file == DIFF_ADDED && use_rev2 != NULL)) { - if (CVS_UNLINK (tmp) < 0) - error (0, errno, "cannot remove %s", tmp); + (void) CVS_UNLINK (tmp); free (tmp); } diff --git a/contrib/cvs/src/import.c b/contrib/cvs/src/import.c index d79a10e..11fc99d 100644 --- a/contrib/cvs/src/import.c +++ b/contrib/cvs/src/import.c @@ -194,7 +194,7 @@ import (argc, argv) do_editor ((char *) NULL, &message, repository, (List *) NULL); } - do_verify (message, repository); + do_verify (&message, repository); msglen = message == NULL ? 0 : strlen (message); if (msglen == 0 || message[msglen - 1] != '\n') { @@ -260,11 +260,7 @@ import (argc, argv) tmpfile = cvs_temp_name (); if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL) error (1, errno, "cannot create temporary file `%s'", tmpfile); - /* On systems where we can unlink an open file, do so, so it will go - away no matter how we exit. FIXME-maybe: Should be checking for - errors but I'm not sure which error(s) we get if we are on a system - where one can't unlink open files. */ - (void) CVS_UNLINK (tmpfile); + (void) CVS_UNLINK (tmpfile); /* to be sure it goes away */ (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]); (void) fprintf (logfp, "Release Tags:\t"); for (i = 2; i < argc; i++) @@ -324,13 +320,11 @@ import (argc, argv) (void) addnode (ulist, p); Update_Logfile (repository, message, logfp, ulist); dellist (&ulist); - if (fclose (logfp) < 0) - error (0, errno, "error closing %s", tmpfile); + (void) fclose (logfp); /* Make sure the temporary file goes away, even on systems that don't let you delete a file that's in use. */ - if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno)) - error (0, errno, "cannot remove %s", tmpfile); + CVS_UNLINK (tmpfile); free (tmpfile); if (message) @@ -497,7 +491,7 @@ process_import_file (message, vfile, vtag, targc, targv) /* Reading all the entries for each file is fairly silly, and probably slow. But I am too lazy at the moment to do anything else. */ - entries = Entries_Open (0, NULL); + entries = Entries_Open (0); node = findnode_fn (entries, vfile); if (node != NULL) { @@ -983,7 +977,6 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, char *userfile; char *local_opt = key_opt; char *free_opt = NULL; - mode_t file_type; if (noexec) return (0); @@ -1011,39 +1004,18 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, which does not depend on what the client or server OS is, as documented in cvsclient.texi), but as long as the server just runs on unix it is a moot point. */ - - /* If PreservePermissions is set, then make sure that the file - is a plain file before trying to open it. Longstanding (although - often unpopular) CVS behavior has been to follow symlinks, so we - maintain that behavior if PreservePermissions is not on. - - NOTE: this error message used to be `cannot fstat', but is now - `cannot lstat'. I don't see a way around this, since we must - stat the file before opening it. -twp */ - - if (CVS_LSTAT (userfile, &sb) < 0) - error (1, errno, "cannot lstat %s", user); - file_type = sb.st_mode & S_IFMT; - - fpuser = NULL; - if (!preserve_perms || file_type == S_IFREG) + fpuser = CVS_FOPEN (userfile, + ((local_opt != NULL && strcmp (local_opt, "b") == 0) + ? "rb" + : "r") + ); + if (fpuser == NULL) { - fpuser = CVS_FOPEN (userfile, - ((local_opt != NULL && strcmp (local_opt, "b") == 0) - ? "rb" - : "r") - ); - if (fpuser == NULL) - { - /* not fatal, continue import */ - if (add_logfp != NULL) - fperror (add_logfp, 0, errno, - "ERROR: cannot read file %s", userfile); - error (0, errno, "ERROR: cannot read file %s", userfile); - goto read_error; - } + /* not fatal, continue import */ + fperror (add_logfp, 0, errno, "ERROR: cannot read file %s", userfile); + error (0, errno, "ERROR: cannot read file %s", userfile); + goto read_error; } - fprcs = CVS_FOPEN (rcs, "w+b"); if (fprcs == NULL) { @@ -1110,6 +1082,10 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, if (fprintf (fprcs, "\012") < 0) goto write_error; + /* Get information on modtime and mode. */ + if (fstat (fileno (fpuser), &sb) < 0) + error (1, errno, "cannot fstat %s", user); + /* Write the revision(s), with the date and author and so on (that is "delta" rather than "deltatext" from rcsfile(5)). */ if (add_vhead != NULL) @@ -1142,102 +1118,13 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, if (fprintf (fprcs, "next ;\012") < 0) goto write_error; - -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* Store initial permissions if necessary. */ - if (preserve_perms) - { - if (file_type == S_IFLNK) - { - char *link = xreadlink (userfile); - if (fprintf (fprcs, "symlink\t@") < 0 || - expand_at_signs (link, strlen (link), fprcs) < 0 || - fprintf (fprcs, "@;\012") < 0) - goto write_error; - free (link); - } - else - { - if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0) - goto write_error; - if (fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0) - goto write_error; - if (fprintf (fprcs, "permissions\t%o;\012", - sb.st_mode & 07777) < 0) - goto write_error; - switch (file_type) - { - case S_IFREG: break; - case S_IFCHR: - case S_IFBLK: - if (fprintf (fprcs, "special\t%s %lu;\012", - (file_type == S_IFCHR - ? "character" - : "block"), - (unsigned long) sb.st_rdev) < 0) - goto write_error; - break; - default: - error (0, 0, - "can't import %s: unknown kind of special file", - userfile); - } - } - } -#endif - if (add_vbranch != NULL) { if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 || fprintf (fprcs, "date %s; author %s; state Exp;\012", altdate1, author) < 0 || fprintf (fprcs, "branches ;\012") < 0 || - fprintf (fprcs, "next ;\012") < 0) - goto write_error; - -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* Store initial permissions if necessary. */ - if (preserve_perms) - { - if (file_type == S_IFLNK) - { - char *link = xreadlink (userfile); - if (fprintf (fprcs, "symlink\t@") < 0 || - expand_at_signs (link, strlen (link), fprcs) < 0 || - fprintf (fprcs, "@;\012") < 0) - goto write_error; - free (link); - } - else - { - if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0 || - fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0 || - fprintf (fprcs, "permissions\t%o;\012", - sb.st_mode & 07777) < 0) - goto write_error; - - switch (file_type) - { - case S_IFREG: break; - case S_IFCHR: - case S_IFBLK: - if (fprintf (fprcs, "special\t%s %lu;\012", - (file_type == S_IFCHR - ? "character" - : "block"), - (unsigned long) sb.st_rdev) < 0) - goto write_error; - break; - default: - error (0, 0, - "cannot import %s: special file of unknown type", - userfile); - } - } - } -#endif - - if (fprintf (fprcs, "\012") < 0) + fprintf (fprcs, "next ;\012\012") < 0) goto write_error; } } @@ -1283,9 +1170,7 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, goto write_error; } - /* Now copy over the contents of the file, expanding at signs. - If preserve_perms is set, do this only for regular files. */ - if (!preserve_perms || file_type == S_IFREG) + /* Now copy over the contents of the file, expanding at signs. */ { char buf[8192]; unsigned int len; @@ -1323,12 +1208,7 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, ierrno = errno; goto write_error_noclose; } - /* Close fpuser only if we opened it to begin with. */ - if (fpuser != NULL) - { - if (fclose (fpuser) < 0) - error (0, errno, "cannot close %s", user); - } + (void) fclose (fpuser); /* * Fix the modes on the RCS files. The user modes of the original @@ -1344,9 +1224,8 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, if (chmod (rcs, mode) < 0) { ierrno = errno; - if (add_logfp != NULL) - fperror (add_logfp, 0, ierrno, - "WARNING: cannot change mode of file %s", rcs); + fperror (add_logfp, 0, ierrno, + "WARNING: cannot change mode of file %s", rcs); error (0, ierrno, "WARNING: cannot change mode of file %s", rcs); err++; } @@ -1359,20 +1238,15 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, write_error: ierrno = errno; - if (fclose (fprcs) < 0) - error (0, errno, "cannot close %s", rcs); + (void) fclose (fprcs); write_error_noclose: - if (fclose (fpuser) < 0) - error (0, errno, "cannot close %s", user); - if (add_logfp != NULL) - fperror (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs); + (void) fclose (fpuser); + fperror (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs); error (0, ierrno, "ERROR: cannot write file %s", rcs); if (ierrno == ENOSPC) { - if (CVS_UNLINK (rcs) < 0) - error (0, errno, "cannot remove %s", rcs); - if (add_logfp != NULL) - fperror (add_logfp, 0, 0, "ERROR: out of space - aborting"); + (void) CVS_UNLINK (rcs); + fperror (add_logfp, 0, 0, "ERROR: out of space - aborting"); error (1, 0, "ERROR: out of space - aborting"); } read_error: @@ -1397,27 +1271,20 @@ expand_at_signs (buf, size, fp) off_t size; FILE *fp; { - register char *cp, *next; + char *cp, *end; - cp = buf; - while ((next = memchr (cp, '@', size)) != NULL) + errno = 0; + for (cp = buf, end = buf + size; cp < end; cp++) { - int len; - - ++next; - len = next - cp; - if (fwrite (cp, 1, len, fp) != len) - return EOF; - if (putc ('@', fp) == EOF) - return EOF; - cp = next; - size -= len; + if (*cp == '@') + { + if (putc ('@', fp) == EOF && errno != 0) + return EOF; + } + if (putc (*cp, fp) == EOF && errno != 0) + return (EOF); } - - if (fwrite (cp, 1, size, fp) != size) - return EOF; - - return 1; + return (1); } /* diff --git a/contrib/cvs/src/lock.c b/contrib/cvs/src/lock.c index 72818f3..0d5cef6 100644 --- a/contrib/cvs/src/lock.c +++ b/contrib/cvs/src/lock.c @@ -243,7 +243,7 @@ Reader_Lock (xrepository) FILE *fp; char *tmp; - if (noexec) + if (noexec || readonlyfs) return (0); /* we only do one directory at a time for read locks! */ @@ -319,6 +319,11 @@ Writer_Lock (list) if (noexec) return (0); + if (readonlyfs) { + error (0, 0, "write lock failed - read-only repository"); + return (1); + } + /* We only know how to do one list at a time */ if (locklist != (List *) NULL) { @@ -704,6 +709,7 @@ lock_obtained (repos) static int lock_filesdoneproc PROTO ((void *callerdat, int err, char *repository, char *update_dir, List *entries)); +static int fsortcmp PROTO((const Node * p, const Node * q)); /* * Create a list of repositories to lock @@ -732,6 +738,17 @@ lock_filesdoneproc (callerdat, err, repository, update_dir, entries) return (err); } +/* + * compare two lock list nodes (for sort) + */ +static int +fsortcmp (p, q) + const Node *p; + const Node *q; +{ + return (strcmp (p->key, q->key)); +} + void lock_tree_for_write (argc, argv, local, aflag) int argc; diff --git a/contrib/cvs/src/logmsg.c b/contrib/cvs/src/logmsg.c index 67194be..e471341 100644 --- a/contrib/cvs/src/logmsg.c +++ b/contrib/cvs/src/logmsg.c @@ -385,14 +385,20 @@ do_editor (dir, messagep, repository, changes) independant of the running of an editor for getting a message. */ void -do_verify (message, repository) - char *message; +do_verify (messagep, repository) + char **messagep; char *repository; { FILE *fp; char *fname; int retcode = 0; + char *line; + int line_length; + size_t line_chars_allocated; + char *p; + struct stat stbuf; + #ifdef CLIENT_SUPPORT if (client_active) /* The verification will happen on the server. */ @@ -406,7 +412,7 @@ do_verify (message, repository) /* If there's no message, then we have nothing to verify. Can this case happen? And if so why would we print a message? */ - if (message == NULL) + if (*messagep == NULL) { cvs_output ("No message to verify\n", 0); return; @@ -422,9 +428,9 @@ do_verify (message, repository) error (1, errno, "cannot create temporary file %s", fname); else { - fprintf (fp, "%s", message); - if ((message)[0] == '\0' || - (message)[strlen (message) - 1] != '\n') + fprintf (fp, "%s", *messagep); + if ((*messagep)[0] == '\0' || + (*messagep)[strlen (*messagep) - 1] != '\n') (void) fprintf (fp, "%s", "\n"); if (fclose (fp) == EOF) error (1, errno, "%s", fname); @@ -450,13 +456,62 @@ do_verify (message, repository) error (1, retcode == -1 ? errno : 0, "Message verification failed"); + } + + /* put the entire message back into the *messagep variable */ + + fp = open_file (fname, "r"); + if (fp == NULL) + { + error (1, errno, "cannot open temporary file %s", fname); + return; + } + + if (*messagep) + free (*messagep); + + if ( CVS_STAT (fname, &stbuf) != 0) + error (1, errno, "cannot find size of temp file %s", fname); + + if (stbuf.st_size == 0) + *messagep = NULL; + else + { + /* On NT, we might read less than st_size bytes, but we won't + read more. So this works. */ + *messagep = (char *) xmalloc (stbuf.st_size + 1); + *messagep[0] = '\0'; + } + + line = NULL; + line_chars_allocated = 0; + + if (*messagep) + { + p = *messagep; + while (1) + { + line_length = getline (&line, &line_chars_allocated, fp); + if (line_length == -1) + { + if (ferror (fp)) + error (0, errno, "warning: cannot read %s", fname); + break; + } + if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) + continue; + (void) strcpy (p, line); + p += line_length; } } + if (fclose (fp) < 0) + error (0, errno, "warning: cannot close %s", fname); /* Delete the temp file */ unlink_file (fname); free (fname); + } } } diff --git a/contrib/cvs/src/main.c b/contrib/cvs/src/main.c index 9465be6..89a10cd 100644 --- a/contrib/cvs/src/main.c +++ b/contrib/cvs/src/main.c @@ -40,6 +40,7 @@ int really_quiet = 0; int quiet = 0; int trace = 0; int noexec = 0; +int readonlyfs = 0; int logoff = 0; mode_t cvsumask = UMASK_DFLT; @@ -212,6 +213,7 @@ static const char *const opt_usage[] = " -l Turn history logging off.\n", " -n Do not execute anything that will change the disk.\n", " -t Show trace of program execution -- try with -n.\n", + " -R Assume repository is read-only, such as CDROM\n", " -v CVS version and copyright.\n", " -b bindir Find RCS programs in 'bindir'.\n", " -T tmpdir Use 'tmpdir' for temporary files.\n", @@ -444,6 +446,10 @@ main (argc, argv) } if (getenv (CVSREAD_ENV) != NULL) cvswrite = 0; + if (getenv (CVSREADONLYFS_ENV) != NULL) { + readonlyfs = 1; + logoff = 1; + } /* Set this to 0 to force getopt initialization. getopt() sets this to 1 internally. */ @@ -473,7 +479,7 @@ main (argc, argv) opterr = 1; while ((c = getopt_long - (argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:xa", long_options, &option_index)) + (argc, argv, "+QqrwtnRlvb:T:e:d:Hfz:s:xa", long_options, &option_index)) != EOF) { switch (c) @@ -509,6 +515,10 @@ main (argc, argv) case 't': trace = 1; break; + case 'R': + readonlyfs = 1; + logoff = 1; + break; case 'n': noexec = 1; case 'l': /* Fall through */ @@ -522,7 +532,7 @@ main (argc, argv) (void) fputs (config_string, stdout); (void) fputs ("\n", stdout); (void) fputs ("\ -Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\ +Copyright (c) 1989-1997 Brian Berliner, david d `zoo' zuhn, \n\ Jeff Polk, and other authors\n", stdout); (void) fputs ("\n", stdout); (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout); @@ -811,7 +821,7 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\ } (void) strcat (path, "/"); (void) strcat (path, CVSROOTADM_HISTORY); - if (isfile (path) && !isaccessible (path, R_OK | W_OK)) + if (readonlyfs == 0 && isfile (path) && !isaccessible (path, R_OK | W_OK)) { save_errno = errno; error (0, 0, "Sorry, you don't have read/write access to the history file"); @@ -863,6 +873,12 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\ (void) putenv (env); /* do not free env, as putenv has control of it */ } + { + char *env; + env = xmalloc (sizeof "CVS_PID=" + 32); /* XXX pid < 10^32 */ + (void) sprintf (env, "CVS_PID=%ld", (long) getpid ()); + (void) putenv (env); + } #endif #ifndef DONT_USE_SIGNALS @@ -923,6 +939,9 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\ if we didn't, then there would be no way to check in a new CVSROOT/config file to fix the broken one! */ parse_config (CVSroot_directory); + + /* Now is a convenient time to read CVSROOT/options */ + parseopts(CVSroot_directory); } } /* end of stuff that gets done if the user DOESN'T ask for help */ @@ -1000,3 +1019,61 @@ usage (cpp) (void) fprintf (stderr, *cpp); error_exit (); } + +void +parseopts(root) + const char *root; +{ + char path[PATH_MAX]; + int save_errno; + char buf[1024]; + const char *p; + char *q; + FILE *fp; + + if (root == NULL) { + printf("no CVSROOT in parseopts\n"); + return; + } + p = strchr (root, ':'); + if (p) + p++; + else + p = root; + if (p == NULL) { + printf("mangled CVSROOT in parseopts\n"); + return; + } + (void) sprintf (path, "%s/%s/%s", p, CVSROOTADM, CVSROOTADM_OPTIONS); + if ((fp = fopen(path, "r")) != NULL) { + while (fgets(buf, sizeof buf, fp) != NULL) { + if (buf[0] == '#') + continue; + q = strrchr(buf, '\n'); + if (q) + *q = '\0'; + + if (!strncmp(buf, "tag=", 4)) { + char *what; + char *rcs_localid; + + rcs_localid = buf + 4; + RCS_setlocalid(rcs_localid); + } + if (!strncmp(buf, "tagexpand=", 10)) { + char *what; + char *rcs_incexc; + + rcs_incexc = buf + 10; + RCS_setincexc(rcs_incexc); + } + /* + * OpenBSD has a "umask=" and "dlimit=" command, we silently + * ignore them here since they are not much use to us. cvsumask + * defaults to 002 already, and the dlimit (data size limit) + * should really be handled elsewhere (eg: login.conf). + */ + } + fclose(fp); + } +} diff --git a/contrib/cvs/src/mkmodules.c b/contrib/cvs/src/mkmodules.c index c3c530d..dab5b3f 100644 --- a/contrib/cvs/src/mkmodules.c +++ b/contrib/cvs/src/mkmodules.c @@ -279,10 +279,6 @@ static const char *const modules_contents[] = { static const char *const config_contents[] = { "# Set this to \"no\" if pserver shouldn't check system users/passwords\n", "#SystemAuth=no\n", - "\n", - "# Set `PreservePermissions' to `yes' to save file status information\n", - "# in the repository.\n", - "#PreservePermissions=no\n", NULL }; @@ -542,7 +538,7 @@ checkout_file (file, temp) (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { - error (0, 0, "failed to check out %s file", + error (0, retcode == -1 ? errno : 0, "failed to check out %s file", file); } freercsnode (&rcsnode); diff --git a/contrib/cvs/src/rcs.c b/contrib/cvs/src/rcs.c index 89af760..3400027 100644 --- a/contrib/cvs/src/rcs.c +++ b/contrib/cvs/src/rcs.c @@ -11,9 +11,6 @@ #include <assert.h> #include "cvs.h" #include "edit.h" -#include "hardlink.h" - -int preserve_perms = 0; /* The RCS -k options, and a set of enums that must match the array. These come first so that we can use enum kflag in function @@ -22,57 +19,11 @@ static const char *const kflags[] = {"kv", "kvl", "k", "v", "o", "b", (char *) NULL}; enum kflag { KFLAG_KV = 0, KFLAG_KVL, KFLAG_K, KFLAG_V, KFLAG_O, KFLAG_B }; -/* A structure we use to buffer the contents of an RCS file. The - various fields are only referenced directly by the rcsbuf_* - functions. We declare the struct here so that we can allocate it - on the stack, rather than in memory. */ - -struct rcsbuffer -{ - /* Points to the current position in the buffer. */ - char *ptr; - /* Points just after the last valid character in the buffer. */ - char *ptrend; - /* The file. */ - FILE *fp; - /* The name of the file, used for error messages. */ - const char *filename; - /* The starting file position of the data in the buffer. */ - unsigned long pos; - /* The length of the value. */ - size_t vlen; - /* Whether the value contains an '@' string. If so, we can not - compress whitespace characters. */ - int at_string; - /* The number of embedded '@' characters in an '@' string. If - this is non-zero, we must search the string for pairs of '@' - and convert them to a single '@'. */ - int embedded_at; -}; - static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile)); static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch)); -static void rcsbuf_open PROTO ((struct rcsbuffer *, FILE *fp, - const char *filename, unsigned long pos)); -static void rcsbuf_close PROTO ((struct rcsbuffer *)); -static int rcsbuf_getkey PROTO ((struct rcsbuffer *, char **keyp, - char **valp)); -static int rcsbuf_getrevnum PROTO ((struct rcsbuffer *, char **revp)); -static char *rcsbuf_fill PROTO ((struct rcsbuffer *, char *ptr, char **keyp, - char **valp)); -static char *rcsbuf_valcopy PROTO ((struct rcsbuffer *, char *val, int polish, - size_t *lenp)); -static void rcsbuf_valpolish PROTO ((struct rcsbuffer *, char *val, int polish, - size_t *lenp)); -static void rcsbuf_valpolish_internal PROTO ((struct rcsbuffer *, char *to, - const char *from, size_t *lenp)); -static unsigned long rcsbuf_ftell PROTO ((struct rcsbuffer *)); -static void rcsbuf_get_buffered PROTO ((struct rcsbuffer *, char **datap, - size_t *lenp)); -static void rcsbuf_cache PROTO ((RCSNode *, struct rcsbuffer *)); -static void rcsbuf_cache_close PROTO ((void)); -static void rcsbuf_cache_open PROTO ((RCSNode *, long, FILE **, - struct rcsbuffer *)); +static int getrcskey PROTO((FILE * fp, char **keyp, char **valp, + size_t *lenp)); +static void getrcsrev PROTO ((FILE *fp, char **revp)); static int checkmagic_proc PROTO((Node *p, void *closure)); static void do_branches PROTO((List * list, char *val)); static void do_symbols PROTO((List * list, char *val)); @@ -89,15 +40,12 @@ static void expand_keywords PROTO((RCSNode *, RCSVers *, const char *, static void cmp_file_buffer PROTO((void *, const char *, size_t)); enum rcs_delta_op {RCS_ANNOTATE, RCS_FETCH}; -static void RCS_deltas PROTO ((RCSNode *, FILE *, struct rcsbuffer *, char *, - enum rcs_delta_op, char **, size_t *, - char **, size_t *)); +static void RCS_deltas PROTO ((RCSNode *, FILE *, char *, enum rcs_delta_op, + char **, size_t *, char **, size_t *)); /* Routines for reading, parsing and writing RCS files. */ -static RCSVers *getdelta PROTO ((struct rcsbuffer *, char *, char **, - char **)); -static Deltatext *RCS_getdeltatext PROTO ((RCSNode *, FILE *, - struct rcsbuffer *)); +static RCSVers *getdelta PROTO ((FILE *, char *)); +static Deltatext *RCS_getdeltatext PROTO ((RCSNode *, FILE *)); static void freedeltatext PROTO ((Deltatext *)); static void RCS_putadmin PROTO ((RCSNode *, FILE *)); @@ -106,20 +54,14 @@ static void RCS_putdesc PROTO ((RCSNode *, FILE *)); static void putdelta PROTO ((RCSVers *, FILE *)); static int putrcsfield_proc PROTO ((Node *, void *)); static int putsymbol_proc PROTO ((Node *, void *)); -static void RCS_copydeltas PROTO ((RCSNode *, FILE *, struct rcsbuffer *, - FILE *, Deltatext *, char *)); -static int count_delta_actions PROTO ((Node *, void *)); +static void RCS_copydeltas PROTO ((RCSNode *, FILE *, FILE *, Deltatext *, char *)); static void putdeltatext PROTO ((FILE *, Deltatext *)); static FILE *rcs_internal_lockfile PROTO ((char *)); static void rcs_internal_unlockfile PROTO ((FILE *, char *)); static char *rcs_lockfilename PROTO ((char *)); -/* The RCS file reading functions are called a lot, and they do some - string comparisons. This macro speeds things up a bit by skipping - the function call when the first characters are different. It - evaluates its arguments multiple times. */ -#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0) +static char * getfullCVSname PROTO ((char *, char **)); /* * We don't want to use isspace() from the C library because: @@ -150,6 +92,7 @@ static const char spacetab[] = { #define whitespace(c) (spacetab[(unsigned char)c] != 0) + /* Parse an rcsfile given a user file name and a repository. If there is an error, we print an error message and return NULL. If the file does not exist, we return NULL without printing anything (I'm not @@ -165,10 +108,6 @@ RCS_parse (file, repos) RCSNode *retval; char *rcsfile; - /* We're creating a new RCSNode, so there is no hope of finding it - in the cache. */ - rcsbuf_cache_close (); - rcsfile = xmalloc (strlen (repos) + strlen (file) + sizeof (RCSEXT) + sizeof (CVSATTIC) + 10); (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT); @@ -178,6 +117,7 @@ RCS_parse (file, repos) if (rcs != NULL) rcs->flags |= VALID; + fclose (fp); retval = rcs; goto out; } @@ -198,6 +138,7 @@ RCS_parse (file, repos) rcs->flags |= VALID; } + fclose (fp); retval = rcs; goto out; } @@ -226,6 +167,7 @@ RCS_parse (file, repos) if (rcs != NULL) rcs->flags |= VALID; + fclose (fp); free (rcs->path); rcs->path = found_path; retval = rcs; @@ -249,6 +191,7 @@ RCS_parse (file, repos) rcs->flags |= VALID; } + fclose (fp); free (rcs->path); rcs->path = found_path; retval = rcs; @@ -280,10 +223,6 @@ RCS_parsercsfile (rcsfile) FILE *fp; RCSNode *rcs; - /* We're creating a new RCSNode, so there is no hope of finding it - in the cache. */ - rcsbuf_cache_close (); - /* open the rcsfile */ if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) == NULL) { @@ -293,6 +232,7 @@ RCS_parsercsfile (rcsfile) rcs = RCS_parsercsfile_i (fp, rcsfile); + fclose (fp); return (rcs); } @@ -305,7 +245,6 @@ RCS_parsercsfile_i (fp, rcsfile) const char *rcsfile; { RCSNode *rdata; - struct rcsbuffer rcsbuf; char *key, *value; /* make a node */ @@ -314,32 +253,40 @@ RCS_parsercsfile_i (fp, rcsfile) rdata->refcount = 1; rdata->path = xstrdup (rcsfile); - /* Process HEAD, BRANCH, and EXPAND keywords from the RCS header. + /* Process HEAD and BRANCH keywords from the RCS header. Most cvs operations on the main branch don't need any more information. Those that do call RCS_reparsercsfile to parse - the rest of the header and the deltas. */ - - rcsbuf_open (&rcsbuf, fp, rcsfile, 0); - - if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + the rest of the header and the deltas. + + People often wonder whether this is inefficient, to open the + file once here and once in RCS_reparsercsfile. Well, it might + help a little bit if we kept the file open (I haven't tried + timing this myself), but basically the common case, which we + want to optimize, is the one in which we call + RCS_parsercsfile_i and not RCS_reparsercsfile (for example, + "cvs update" on a lot of files most of which are unmodified). + So making the case in which we call RCS_reparsercsfile fast is + not as important. */ + + if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) goto l_error; - if (STREQ (key, RCSDESC)) + if (strcmp (key, RCSDESC) == 0) goto l_error; - if (STREQ (RCSHEAD, key) && value != NULL) - rdata->head = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL); + if (strcmp (RCSHEAD, key) == 0 && value != NULL) + rdata->head = xstrdup (value); - if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) goto l_error; - if (STREQ (key, RCSDESC)) + if (strcmp (key, RCSDESC) == 0) goto l_error; - if (STREQ (RCSBRANCH, key) && value != NULL) + if (strcmp (RCSBRANCH, key) == 0 && value != NULL) { char *cp; - rdata->branch = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL); + rdata->branch = xstrdup (value); if ((numdots (rdata->branch) & 1) != 0) { /* turn it into a branch if it's a revision */ @@ -348,43 +295,23 @@ RCS_parsercsfile_i (fp, rcsfile) } } - /* Look ahead for expand, stopping when we see desc or a revision - number. */ - while (1) - { - char *cp; - - if (STREQ (RCSEXPAND, key)) - { - rdata->expand = rcsbuf_valcopy (&rcsbuf, value, 0, - (size_t *) NULL); - break; - } - - for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) - /* do nothing */ ; - if (*cp == '\0') - break; - - if (STREQ (RCSDESC, key)) - break; - - if (! rcsbuf_getkey (&rcsbuf, &key, &value)) - break; - } - rdata->flags |= PARTIAL; - - rcsbuf_cache (rdata, &rcsbuf); - return rdata; l_error: - error (0, 0, "`%s' does not appear to be a valid rcs file", - rcsfile); - rcsbuf_close (&rcsbuf); + if (!really_quiet) + { + if (ferror(fp)) + { + error (1, 0, "error reading `%s'", rcsfile); + } + else + { + error (0, 0, "`%s' does not appear to be a valid rcs file", + rcsfile); + } + } freercsnode (&rdata); - fclose (fp); return (NULL); } @@ -396,24 +323,25 @@ l_error: If PFP is NULL, close the file when done. Otherwise, leave it open and store the FILE * in *PFP. */ void -RCS_reparsercsfile (rdata, pfp, rcsbufp) +RCS_reparsercsfile (rdata, pfp) RCSNode *rdata; FILE **pfp; - struct rcsbuffer *rcsbufp; { FILE *fp; char *rcsfile; - struct rcsbuffer rcsbuf; + Node *q, *kv; RCSVers *vnode; - int gotkey; + long fpos; char *cp; char *key, *value; assert (rdata != NULL); rcsfile = rdata->path; - rcsbuf_cache_open (rdata, 0, &fp, &rcsbuf); + fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ); + if (fp == NULL) + error (1, 0, "unable to reopen `%s'", rcsfile); /* make a node */ /* This probably shouldn't be done until later: if a file has an @@ -425,68 +353,66 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp) * process all the special header information, break out when we get to * the first revision delta */ - gotkey = 0; for (;;) { + fpos = ftell (fp); + /* get the next key/value pair */ - if (!gotkey) + + /* if key is NULL here, then the file is missing some headers + or we had trouble reading the file. */ + if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) { - if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + if (ferror(fp)) + { + error (1, 0, "error reading `%s'", rcsfile); + } + else { error (1, 0, "`%s' does not appear to be a valid rcs file", rcsfile); } } - gotkey = 0; - - /* Skip head, branch and expand tags; we already have them. */ - if (STREQ (key, RCSHEAD) - || STREQ (key, RCSBRANCH) - || STREQ (key, RCSEXPAND)) - { + /* Skip head and branch tags; we already have them. */ + if (strcmp (key, RCSHEAD) == 0 || strcmp (key, RCSBRANCH) == 0) continue; - } - if (STREQ (key, "access")) + if (strcmp (key, "access") == 0) { if (value != NULL) - { - /* We pass the POLISH parameter as 1 because - RCS_addaccess expects nothing but spaces. FIXME: - It would be easy and more efficient to change - RCS_addaccess. */ - rdata->access = rcsbuf_valcopy (&rcsbuf, value, 1, - (size_t *) NULL); - } + rdata->access = xstrdup (value); continue; } /* We always save lock information, so that we can handle -kkvl correctly when checking out a file. */ - if (STREQ (key, "locks")) + if (strcmp (key, "locks") == 0) { if (value != NULL) - rdata->locks_data = rcsbuf_valcopy (&rcsbuf, value, 0, - (size_t *) NULL); - if (! rcsbuf_getkey (&rcsbuf, &key, &value)) - { - error (1, 0, "premature end of file reading %s", rcsfile); - } - if (STREQ (key, "strict") && value == NULL) + rdata->locks_data = xstrdup (value); + fpos = ftell (fp); + if (getrcskey (fp, &key, &value, NULL) >= 0 && + strcmp (key, "strict") == 0 && + value == NULL) { rdata->strict_locks = 1; } else - gotkey = 1; + (void) fseek (fp, fpos, SEEK_SET); continue; } - if (STREQ (RCSSYMBOLS, key)) + if (strcmp (RCSSYMBOLS, key) == 0) { if (value != NULL) - rdata->symbols_data = rcsbuf_valcopy (&rcsbuf, value, 0, - (size_t *) NULL); + rdata->symbols_data = xstrdup(value); + continue; + } + + if (strcmp (RCSEXPAND, key) == 0) + { + rdata->expand = xstrdup (value); continue; } @@ -497,19 +423,15 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp) */ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) /* do nothing */ ; - /* Note that when comparing with RCSDATE, we are not massaging - VALUE from the string found in the RCS file. This is OK - since we know exactly what to expect. */ - if (*cp == '\0' && strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) == 0) + if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) break; - if (STREQ (key, RCSDESC)) + if (strcmp (key, RCSDESC) == 0) break; - if (STREQ (key, "comment")) + if (strcmp (key, "comment") == 0) { - rdata->comment = rcsbuf_valcopy (&rcsbuf, value, 0, - (size_t *) NULL); + rdata->comment = xstrdup (value); continue; } if (rdata->other == NULL) @@ -517,7 +439,7 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp) kv = getnode (); kv->type = RCSFIELD; kv->key = xstrdup (key); - kv->data = rcsbuf_valcopy (&rcsbuf, value, 1, (size_t *) NULL); + kv->data = xstrdup (value); if (addnode (rdata->other, kv) != 0) { error (0, 0, "warning: duplicate key `%s' in RCS file `%s'", @@ -528,11 +450,15 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp) /* if we haven't grabbed it yet, we didn't want it */ } - /* We got out of the loop, so we have the first part of the first - revision delta in KEY (the revision) and VALUE (the date key - and its value). This is what getdelta expects to receive. */ + /* + * we got out of the loop, so we have the first part of the first + * revision delta in our hand key=the revision and value=the date key and + * its value + */ + /* First, seek back to the start of the delta block. */ + (void) fseek (fp, fpos, SEEK_SET); - while ((vnode = getdelta (&rcsbuf, rcsfile, &key, &value)) != NULL) + while ((vnode = getdelta (fp, rcsfile)) != NULL) { /* get the node */ q = getnode (); @@ -552,9 +478,8 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp) } } - /* Here KEY and VALUE are whatever caused getdelta to return NULL. */ - - if (STREQ (key, RCSDESC)) + (void) getrcskey (fp, &key, &value, NULL); + if (key != NULL && strcmp (key, RCSDESC) == 0) { if (rdata->desc != NULL) { @@ -563,17 +488,19 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp) key, rcsfile); free (rdata->desc); } - rdata->desc = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL); + rdata->desc = xstrdup (value); } - rdata->delta_pos = rcsbuf_ftell (&rcsbuf); + rdata->delta_pos = ftell (fp); if (pfp == NULL) - rcsbuf_cache (rdata, &rcsbuf); + { + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", rcsfile); + } else { *pfp = fp; - *rcsbufp = rcsbuf; } rdata->flags &= ~PARTIAL; } @@ -595,21 +522,31 @@ RCS_fully_parse (rcs) RCSNode *rcs; { FILE *fp; - struct rcsbuffer rcsbuf; - RCS_reparsercsfile (rcs, &fp, &rcsbuf); + RCS_reparsercsfile (rcs, &fp); while (1) { + int c; char *key, *value; + size_t vallen; Node *vers; RCSVers *vnode; /* Rather than try to keep track of how much information we have read, just read to the end of the file. */ - if (! rcsbuf_getrevnum (&rcsbuf, &key)) + do + { + c = getc (fp); + if (c == EOF) + break; + } while (whitespace (c)); + if (c == EOF) break; + if (ungetc (c, fp) == EOF) + error (1, errno, "ungetc failed"); + getrcsrev (fp, &key); vers = findnode (rcs->versions, key); if (vers == NULL) error (1, 0, @@ -618,9 +555,9 @@ RCS_fully_parse (rcs) vnode = (RCSVers *) vers->data; - while (rcsbuf_getkey (&rcsbuf, &key, &value)) + while (getrcskey (fp, &key, &value, &vallen) >= 0) { - if (! STREQ (key, "text")) + if (strcmp (key, "text") != 0) { Node *kv; @@ -629,7 +566,7 @@ RCS_fully_parse (rcs) kv = getnode (); kv->type = RCSFIELD; kv->key = xstrdup (key); - kv->data = rcsbuf_valcopy (&rcsbuf, value, 1, (size_t *) NULL); + kv->data = xstrdup (value); if (addnode (vnode->other, kv) != 0) { error (0, 0, @@ -642,7 +579,7 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'", continue; } - if (! STREQ (vnode->version, rcs->head)) + if (strcmp (vnode->version, rcs->head) != 0) { unsigned long add, del; char buf[50]; @@ -654,10 +591,8 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'", del = 0; if (value != NULL) { - size_t vallen; const char *cp; - rcsbuf_valpolish (&rcsbuf, value, 0, &vallen); cp = value; while (cp < value + vallen) { @@ -737,7 +672,8 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'", } } - rcsbuf_cache (rcs, &rcsbuf); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", rcs->path); } /* @@ -832,877 +768,353 @@ rcsvers_delproc (p) { free_rcsvers_contents ((RCSVers *) p->data); } - -/* These functions retrieve keys and values from an RCS file using a - buffer. We use this somewhat complex approach because it turns out - that for many common operations, CVS spends most of its time - reading keys, so it's worth doing some fairly hairy optimization. */ - -/* The number of bytes we try to read each time we need more data. */ - -#define RCSBUF_BUFSIZE (8192) - -/* The buffer we use to store data. This grows as needed. */ - -static char *rcsbuf_buffer = NULL; -static size_t rcsbuf_buffer_size = 0; - -/* Whether rcsbuf_buffer is in use. This is used as a sanity check. */ - -static int rcsbuf_inuse; - -/* Set up to start gathering keys and values from an RCS file. This - initializes RCSBUF. */ - -static void -rcsbuf_open (rcsbuf, fp, filename, pos) - struct rcsbuffer *rcsbuf; - FILE *fp; - const char *filename; - unsigned long pos; -{ - if (rcsbuf_inuse) - error (1, 0, "rcsbuf_open: internal error"); - rcsbuf_inuse = 1; - - if (rcsbuf_buffer_size < RCSBUF_BUFSIZE) - expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, RCSBUF_BUFSIZE); - - rcsbuf->ptr = rcsbuf_buffer; - rcsbuf->ptrend = rcsbuf_buffer; - rcsbuf->fp = fp; - rcsbuf->filename = filename; - rcsbuf->pos = pos; - rcsbuf->vlen = 0; - rcsbuf->at_string = 0; - rcsbuf->embedded_at = 0; -} - -/* Stop gathering keys from an RCS file. */ - -static void -rcsbuf_close (rcsbuf) - struct rcsbuffer *rcsbuf; -{ - if (! rcsbuf_inuse) - error (1, 0, "rcsbuf_close: internal error"); - rcsbuf_inuse = 0; -} -/* Read a key/value pair from an RCS file. This sets *KEYP to point - to the key, and *VALUEP to point to the value. A missing or empty - value is indicated by setting *VALUEP to NULL. - - This function returns 1 on success, or 0 on EOF. If there is an - error reading the file, or an EOF in an unexpected location, it - gives a fatal error. +/* + * getrcskey - fill in the key and value from the rcs file the algorithm is + * as follows + * + * o skip whitespace + * o fill in key with everything up to next white + * space or semicolon + * o if key == "desc" then key and data are NULL and return -1 + * o if key wasn't terminated by a semicolon, skip white space and fill + * in value with everything up to a semicolon + * o compress all whitespace down to a single space + * o if a word starts with @, do funky rcs processing + * o strip whitespace off end of value or set value to NULL if it empty + * o return 0 since we found something besides "desc" + * + * Sets *KEYP and *VALUEP to point to storage managed by the getrcskey + * function; the contents are only valid until the next call to + * getrcskey or getrcsrev. If LENP is not NULL, this sets *LENP to + * the length of *VALUEP; this is needed if the string might contain + * binary data. + */ - This sets *KEYP and *VALUEP to point to storage managed by - rcsbuf_getkey. Moreover, *VALUEP has not been massaged from the - RCS format: it may contain embedded whitespace and embedded '@' - characters. Call rcsbuf_valcopy or rcsbuf_valpolish to do - appropriate massaging. */ +static char *key = NULL; +static char *value = NULL; +static size_t keysize = 0; +static size_t valsize = 0; static int -rcsbuf_getkey (rcsbuf, keyp, valp) - struct rcsbuffer *rcsbuf; +getrcskey (fp, keyp, valp, lenp) + FILE *fp; char **keyp; char **valp; + size_t *lenp; { - register const char * const my_spacetab = spacetab; - register char *ptr, *ptrend; - char c; - -#define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0) - - rcsbuf->vlen = 0; - rcsbuf->at_string = 0; - rcsbuf->embedded_at = 0; - - ptr = rcsbuf->ptr; - ptrend = rcsbuf->ptrend; + char *cur, *max; + int c; + int just_string; - /* Sanity check. */ - if (ptr < rcsbuf_buffer || ptr > rcsbuf_buffer + rcsbuf_buffer_size) - abort (); + if (lenp != NULL) + *lenp = 0; - /* If the pointer is more than RCSBUF_BUFSIZE bytes into the - buffer, move back to the start of the buffer. This keeps the - buffer from growing indefinitely. */ - if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE) + /* skip leading whitespace */ + do { - int len; - - len = ptrend - ptr; - - /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes - at a time, so we can't have more bytes than that past PTR. */ - if (len > RCSBUF_BUFSIZE) - abort (); - - /* Update the POS field, which holds the file offset of the - first byte in the RCSBUF_BUFFER buffer. */ - rcsbuf->pos += ptr - rcsbuf_buffer; - - memcpy (rcsbuf_buffer, ptr, len); - ptr = rcsbuf_buffer; - ptrend = ptr + len; - rcsbuf->ptrend = ptrend; - } - - /* Skip leading whitespace. */ + c = getc (fp); + if (c == EOF) + { + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); + } + } while (whitespace (c)); - while (1) + /* fill in key */ + cur = key; + max = key + keysize; + while (!whitespace (c) && c != ';') { - if (ptr >= ptrend) + if (cur >= max) { - ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); - if (ptr == NULL) - return 0; - ptrend = rcsbuf->ptrend; + size_t curoff = cur - key; + expand_string (&key, &keysize, keysize + 1); + cur = key + curoff; + max = key + keysize; } + *cur++ = c; - c = *ptr; - if (! my_whitespace (c)) - break; - - ++ptr; - } - - /* We've found the start of the key. */ - - *keyp = ptr; - - if (c != ';') - { - while (1) + c = getc (fp); + if (c == EOF) { - ++ptr; - if (ptr >= ptrend) - { - ptr = rcsbuf_fill (rcsbuf, ptr, keyp, (char **) NULL); - if (ptr == NULL) - error (1, 0, "EOF in key in RCS file %s", - rcsbuf->filename); - ptrend = rcsbuf->ptrend; - } - c = *ptr; - if (c == ';' || my_whitespace (c)) - break; + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); } } - - /* Here *KEYP points to the key in the buffer, C is the character - we found at the of the key, and PTR points to the location in - the buffer where we found C. We must set *PTR to \0 in order - to terminate the key. If the key ended with ';', then there is - no value. */ - - *ptr = '\0'; - ++ptr; - - if (c == ';') + if (cur >= max) { - *valp = NULL; - rcsbuf->ptr = ptr; - return 1; + size_t curoff = cur - key; + expand_string (&key, &keysize, keysize + 1); + cur = key + curoff; + max = key + keysize; } + *cur = '\0'; - /* C must be whitespace. Skip whitespace between the key and the - value. If we find ';' now, there is no value. */ - - while (1) + /* skip whitespace between key and val */ + while (whitespace (c)) { - if (ptr >= ptrend) - { - ptr = rcsbuf_fill (rcsbuf, ptr, keyp, (char **) NULL); - if (ptr == NULL) - error (1, 0, "EOF while looking for value in RCS file %s", - rcsbuf->filename); - ptrend = rcsbuf->ptrend; - } - c = *ptr; - if (c == ';') + c = getc (fp); + if (c == EOF) { - *valp = NULL; - rcsbuf->ptr = ptr + 1; - return 1; + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); } - if (! my_whitespace (c)) - break; - ++ptr; - } + } - /* Now PTR points to the start of the value, and C is the first - character of the value. */ - - if (c != '@') - *valp = ptr; - else + /* if we ended key with a semicolon, there is no value */ + if (c == ';') { - char *pat; - size_t vlen; - - /* Optimize the common case of a value composed of a single - '@' string. */ - - rcsbuf->at_string = 1; + *keyp = key; + *valp = (char *) NULL; + return (0); + } - ++ptr; + /* otherwise, there might be a value, so fill it in */ + cur = value; + max = value + valsize; - *valp = ptr; + just_string = (strcmp (key, RCSDESC) == 0 + || strcmp (key, "text") == 0 + || strcmp (key, "log") == 0); - while (1) + /* process the value */ + for (;;) + { + /* handle RCS "strings" */ + if (c == '@') { - while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL) - { - /* Note that we pass PTREND as the PTR value to - rcsbuf_fill, so that we will wind up setting PTR to - the location corresponding to the old PTREND, so - that we don't search the same bytes again. */ - ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); - if (ptr == NULL) - error (1, 0, - "EOF while looking for end of string in RCS file %s", - rcsbuf->filename); - ptrend = rcsbuf->ptrend; - } - - /* Handle the special case of an '@' right at the end of - the known bytes. */ - if (pat + 1 >= ptrend) + for (;;) { - /* Note that we pass PAT, not PTR, here. */ - pat = rcsbuf_fill (rcsbuf, pat, keyp, valp); - if (pat == NULL) + c = getc (fp); + if (c == EOF) { - /* EOF here is OK; it just means that the last - character of the file was an '@' terminating a - value for a key type which does not require a - trailing ';'. */ - pat = rcsbuf->ptrend - 1; - + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); } - ptrend = rcsbuf->ptrend; - /* Note that the value of PTR is bogus here. This is - OK, because we don't use it. */ - } - - if (pat + 1 >= ptrend || pat[1] != '@') - break; - - /* We found an '@' pair in the string. Keep looking. */ - ++rcsbuf->embedded_at; - ptr = pat + 2; - } - - /* Here PAT points to the final '@' in the string. */ - - *pat = '\0'; - - vlen = pat - *valp; - if (vlen == 0) - *valp = NULL; - rcsbuf->vlen = vlen; - - ptr = pat + 1; - } - - /* Certain keywords only have a '@' string. If there is no '@' - string, then the old getrcskey function assumed that they had - no value, and we do the same. */ - - { - char *k; - - k = *keyp; - if (STREQ (k, RCSDESC) - || STREQ (k, "text") - || STREQ (k, "log")) - { - if (c != '@') - *valp = NULL; - rcsbuf->ptr = ptr; - return 1; - } - } - - /* If we've already gathered a '@' string, try to skip whitespace - and find a ';'. */ - if (c == '@') - { - while (1) - { - char n; + if (c == '@') + { + c = getc (fp); + if (c == EOF) + { + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); + } + + if (c != '@') + break; + } - if (ptr >= ptrend) - { - ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp); - if (ptr == NULL) - error (1, 0, "EOF in value in RCS file %s", - rcsbuf->filename); - ptrend = rcsbuf->ptrend; - } - n = *ptr; - if (n == ';') - { - /* We're done. We already set everything up for this - case above. */ - rcsbuf->ptr = ptr + 1; - return 1; + if (cur >= max) + { + size_t curoff = cur - value; + expand_string (&value, &valsize, valsize + 1); + cur = value + curoff; + max = value + valsize; + } + *cur++ = c; } - if (! my_whitespace (n)) - break; - ++ptr; - } - - /* The value extends past the '@' string. We need to undo the - closing of the '@' done in the default case above. This - case never happens in a plain RCS file, but it can happen - if user defined phrases are used. */ - if (rcsbuf->vlen != 0) - (*valp)[rcsbuf->vlen] = ' '; - else - *valp = ptr; - } - - /* Here we have a value which is not a simple '@' string. We need - to gather up everything until the next ';', including any '@' - strings. *VALP points to the start of the value. If - RCSBUF->VLEN is not zero, then we have already read an '@' - string, and PTR points to the data following the '@' string. - Otherwise, PTR points to the start of the value. */ - - while (1) - { - char *start, *psemi, *pat; - - /* Find the ';' which must end the value. */ - start = ptr; - while ((psemi = memchr (ptr, ';', ptrend - ptr)) == NULL) - { - int slen; - - /* Note that we pass PTREND as the PTR value to - rcsbuf_fill, so that we will wind up setting PTR to the - location corresponding to the old PTREND, so that we - don't search the same bytes again. */ - slen = start - *valp; - ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); - if (ptr == NULL) - error (1, 0, "EOF in value in RCS file %s", rcsbuf->filename); - start = *valp + slen; - ptrend = rcsbuf->ptrend; } - /* See if there are any '@' strings in the value. */ - pat = memchr (start, '@', psemi - start); - - if (pat == NULL) - { - size_t vlen; - - /* We're done with the value. Trim any trailing - whitespace. */ - - rcsbuf->ptr = psemi + 1; - - start = *valp; - while (psemi > start && my_whitespace (psemi[-1])) - --psemi; - *psemi = '\0'; - - vlen = psemi - start; - if (vlen == 0) - *valp = NULL; - rcsbuf->vlen = vlen; - - return 1; - } - - /* We found an '@' string in the value. We set - RCSBUF->AT_STRING, which means that we won't be able to - compress whitespace correctly for this type of value. - Since this type of value never arises in a normal RCS file, - this should not be a big deal. It means that if anybody - adds a phrase which can have both an '@' string and regular - text, they will have to handle whitespace compression - themselves. */ - - rcsbuf->at_string = 1; - - *pat = ' '; - - ptr = pat + 1; + /* The syntax for some key-value pairs is different; they + don't end with a semicolon. */ + if (just_string) + break; - while (1) + /* compress whitespace down to a single space */ + if (whitespace (c)) { - while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL) - { - /* Note that we pass PTREND as the PTR value to - rcsbuff_fill, so that we will wind up setting PTR - to the location corresponding to the old PTREND, so - that we don't search the same bytes again. */ - ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); - if (ptr == NULL) - error (1, 0, - "EOF while looking for end of string in RCS file %s", - rcsbuf->filename); - ptrend = rcsbuf->ptrend; - } + do { + c = getc (fp); + if (c == EOF) + { + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); + } + } while (whitespace (c)); - /* Handle the special case of an '@' right at the end of - the known bytes. */ - if (pat + 1 >= ptrend) + /* Do not include any trailing whitespace in the value. */ + if (c != ';') { - ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp); - if (ptr == NULL) - error (1, 0, "EOF in value in RCS file %s", - rcsbuf->filename); - ptrend = rcsbuf->ptrend; + if (cur >= max) + { + size_t curoff = cur - value; + expand_string (&value, &valsize, valsize + 1); + cur = value + curoff; + max = value + valsize; + } + *cur++ = ' '; } - - if (pat[1] != '@') - break; - - /* We found an '@' pair in the string. Keep looking. */ - ++rcsbuf->embedded_at; - ptr = pat + 2; } - /* Here PAT points to the final '@' in the string. */ - - *pat = ' '; - - ptr = pat + 1; - } - -#undef my_whitespace -} - -/* Read an RCS revision number from an RCS file. This sets *REVP to - point to the revision number; it will point to space that is - managed by the rcsbuf functions, and is only good until the next - call to rcsbuf_getkey or rcsbuf_getrevnum. - - This function returns 1 on success, or 0 on EOF. If there is an - error reading the file, or an EOF in an unexpected location, it - gives a fatal error. */ - -static int -rcsbuf_getrevnum (rcsbuf, revp) - struct rcsbuffer *rcsbuf; - char **revp; -{ - char *ptr, *ptrend; - char c; - - ptr = rcsbuf->ptr; - ptrend = rcsbuf->ptrend; - - *revp = NULL; - - /* Skip leading whitespace. */ + /* if we got a semi-colon we are done with the entire value */ + if (c == ';') + break; - while (1) - { - if (ptr >= ptrend) + if (cur >= max) { - ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); - if (ptr == NULL) - return 0; - ptrend = rcsbuf->ptrend; + size_t curoff = cur - value; + expand_string (&value, &valsize, valsize + 1); + cur = value + curoff; + max = value + valsize; } + *cur++ = c; - c = *ptr; - if (! whitespace (c)) - break; - - ++ptr; - } - - if (! isdigit (c) && c != '.') - error (1, 0, - "unexpected `%c' reading revision number in RCS file %s", - c, rcsbuf->filename); - - *revp = ptr; - - do - { - ++ptr; - if (ptr >= ptrend) + c = getc (fp); + if (c == EOF) { - ptr = rcsbuf_fill (rcsbuf, ptr, revp, (char **) NULL); - if (ptr == NULL) - error (1, 0, - "unexpected EOF reading revision number in RCS file %s", - rcsbuf->filename); - ptrend = rcsbuf->ptrend; + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); } - - c = *ptr; } - while (isdigit (c) || c == '.'); - - if (! whitespace (c)) - error (1, 0, "unexpected `%c' reading revision number in RCS file %s", - c, rcsbuf->filename); - - *ptr = '\0'; - - rcsbuf->ptr = ptr + 1; - - return 1; -} - -/* Fill RCSBUF_BUFFER with bytes from the file associated with RCSBUF, - updating PTR and the PTREND field. If KEYP and *KEYP are not NULL, - then *KEYP points into the buffer, and must be adjusted if the - buffer is changed. Likewise for VALP. Returns the new value of - PTR, or NULL on error. */ -static char * -rcsbuf_fill (rcsbuf, ptr, keyp, valp) - struct rcsbuffer *rcsbuf; - char *ptr; - char **keyp; - char **valp; -{ - int got; - - if (rcsbuf->ptrend - rcsbuf_buffer + RCSBUF_BUFSIZE > rcsbuf_buffer_size) + /* terminate the string */ + if (cur >= max) { - int poff, peoff, koff, voff; - - poff = ptr - rcsbuf_buffer; - peoff = rcsbuf->ptrend - rcsbuf_buffer; - if (keyp != NULL && *keyp != NULL) - koff = *keyp - rcsbuf_buffer; - if (valp != NULL && *valp != NULL) - voff = *valp - rcsbuf_buffer; - - expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, - rcsbuf_buffer_size + RCSBUF_BUFSIZE); - - ptr = rcsbuf_buffer + poff; - rcsbuf->ptrend = rcsbuf_buffer + peoff; - if (keyp != NULL && *keyp != NULL) - *keyp = rcsbuf_buffer + koff; - if (valp != NULL && *valp != NULL) - *valp = rcsbuf_buffer + voff; + size_t curoff = cur - value; + expand_string (&value, &valsize, valsize + 1); + cur = value + curoff; + max = value + valsize; } + *cur = '\0'; - got = fread (rcsbuf->ptrend, 1, RCSBUF_BUFSIZE, rcsbuf->fp); - if (got == 0) + /* if the string is empty, make it null */ + if (value && cur != value) { - if (ferror (rcsbuf->fp)) - error (1, errno, "cannot read %s", rcsbuf->filename); - return NULL; - } - - rcsbuf->ptrend += got; - - return ptr; -} - -/* Copy the value VAL returned by rcsbuf_getkey into a memory buffer, - returning the memory buffer. Polish the value like - rcsbuf_valpolish, q.v. */ - -static char * -rcsbuf_valcopy (rcsbuf, val, polish, lenp) - struct rcsbuffer *rcsbuf; - char *val; - int polish; - size_t *lenp; -{ - size_t vlen; - int embedded_at; - char *ret; - - if (val == NULL) - { - if (lenp != NULL) - *lenp = 0; - return NULL; - } - - vlen = rcsbuf->vlen; - embedded_at = rcsbuf->embedded_at; - - ret = xmalloc (vlen - embedded_at + 1); - - if (rcsbuf->at_string ? embedded_at == 0 : ! polish) - { - /* No special action to take. */ - memcpy (ret, val, vlen + 1); - if (lenp != NULL) - *lenp = vlen; - return ret; - } - - rcsbuf_valpolish_internal (rcsbuf, ret, val, lenp); - return ret; -} - -/* Polish the value VAL returned by rcsbuf_getkey. The POLISH - parameter is non-zero if multiple embedded whitespace characters - should be compressed into a single whitespace character. Note that - leading and trailing whitespace was already removed by - rcsbuf_getkey. Within an '@' string, pairs of '@' characters are - compressed into a single '@' character regardless of the value of - POLISH. If LENP is not NULL, set *LENP to the length of the value. */ - -static void -rcsbuf_valpolish (rcsbuf, val, polish, lenp) - struct rcsbuffer *rcsbuf; - char *val; - int polish; - size_t *lenp; -{ - if (val == NULL) - { - if (lenp != NULL) - *lenp= 0; - return; - } - - if (rcsbuf->at_string ? rcsbuf->embedded_at == 0 : ! polish) - { - /* No special action to take. */ + *valp = value; if (lenp != NULL) - *lenp = rcsbuf->vlen; - return; + *lenp = cur - value; } - - rcsbuf_valpolish_internal (rcsbuf, val, val, lenp); + else + *valp = NULL; + *keyp = key; + return (0); } -/* Internal polishing routine, called from rcsbuf_valcopy and - rcsbuf_valpolish. */ - +/* Read an RCS revision number from FP. Put a pointer to it in *REVP; + it points to space managed by getrcsrev which is only good until + the next call to getrcskey or getrcsrev. */ static void -rcsbuf_valpolish_internal (rcsbuf, to, from, lenp) - struct rcsbuffer *rcsbuf; - char *to; - const char *from; - size_t *lenp; +getrcsrev (fp, revp) + FILE *fp; + char **revp; { - size_t len; - - len = rcsbuf->vlen; - - if (! rcsbuf->at_string) - { - char *orig_to; - size_t clen; - - orig_to = to; + char *cur; + char *max; + int c; - for (clen = len; clen > 0; ++from, --clen) + do { + c = getc (fp); + if (c == EOF) { - char c; - - c = *from; - if (whitespace (c)) - { - /* Note that we know that clen can not drop to zero - while we have whitespace, because we know there is - no trailing whitespace. */ - while (whitespace (from[1])) - { - ++from; - --clen; - } - c = ' '; - } - *to++ = c; + /* FIXME: should be including filename in error message. */ + if (ferror (fp)) + error (1, errno, "cannot read rcs file"); + else + error (1, 0, "unexpected end of file reading rcs file"); } + } while (whitespace (c)); - *to = '\0'; + if (!(isdigit (c) || c == '.')) + /* FIXME: should be including filename in error message. */ + error (1, 0, "error reading rcs file; revision number expected"); - if (lenp != NULL) - *lenp = to - orig_to; - } - else + cur = key; + max = key + keysize; + while (isdigit (c) || c == '.') { - const char *orig_from; - char *orig_to; - int embedded_at; - size_t clen; - - orig_from = from; - orig_to = to; - - embedded_at = rcsbuf->embedded_at; - - if (lenp != NULL) - *lenp = len - embedded_at; - - for (clen = len; clen > 0; ++from, --clen) + if (cur >= max) { - char c; - - c = *from; - *to++ = c; - if (c == '@') - { - ++from; - - /* Sanity check. */ - if (*from != '@' || clen == 0) - abort (); - - --clen; - - --embedded_at; - if (embedded_at == 0) - { - /* We've found all the embedded '@' characters. - We can just memcpy the rest of the buffer after - this '@' character. */ - if (orig_to != orig_from) - memcpy (to, from + 1, clen - 1); - else - memmove (to, from + 1, clen - 1); - from += clen; - to += clen - 1; - break; - } - } + size_t curoff = cur - key; + expand_string (&key, &keysize, keysize + 1); + cur = key + curoff; + max = key + keysize; } + *cur++ = c; - /* Sanity check. */ - if (from != orig_from + len - || to != orig_to + (len - rcsbuf->embedded_at)) + c = getc (fp); + if (c == EOF) { - abort (); + /* FIXME: should be including filename in error message. */ + if (ferror (fp)) + error (1, errno, "cannot read rcs file"); + else + error (1, 0, "unexpected end of file reading rcs file"); } - - *to = '\0'; } -} - -/* Return the current position of an rcsbuf. */ - -static unsigned long -rcsbuf_ftell (rcsbuf) - struct rcsbuffer *rcsbuf; -{ - return rcsbuf->pos + (rcsbuf->ptr - rcsbuf_buffer); -} -/* Return a pointer to any data buffered for RCSBUF, along with the - length. */ - -static void -rcsbuf_get_buffered (rcsbuf, datap, lenp) - struct rcsbuffer *rcsbuf; - char **datap; - size_t *lenp; -{ - *datap = rcsbuf->ptr; - *lenp = rcsbuf->ptrend - rcsbuf->ptr; + if (cur >= max) + { + size_t curoff = cur - key; + expand_string (&key, &keysize, keysize + 1); + cur = key + curoff; + max = key + keysize; + } + *cur = '\0'; + *revp = key; } -/* CVS optimizes by quickly reading some header information from a - file. If it decides it needs to do more with the file, it reopens - it. We speed that up here by maintaining a cache of a single open - file, to save the time it takes to reopen the file in the common - case. */ - -static RCSNode *cached_rcs; -static struct rcsbuffer cached_rcsbuf; - -/* Cache RCS and RCSBUF. This takes responsibility for closing - RCSBUF->FP. */ - -static void -rcsbuf_cache (rcs, rcsbuf) - RCSNode *rcs; - struct rcsbuffer *rcsbuf; +/* Like getrcsrev, but don't die on error. Return the last character + read (last call to getc, which may be EOF). TODO: implement getrcsrev + in terms of this function. */ +static int +getrevnum (fp, revp) + FILE *fp; + char **revp; { - if (cached_rcs != NULL) - rcsbuf_cache_close (); - cached_rcs = rcs; - ++rcs->refcount; - cached_rcsbuf = *rcsbuf; -} - -/* If there is anything in the cache, close it. */ + char *cur; + char *max; + int c; -static void -rcsbuf_cache_close () -{ - if (cached_rcs != NULL) - { - if (fclose (cached_rcsbuf.fp) != 0) - error (0, errno, "cannot close %s", cached_rcsbuf.filename); - rcsbuf_close (&cached_rcsbuf); - freercsnode (&cached_rcs); - cached_rcs = NULL; - } -} + *revp = NULL; + do { + c = getc (fp); + if (c == EOF) + return c; + } while (whitespace (c)); -/* Open an rcsbuffer for RCS, getting it from the cache if possible. - Set *FPP to the file, and *RCSBUFP to the rcsbuf. The file should - be put at position POS. */ + if (!(isdigit (c) || c == '.')) + return c; -static void -rcsbuf_cache_open (rcs, pos, pfp, prcsbuf) - RCSNode *rcs; - long pos; - FILE **pfp; - struct rcsbuffer *prcsbuf; -{ - if (cached_rcs == rcs) + cur = key; + max = key + keysize; + while (isdigit (c) || c == '.') { - if (rcsbuf_ftell (&cached_rcsbuf) != pos) + if (cur >= max) { - if (fseek (cached_rcsbuf.fp, pos, SEEK_SET) != 0) - error (1, 0, "cannot fseek RCS file %s", - cached_rcsbuf.filename); - cached_rcsbuf.ptr = rcsbuf_buffer; - cached_rcsbuf.ptrend = rcsbuf_buffer; - cached_rcsbuf.pos = pos; + size_t curoff = cur - key; + expand_string (&key, &keysize, keysize + 1); + cur = key + curoff; + max = key + keysize; } - *pfp = cached_rcsbuf.fp; - - /* When RCS_parse opens a file using fopen_case, it frees the - filename which we cached in CACHED_RCSBUF and stores a new - file name in RCS->PATH. We avoid problems here by always - copying the filename over. FIXME: This is hackish. */ - cached_rcsbuf.filename = rcs->path; - - *prcsbuf = cached_rcsbuf; + *cur = c; - cached_rcs = NULL; - - /* Removing RCS from the cache removes a reference to it. */ - --rcs->refcount; - if (rcs->refcount <= 0) - error (1, 0, "rcsbuf_cache_open: internal error"); + c = getc (fp); + if (c == EOF) + break; + cur++; } - else - { - if (cached_rcs != NULL) - rcsbuf_cache_close (); - *pfp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); - if (*pfp == NULL) - error (1, 0, "unable to reopen `%s'", rcs->path); - if (pos != 0) - { - if (fseek (*pfp, pos, SEEK_SET) != 0) - error (1, 0, "cannot fseek RCS file %s", rcs->path); - } - rcsbuf_open (prcsbuf, *pfp, rcs->path, pos); + if (cur >= max) + { + size_t curoff = cur - key; + expand_string (&key, &keysize, keysize + 1); + cur = key + curoff; + max = key + keysize; } + *cur = '\0'; + *revp = key; + return c; } - /* * process the symbols list of the rcs file */ @@ -1902,10 +1314,10 @@ RCS_gettag (rcs, symtag, force_tag_match, simple_tag) /* XXX this is probably not necessary, --jtc */ if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); /* If tag is "HEAD", special case to get head RCS revision */ - if (tag && (STREQ (tag, TAG_HEAD) || *tag == '\0')) + if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0')) #if 0 /* This #if 0 is only in the Cygnus code. Why? Death support? */ if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC)) return ((char *) NULL); /* head request for removed file */ @@ -2053,13 +1465,25 @@ RCS_magicrev (rcs, rev) char *rev; { int rev_num; - char *xrev, *test_branch; + char *xrev, *test_branch, *local_branch_num; xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */ check_rev = xrev; + local_branch_num = getenv("CVS_LOCAL_BRANCH_NUM"); + if (local_branch_num) + { + rev_num = atoi(local_branch_num); + if (rev_num < 2) + rev_num = 2; + else + rev_num &= ~1; + } + else + rev_num = 2; + /* only look at even numbered branches */ - for (rev_num = 2; ; rev_num += 2) + for ( ; ; rev_num += 2) { /* see if the physical branch exists */ (void) sprintf (xrev, "%s.%d", rev, rev_num); @@ -2091,7 +1515,7 @@ checkmagic_proc (p, closure) Node *p; void *closure; { - if (STREQ (check_rev, p->data)) + if (strcmp (check_rev, p->data) == 0) return (1); else return (0); @@ -2245,7 +1669,7 @@ RCS_getbranch (rcs, tag, force_tag_match) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); /* find out if the tag contains a dot, or is on the trunk */ cp = strrchr (tag, '.'); @@ -2463,7 +1887,7 @@ RCS_getdate (rcs, date, force_tag_match) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); /* if the head is on a branch, try the branch first */ if (rcs->branch != NULL) @@ -2501,7 +1925,7 @@ RCS_getdate (rcs, date, force_tag_match) */ /* if we found what we're looking for, and it's not 1.1 return it */ - if (cur_rev != NULL && ! STREQ (cur_rev, "1.1")) + if (cur_rev != NULL && strcmp (cur_rev, "1.1") != 0) return (xstrdup (cur_rev)); /* look on the vendor branch */ @@ -2550,7 +1974,7 @@ RCS_getdatebranch (rcs, date, branch) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); p = findnode (rcs->versions, xrev); free (xrev); @@ -2647,7 +2071,7 @@ RCS_getrevtime (rcs, rev, date, fudge) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); /* look up the revision */ p = findnode (rcs->versions, rev); @@ -2699,7 +2123,7 @@ RCS_getlocks (rcs) assert(rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); if (rcs->locks_data) { rcs->locks = getlist (); @@ -2718,7 +2142,7 @@ RCS_symbols(rcs) assert(rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); if (rcs->symbols_data) { rcs->symbols = getlist (); @@ -2740,7 +2164,7 @@ translate_symtag (rcs, tag) const char *tag; { if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); if (rcs->symbols != NULL) { @@ -2824,7 +2248,7 @@ RCS_check_kflag (arg) { for (cpp = kflags; *cpp != NULL; cpp++) { - if (STREQ (arg, *cpp)) + if (strcmp (arg, *cpp) == 0) break; } } @@ -2882,7 +2306,7 @@ RCS_isdead (rcs, tag) RCSVers *version; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); p = findnode (rcs->versions, tag); if (p == NULL) @@ -2902,6 +2326,8 @@ RCS_getexpand (rcs) RCSNode *rcs; { assert (rcs != NULL); + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); return rcs->expand; } @@ -2910,27 +2336,31 @@ struct rcs_keyword { const char *string; size_t len; + int expandit; }; #define KEYWORD_INIT(s) (s), sizeof (s) - 1 -static const struct rcs_keyword keywords[] = +static struct rcs_keyword keywords[] = { - { KEYWORD_INIT ("Author") }, - { KEYWORD_INIT ("Date") }, - { KEYWORD_INIT ("Header") }, - { KEYWORD_INIT ("Id") }, - { KEYWORD_INIT ("Locker") }, - { KEYWORD_INIT ("Log") }, - { KEYWORD_INIT ("Name") }, - { KEYWORD_INIT ("RCSfile") }, - { KEYWORD_INIT ("Revision") }, - { KEYWORD_INIT ("Source") }, - { KEYWORD_INIT ("State") }, - { NULL, 0 } + { KEYWORD_INIT ("Author"), 1 }, + { KEYWORD_INIT ("Date"), 1 }, + { KEYWORD_INIT ("CVSHeader"), 1 }, + { KEYWORD_INIT ("Header"), 1 }, + { KEYWORD_INIT ("Id"), 1 }, + { KEYWORD_INIT ("Locker"), 1 }, + { KEYWORD_INIT ("Log"), 1 }, + { KEYWORD_INIT ("Name"), 1 }, + { KEYWORD_INIT ("RCSfile"), 1 }, + { KEYWORD_INIT ("Revision"), 1 }, + { KEYWORD_INIT ("Source"), 1 }, + { KEYWORD_INIT ("State"), 1 }, + { NULL, 0, 0 }, + { NULL, 0, 0 } }; enum keyword { KEYWORD_AUTHOR = 0, KEYWORD_DATE, + KEYWORD_CVSHEADER, KEYWORD_HEADER, KEYWORD_ID, KEYWORD_LOCKER, @@ -2939,8 +2369,10 @@ enum keyword KEYWORD_RCSFILE, KEYWORD_REVISION, KEYWORD_SOURCE, - KEYWORD_STATE + KEYWORD_STATE, + KEYWORD_LOCALID }; +enum keyword keyword_local = KEYWORD_ID; /* Convert an RCS date string into a readable string. This is like the RCS date2str function. */ @@ -3118,7 +2550,8 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) slen = s - srch; for (keyword = keywords; keyword->string != NULL; keyword++) { - if (keyword->len == slen + if (keyword->expandit + && keyword->len == slen && strncmp (keyword->string, srch, slen) == 0) { break; @@ -3165,15 +2598,25 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) free_value = 1; break; + case KEYWORD_CVSHEADER: case KEYWORD_HEADER: case KEYWORD_ID: + case KEYWORD_LOCALID: { char *path; int free_path; char *date; + char *old_path; - if (kw == KEYWORD_HEADER) + old_path = NULL; + if (kw == KEYWORD_HEADER || + (kw == KEYWORD_LOCALID && + keyword_local == KEYWORD_HEADER)) path = rcs->path; + else if (kw == KEYWORD_CVSHEADER || + (kw == KEYWORD_LOCALID && + keyword_local == KEYWORD_CVSHEADER)) + path = getfullCVSname(rcs->path, &old_path); else path = last_component (rcs->path); path = escape_keyword_value (path, &free_path); @@ -3193,6 +2636,8 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) locker != NULL ? locker : ""); if (free_path) free (path); + if (old_path) + free (old_path); free (date); free_value = 1; } @@ -3490,16 +2935,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) OPTIONS is a string such as "-kb" or "-kv" for keyword expansion options. It may be NULL to use the default expansion mode of the - file, typically "-kkv". - - On an error which prevented checking out the file, either print a - nonfatal error and return 1, or give a fatal error. On success, - return 0. */ - -/* This function mimics the behavior of `rcs co' almost exactly. The - chief difference is in its support for preserving file ownership, - permissions, and special files across checkin and checkout -- see - comments in RCS_checkin for some issues about this. -twp */ + file, typically "-kkv". */ int RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) @@ -3514,27 +2950,15 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) { int free_rev = 0; enum kflag expand; - FILE *fp, *ofp; + FILE *fp; struct stat sb; - struct rcsbuffer rcsbuf; char *key; char *value; size_t len; int free_value = 0; char *log = NULL; size_t loglen; - Node *vp = NULL; -#ifdef PRESERVE_PERMISSIONS_SUPPORT - uid_t rcs_owner; - gid_t rcs_group; - mode_t rcs_mode; - int change_rcs_owner = 0; - int change_rcs_group = 0; - int change_rcs_mode = 0; - int special_file = 0; - unsigned long devnum_long; - dev_t devnum = 0; -#endif + FILE *ofp; if (trace) { @@ -3571,25 +2995,34 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) free_rev = 1; } - if (rev == NULL || STREQ (rev, rcs->head)) + if (rev == NULL || strcmp (rev, rcs->head) == 0) { int gothead; /* We want the head revision. Try to read it directly. */ if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, &fp, &rcsbuf); + RCS_reparsercsfile (rcs, &fp); else - rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf); + { + fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); + if (fp == NULL) + error (1, 0, "unable to reopen `%s'", rcs->path); + if (fseek (fp, rcs->delta_pos, SEEK_SET) != 0) + error (1, 0, "cannot fseek RCS file"); + } gothead = 0; - if (! rcsbuf_getrevnum (&rcsbuf, &key)) - error (1, 0, "unexpected EOF reading %s", rcs->path); - while (rcsbuf_getkey (&rcsbuf, &key, &value)) + getrcsrev (fp, &key); + while (getrcskey (fp, &key, &value, &len) >= 0) { - if (STREQ (key, "log")) - log = rcsbuf_valcopy (&rcsbuf, value, 0, &loglen); - else if (STREQ (key, "text")) + if (strcmp (key, "log") == 0) + { + log = xmalloc (len); + memcpy (log, value, len); + loglen = len; + } + if (strcmp (key, "text") == 0) { gothead = 1; break; @@ -3604,23 +3037,20 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) return 1; } - rcsbuf_valpolish (&rcsbuf, value, 0, &len); - if (fstat (fileno (fp), &sb) < 0) error (1, errno, "cannot fstat %s", rcs->path); - rcsbuf_cache (rcs, &rcsbuf); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", rcs->path); } else { - struct rcsbuffer *rcsbufp; - /* It isn't the head revision of the trunk. We'll need to walk through the deltas. */ fp = NULL; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, &fp, &rcsbuf); + RCS_reparsercsfile (rcs, &fp); if (fp == NULL) { @@ -3628,17 +3058,14 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) here too. Probably should change it thusly.... */ if (stat (rcs->path, &sb) < 0) error (1, errno, "cannot stat %s", rcs->path); - rcsbufp = NULL; } else { if (fstat (fileno (fp), &sb) < 0) error (1, errno, "cannot fstat %s", rcs->path); - rcsbufp = &rcsbuf; } - RCS_deltas (rcs, fp, rcsbufp, rev, RCS_FETCH, &value, &len, - &log, &loglen); + RCS_deltas (rcs, fp, rev, RCS_FETCH, &value, &len, &log, &loglen); free_value = 1; } @@ -3661,7 +3088,7 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) ouroptions = rcs->expand; for (cpp = kflags; *cpp != NULL; cpp++) - if (STREQ (*cpp, ouroptions)) + if (strcmp (*cpp, ouroptions) == 0) break; if (*cpp != NULL) @@ -3675,186 +3102,17 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) } } -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* Handle special files and permissions, if that is desired. */ - if (preserve_perms) - { - RCSVers *vers; - Node *info; - struct hardlink_info *hlinfo; - - vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); - if (vp == NULL) - error (1, 0, "internal error: no revision information for %s", - rev == NULL ? rcs->head : rev); - vers = (RCSVers *) vp->data; - - /* First we look for symlinks, which are simplest to handle. */ - info = findnode (vers->other_delta, "symlink"); - if (info != NULL) - { - char *dest; - - if (pfn != NULL || (workfile == NULL && sout == RUN_TTY)) - error (1, 0, "symbolic link %s:%s cannot be piped", - rcs->path, vers->version); - if (workfile == NULL) - dest = sout; - else - dest = workfile; - - /* Remove `dest', just in case. It's okay to get ENOENT here, - since we just want the file not to be there. (TODO: decide - whether it should be considered an error for `dest' to exist - at this point. If so, the unlink call should be removed and - `symlink' should signal the error. -twp) */ - if (unlink (dest) < 0 && existence_error (errno)) - error (1, errno, "cannot remove %s", dest); - if (symlink (info->data, dest) < 0) - error (1, errno, "cannot create symbolic link from %s to %s", - dest, info->data); - if (free_value) - free (value); - if (free_rev) - free (rev); - return 0; - } - - /* Next, we look at this file's hardlinks field, and see whether - it is linked to any other file that has been checked out. - If so, we don't do anything else -- just link it to that file. - - If we are checking out a file to a pipe or temporary storage, - none of this should matter. Hence the `workfile != NULL' - wrapper around the whole thing. -twp */ - - if (workfile != NULL) - { - info = findnode (vers->other_delta, "hardlinks"); - if (info != NULL) - { - char *links = xstrdup (info->data); - char *working_dir = xgetwd(); - char *p, *file = NULL; - Node *n, *uptodate_link; - - /* For each file in the hardlinks field, check to see - if it exists, and if so, if it has been checked out - this iteration. */ - uptodate_link = NULL; - for (p = strtok (links, " "); - p != NULL && uptodate_link == NULL; - p = strtok (NULL, " ")) - { - file = (char *) - xmalloc (sizeof(char) * - (strlen(working_dir) + strlen(p) + 2)); - sprintf (file, "%s/%s", working_dir, p); - n = lookup_file_by_inode (file); - if (n == NULL) - { - if (strcmp (p, workfile) != 0) - { - /* One of the files that WORKFILE should be - linked to is not even in the working directory. - The user should probably be warned. */ - error (0, 0, - "warning: %s should be hardlinked to %s, but is missing", - p, workfile); - } - free (file); - continue; - } - - /* hlinfo may be NULL if, for instance, a file is being - removed. */ - hlinfo = (struct hardlink_info *) n->data; - if (hlinfo && hlinfo->checked_out) - uptodate_link = n; - free (file); - } - free (links); - free (working_dir); - - /* If we've found a file that `workfile' is supposed to be - linked to, and it has been checked out since CVS was - invoked, then simply link workfile to that file. - - If one of these conditions is not met, then we're - checking out workfile to a temp file or stdout, or - workfile is the first one in its hardlink group to be - checked out. Either way we must continue with a full - checkout. */ - - if (uptodate_link != NULL) - { - if (link (uptodate_link->key, workfile) < 0) - error (1, errno, "cannot link %s to %s", - workfile, uptodate_link->key); - hlinfo->checked_out = 1; /* probably unnecessary */ - if (free_value) - free (value); - if (free_rev) - free (rev); - return 0; - } - } - } - - info = findnode (vers->other_delta, "owner"); - if (info != NULL) - { - change_rcs_owner = 1; - rcs_owner = (uid_t) strtoul (info->data, NULL, 10); - } - info = findnode (vers->other_delta, "group"); - if (info != NULL) - { - change_rcs_group = 1; - rcs_group = (gid_t) strtoul (info->data, NULL, 10); - } - info = findnode (vers->other_delta, "permissions"); - if (info != NULL) - { - change_rcs_mode = 1; - rcs_mode = (mode_t) strtoul (info->data, NULL, 8); - } - info = findnode (vers->other_delta, "special"); - if (info != NULL) - { - /* If the size of `devtype' changes, fix the sscanf call also */ - char devtype[16]; - - if (sscanf (info->data, "%16s %lu", - devtype, &devnum_long) < 2) - error (1, 0, "%s:%s has bad `special' newphrase %s", - workfile, vers->version, info->data); - devnum = devnum_long; - if (strcmp (devtype, "character") == 0) - special_file = S_IFCHR; - else if (strcmp (devtype, "block") == 0) - special_file = S_IFBLK; - else - error (0, 0, "%s is a special file of unsupported type `%s'", - workfile, info->data); - } - } -#endif - if (expand != KFLAG_O && expand != KFLAG_B) { + Node *p; char *newvalue; - /* Don't fetch the delta node again if we already have it. */ - if (vp == NULL) - { - vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); - if (vp == NULL) - error (1, 0, "internal error: no revision information for %s", - rev == NULL ? rcs->head : rev); - } + p = findnode (rcs->versions, rev == NULL ? rcs->head : rev); + if (p == NULL) + error (1, 0, "internal error: no revision information for %s", + rev == NULL ? rcs->head : rev); - expand_keywords (rcs, (RCSVers *) vp->data, nametag, log, loglen, + expand_keywords (rcs, (RCSVers *) p->data, nametag, log, loglen, expand, value, len, &newvalue, &len); if (newvalue != value) @@ -3874,55 +3132,19 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) if (pfn != NULL) { -#ifdef PRESERVE_PERMISSIONS_SUPPORT - if (special_file) - error (1, 0, "special file %s cannot be piped to anything", - rcs->path); -#endif /* The PFN interface is very simple to implement right now, as we always have the entire file in memory. */ if (len != 0) pfn (callerdat, value, len); } -#ifdef PRESERVE_PERMISSIONS_SUPPORT - else if (special_file) - { - char *dest; - - /* Can send either to WORKFILE or to SOUT, as long as SOUT is - not RUN_TTY. */ - dest = workfile; - if (dest == NULL) - { - if (sout == RUN_TTY) - error (1, 0, "special file %s cannot be written to stdout", - rcs->path); - dest = sout; - } - - /* Unlink `dest', just in case. It's okay if this provokes a - ENOENT error. */ - if (unlink (dest) < 0 && existence_error (errno)) - error (1, errno, "cannot remove %s", dest); - if (mknod (dest, special_file, devnum) < 0) - error (1, errno, "could not create special file %s", - dest); - } -#endif else { - /* Not a special file: write to WORKFILE or SOUT. */ if (workfile == NULL) { if (sout == RUN_TTY) ofp = stdout; else { - /* Symbolic links should be removed before replacement, so that - `fopen' doesn't follow the link and open the wrong file. */ - if (islink (sout)) - if (unlink_file (sout) < 0) - error (1, errno, "cannot remove %s", sout); ofp = CVS_FOPEN (sout, expand == KFLAG_B ? "wb" : "w"); if (ofp == NULL) error (1, errno, "cannot open %s", sout); @@ -3930,11 +3152,6 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) } else { - /* Output is supposed to go to WORKFILE, so we should open that - file. Symbolic links should be removed first (see above). */ - if (islink (workfile)) - if (unlink_file (workfile) < 0) - error (1, errno, "cannot remove %s", workfile); ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w"); if (ofp == NULL) error (1, errno, "cannot open %s", workfile); @@ -3979,56 +3196,22 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) nstep = nleft; } } - } - - if (workfile != NULL) - { - int ret; - -#ifdef PRESERVE_PERMISSIONS_SUPPORT - if (!special_file && fclose (ofp) < 0) - error (1, errno, "cannot close %s", workfile); - if (change_rcs_owner || change_rcs_group) + if (workfile != NULL) { - if (chown (workfile, rcs_owner, rcs_group) < 0) - error (0, errno, "could not change file ownership on %s", + if (fclose (ofp) < 0) + error (1, errno, "cannot close %s", workfile); + if (chmod (workfile, + sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)) < 0) + error (0, errno, "cannot change mode of file %s", workfile); } - - ret = chmod (workfile, - change_rcs_mode - ? rcs_mode - : sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)); -#else - if (fclose (ofp) < 0) - error (1, errno, "cannot close %s", workfile); - - ret = chmod (workfile, - sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)); -#endif - if (ret < 0) + else if (sout != RUN_TTY) { - error (0, errno, "cannot change mode of file %s", - workfile); + if (fclose (ofp) < 0) + error (1, errno, "cannot close %s", sout); } } - else if (sout != RUN_TTY) - { - if ( -#ifdef PRESERVE_PERMISSIONS_SUPPORT - !special_file && -#endif - fclose (ofp) < 0) - error (1, errno, "cannot close %s", sout); - } - -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* If we are in the business of preserving hardlinks, then - mark this file as having been checked out. */ - if (preserve_perms && workfile != NULL) - update_hardlink_info (workfile); -#endif if (free_value) free (value); @@ -4077,7 +3260,7 @@ RCS_findlock_or_tip (rcs) lock = NULL; for (p = locklist->list->next; p != locklist->list; p = p->next) { - if (STREQ (p->data, user)) + if (strcmp (p->data, user) == 0) { if (lock != NULL) { @@ -4183,11 +3366,8 @@ compare_truncated_revnums (r, s) FIXME: isn't the max rev always the last one? If so, we don't even need a loop. */ -static char *max_rev PROTO ((const RCSVers *)); - static char * -max_rev (branchnode) - const RCSVers *branchnode; +max_rev (const RCSVers *branchnode) { Node *head; Node *bp; @@ -4322,27 +3502,22 @@ RCS_addbranch (rcs, branch) return newrevnum; } -/* Check in to RCSFILE with revision REV (which must be greater than - the largest revision) and message MESSAGE (which is checked for - legality). If FLAGS & RCS_FLAGS_DEAD, check in a dead revision. - If FLAGS & RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS & - RCS_FLAGS_MODTIME, use the working file's modification time for the - checkin time. WORKFILE is the working file to check in from, or - NULL to use the usual RCS rules for deriving it from the RCSFILE. - If FLAGS & RCS_FLAGS_KEEPFILE, don't unlink the working file; - unlinking the working file is standard RCS behavior, but is rarely - appropriate for CVS. - - This function should almost exactly mimic the behavior of `rcs ci'. The - principal point of difference is the support here for preserving file - ownership and permissions in the delta nodes. This is not a clean - solution -- precisely because it diverges from RCS's behavior -- but - it doesn't seem feasible to do this anywhere else in the code. [-twp] +/* Check in to RCSFILE with revision REV (which must be greater than the + largest revision) and message MESSAGE (which is checked for legality). + If FLAGS & RCS_FLAGS_DEAD, check in a dead revision. If FLAGS & + RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS & RCS_FLAGS_MODTIME, + use the working file's modification time for the checkin time. + WORKFILE is the working file to check in from, or NULL to use the usual + RCS rules for deriving it from the RCSFILE. Return value is -1 for error (and errno is set to indicate the error), positive for error (and an error message has been printed), or zero for success. */ +/* TODO: RCS_checkin always unlinks the working file after checkin -- + then RCS_checkout checks it out again. The logic should probably + be reversed here. */ + int RCS_checkin (rcs, workfile, message, rev, flags) RCSNode *rcs; @@ -4365,7 +3540,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) commitpt = NULL; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); /* Get basename of working file. Is there a library function to do this? I couldn't find one. -twp */ @@ -4419,92 +3594,6 @@ RCS_checkin (rcs, workfile, message, rev, flags) else delta->state = xstrdup ("Exp"); -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* If permissions should be preserved on this project, then - save the permission info. */ - if (preserve_perms) - { - Node *np; - struct stat sb; - char buf[64]; /* static buffer should be safe: see usage. -twp */ - char *fullpath; - - delta->other_delta = getlist(); - - if (CVS_LSTAT (workfile, &sb) < 0) - error (1, 1, "cannot lstat %s", workfile); - - if (S_ISLNK (sb.st_mode)) - { - np = getnode(); - np->key = xstrdup ("symlink"); - np->data = xreadlink (workfile); - addnode (delta->other_delta, np); - } - else - { - (void) sprintf (buf, "%u", sb.st_uid); - np = getnode(); - np->key = xstrdup ("owner"); - np->data = xstrdup (buf); - addnode (delta->other_delta, np); - - (void) sprintf (buf, "%u", sb.st_gid); - np = getnode(); - np->key = xstrdup ("group"); - np->data = xstrdup (buf); - addnode (delta->other_delta, np); - - (void) sprintf (buf, "%o", sb.st_mode & 07777); - np = getnode(); - np->key = xstrdup ("permissions"); - np->data = xstrdup (buf); - addnode (delta->other_delta, np); - - /* Save device number. */ - switch (sb.st_mode & S_IFMT) - { - case S_IFREG: break; - case S_IFCHR: - case S_IFBLK: - np = getnode(); - np->key = xstrdup ("special"); - sprintf (buf, "%s %lu", - ((sb.st_mode & S_IFMT) == S_IFCHR - ? "character" : "block"), - (unsigned long) sb.st_rdev); - np->data = xstrdup (buf); - addnode (delta->other_delta, np); - break; - - default: - error (0, 0, "special file %s has unknown type", workfile); - } - - /* Save hardlinks. */ - fullpath = xgetwd(); - fullpath = xrealloc (fullpath, - strlen(fullpath) + strlen(workfile) + 2); - sprintf (fullpath + strlen(fullpath), "/%s", workfile); - - np = lookup_file_by_inode (fullpath); - if (np == NULL) - { - error (1, 0, "lost information on %s's linkage", workfile); - } - else - { - struct hardlink_info *hlinfo; - hlinfo = (struct hardlink_info *) np->data; - np = getnode(); - np->key = xstrdup ("hardlinks"); - np->data = xstrdup (hlinfo->links); - (void) addnode (delta->other_delta, np); - } - } - } -#endif - /* Create a new deltatext node. */ dtext = (Deltatext *) xmalloc (sizeof (Deltatext)); memset (dtext, 0, sizeof (Deltatext)); @@ -4543,9 +3632,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) dtext->version = xstrdup (newrev); bufsize = 0; - get_file (workfile, workfile, - rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", - &dtext->text, &bufsize, &dtext->len); + get_file(workfile, workfile, "r", &dtext->text, &bufsize, &dtext->len); if (!checkin_quiet) { @@ -4554,9 +3641,6 @@ RCS_checkin (rcs, workfile, message, rev, flags) cvs_output ("\n", 1); } - /* We are probably about to invalidate any cached file. */ - rcsbuf_cache_close (); - fout = rcs_internal_lockfile (rcs->path); RCS_putadmin (rcs, fout); RCS_putdtree (rcs, rcs->head, fout); @@ -4568,12 +3652,12 @@ RCS_checkin (rcs, workfile, message, rev, flags) rcs_internal_unlockfile (fout, rcs->path); freedeltatext (dtext); - if ((flags & RCS_FLAGS_KEEPFILE) == 0) - { - if (unlink_file (workfile) < 0) - /* FIXME-update-dir: message does not include update_dir. */ - error (0, errno, "cannot remove %s", workfile); - } + /* Removing the file here is an RCS user-visible behavior which + we almost surely do not need in the CVS case. In fact, getting + rid of it should clean up link_file and friends in import.c. */ + if (unlink_file (workfile) < 0) + /* FIXME-update-dir: message does not include update_dir. */ + error (0, errno, "cannot remove %s", workfile); if (!checkin_quiet) cvs_output ("done\n", 5); @@ -4613,7 +3697,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) goto checkin_done; } else if (commitpt->next == NULL - || STREQ (commitpt->version, rcs->head)) + || strcmp (commitpt->version, rcs->head) == 0) delta->version = increment_revnum (commitpt->version); else delta->version = RCS_addbranch (rcs, commitpt->version); @@ -4717,7 +3801,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) nodep = findnode (RCS_getlocks (rcs), commitpt->version); if (nodep != NULL) { - if (! STREQ (nodep->data, delta->author)) + if (strcmp (nodep->data, delta->author) != 0) { error (0, 0, "%s: revision %s locked by %s", rcs->path, @@ -4739,13 +3823,13 @@ RCS_checkin (rcs, workfile, message, rev, flags) tmpfile = cvs_temp_name(); status = RCS_checkout (rcs, NULL, commitpt->version, NULL, ((rcs->expand != NULL - && STREQ (rcs->expand, "b")) + && strcmp (rcs->expand, "b") == 0) ? "-kb" : "-ko"), tmpfile, (RCSCHECKOUTPROC)0, NULL); if (status != 0) - error (1, 0, + error (1, status < 0 ? errno : 0, "could not check out revision %s of `%s'", commitpt->version, rcs->path); @@ -4756,18 +3840,18 @@ RCS_checkin (rcs, workfile, message, rev, flags) /* Diff options should include --binary if the RCS file has -kb set in its `expand' field. */ - diffopts = (rcs->expand != NULL && STREQ (rcs->expand, "b") + diffopts = (rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 ? "-a -n --binary" : "-a -n"); - if (STREQ (commitpt->version, rcs->head) && + if (strcmp (commitpt->version, rcs->head) == 0 && numdots (delta->version) == 1) { /* If this revision is being inserted on the trunk, the change text for the new delta should be the contents of the working file ... */ bufsize = 0; get_file (workfile, workfile, - rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", + rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 ? "rb" : "r", &dtext->text, &bufsize, &dtext->len); /* ... and the change text for the old delta should be a diff. */ @@ -4803,7 +3887,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) This should cause no harm, but doesn't strike me as immensely clean. */ get_file (changefile, changefile, - rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", + rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 ? "rb" : "r", &commitpt->text->text, &bufsize, &commitpt->text->len); /* If COMMITPT->TEXT->TEXT is NULL, it means that CHANGEFILE @@ -4840,7 +3924,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) /* See the comment above, at the other get_file invocation, regarding binary vs. text. */ get_file (changefile, changefile, - rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", + rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 ? "rb" : "r", &dtext->text, &bufsize, &dtext->len); if (dtext->text == NULL) @@ -4864,7 +3948,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) if (numdots (commitpt->version) == numdots (delta->version)) { - if (STREQ (commitpt->version, rcs->head)) + if (strcmp (commitpt->version, rcs->head) == 0) { delta->next = rcs->head; rcs->head = xstrdup (delta->version); @@ -4894,12 +3978,12 @@ RCS_checkin (rcs, workfile, message, rev, flags) RCS_rewrite (rcs, dtext, commitpt->version); - if ((flags & RCS_FLAGS_KEEPFILE) == 0) - { - if (unlink_file (workfile) < 0) - /* FIXME-update-dir: message does not include update_dir. */ - error (1, errno, "cannot remove %s", workfile); - } + /* Removing the file here is an RCS user-visible behavior which + we almost surely do not need in the CVS case. In fact, getting + rid of it should clean up link_file and friends in import.c. */ + if (unlink_file (workfile) < 0) + /* FIXME-update-dir: message does not include update_dir. */ + error (1, errno, "cannot remove %s", workfile); if (unlink_file (tmpfile) < 0) error (0, errno, "cannot remove %s", tmpfile); if (unlink_file (changefile) < 0) @@ -4952,70 +4036,42 @@ RCS_cmp_file (rcs, rev, options, filename) int retcode; if (options != NULL && options[0] != '\0') - binary = STREQ (options, "-kb"); + binary = (strcmp (options, "-kb") == 0); else { char *expand; expand = RCS_getexpand (rcs); - if (expand != NULL && STREQ (expand, "b")) + if (expand != NULL && strcmp (expand, "b") == 0) binary = 1; else binary = 0; } -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* If CVS is to deal properly with special files (when - PreservePermissions is on), the best way is to check out the - revision to a temporary file and call `xcmp' on the two disk - files. xcmp needs to handle non-regular files properly anyway, - so calling it simplifies RCS_cmp_file. We *could* just yank - the delta node out of the version tree and look for device - numbers, but writing to disk and calling xcmp is a better - abstraction (therefore probably more robust). -twp */ + fp = CVS_FOPEN (filename, binary ? FOPEN_BINARY_READ : "r"); - if (preserve_perms) - { - char *tmp; + data.filename = filename; + data.fp = fp; + data.different = 0; - tmp = cvs_temp_name(); - retcode = RCS_checkout(rcs, NULL, rev, NULL, options, tmp, NULL, NULL); - if (retcode != 0) - return 1; + retcode = RCS_checkout (rcs, (char *) NULL, rev, (char *) NULL, + options, RUN_TTY, cmp_file_buffer, + (void *) &data); - retcode = xcmp (tmp, filename); - if (CVS_UNLINK (tmp) < 0) - error (0, errno, "cannot remove %s", tmp); - return retcode; - } - else -#endif + /* If we have not yet found a difference, make sure that we are at + the end of the file. */ + if (! data.different) { - fp = CVS_FOPEN (filename, binary ? FOPEN_BINARY_READ : "r"); - - data.filename = filename; - data.fp = fp; - data.different = 0; - - retcode = RCS_checkout (rcs, (char *) NULL, rev, (char *) NULL, - options, RUN_TTY, cmp_file_buffer, - (void *) &data); - - /* If we have not yet found a difference, make sure that we are at - the end of the file. */ - if (! data.different) - { - if (getc (fp) != EOF) - data.different = 1; - } - - fclose (fp); - - if (retcode != 0) - return 1; - - return data.different; + if (getc (fp) != EOF) + data.different = 1; } + + fclose (fp); + + if (retcode != 0) + return 1; + + return data.different; } /* This is a subroutine of RCS_cmp_file. It is passed to @@ -5083,12 +4139,12 @@ RCS_settag (rcs, tag, rev) Node *node; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); /* FIXME: This check should be moved to RCS_check_tag. There is no reason for it to be here. */ - if (STREQ (tag, TAG_BASE) - || STREQ (tag, TAG_HEAD)) + if (strcmp (tag, TAG_BASE) == 0 + || strcmp (tag, TAG_HEAD) == 0) { /* Print the name of the tag might be considered redundant with the caller, which also prints it. Perhaps this helps @@ -5142,7 +4198,7 @@ RCS_deltag (rcs, tag) List *symbols; Node *node; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); symbols = RCS_symbols (rcs); if (symbols == NULL) @@ -5165,14 +4221,11 @@ RCS_setbranch (rcs, rev) const char *rev; { if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); - - if (rev && ! *rev) - rev = NULL; + RCS_reparsercsfile (rcs, NULL); if (rev == NULL && rcs->branch == NULL) return 0; - if (rev != NULL && rcs->branch != NULL && STREQ (rev, rcs->branch)) + if (rev != NULL && rcs->branch != NULL && strcmp (rev, rcs->branch) == 0) return 0; if (rcs->branch != NULL) @@ -5202,7 +4255,7 @@ RCS_lock (rcs, rev, lock_quiet) char *xrev = NULL; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); locks = RCS_getlocks (rcs); if (locks == NULL) @@ -5246,7 +4299,7 @@ RCS_lock (rcs, rev, lock_quiet) p = findnode (locks, xrev); if (p != NULL) { - if (STREQ (p->data, user)) + if (strcmp (p->data, user) == 0) { /* We already own the lock on this revision, so do nothing. */ free (xrev); @@ -5298,7 +4351,7 @@ RCS_unlock (rcs, rev, unlock_quiet) user = getcaller(); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); /* If rev is NULL, unlock the latest revision (first in rcs->locks) held by the caller. */ @@ -5325,7 +4378,7 @@ RCS_unlock (rcs, rev, unlock_quiet) lock = NULL; for (p = locks->list->next; p != locks->list; p = p->next) { - if (STREQ (p->data, user)) + if (strcmp (p->data, user) == 0) { if (lock != NULL) { @@ -5364,7 +4417,7 @@ RCS_unlock (rcs, rev, unlock_quiet) return 0; } - if (! STREQ (lock->data, user)) + if (strcmp (lock->data, user) != 0) { /* If the revision is locked by someone else, notify them. Note that this shouldn't ever happen if RCS_unlock @@ -5400,7 +4453,7 @@ RCS_addaccess (rcs, user) char *access, *a; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); if (rcs->access == NULL) rcs->access = xstrdup (user); @@ -5409,7 +4462,7 @@ RCS_addaccess (rcs, user) access = xstrdup (rcs->access); for (a = strtok (access, " "); a != NULL; a = strtok (NULL, " ")) { - if (STREQ (a, user)) + if (strcmp (a, user) == 0) { free (access); return; @@ -5433,7 +4486,7 @@ RCS_delaccess (rcs, user) int ulen; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); if (rcs->access == NULL) return; @@ -5464,7 +4517,7 @@ RCS_getaccess (rcs) RCSNode *rcs; { if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + RCS_reparsercsfile (rcs, NULL); return rcs->access; } @@ -5480,7 +4533,7 @@ findtag (node, arg) { char *rev = (char *)arg; - if (STREQ (node->data, rev)) + if (strcmp (node->data, rev) == 0) return 1; else return 0; @@ -5573,7 +4626,7 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) { /* A range consisting of a branch number means the latest revision on that branch. */ - if (RCS_isbranch (rcs, rev1) && STREQ (rev1, rev2)) + if (RCS_isbranch (rcs, rev1) && strcmp (rev1, rev2) == 0) rev1 = rev2 = RCS_getbranch (rcs, rev1, 0); else { @@ -5664,12 +4717,12 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) *bp = '.'; } } - else if (! STREQ (rev1, branchpoint)) + else if (strcmp (rev1, branchpoint) != 0) { /* Walk deltas from BRANCHPOINT on, looking for REV1. */ nodep = findnode (rcs->versions, branchpoint); revp = (RCSVers *) nodep->data; - while (revp->next != NULL && ! STREQ (revp->next, rev1)) + while (revp->next != NULL && strcmp (revp->next, rev1) != 0) { revp = (RCSVers *) nodep->data; nodep = findnode (rcs->versions, revp->next); @@ -5718,7 +4771,7 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) revp = (RCSVers *) nodep->data; if (rev2 != NULL) - found = STREQ (revp->version, rev2); + found = (strcmp (revp->version, rev2) == 0); next = revp->next; if ((!found && next != NULL) || rev2_inclusive || rev2 == NULL) @@ -5815,6 +4868,13 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) if (status > 0) goto delrev_done; + else if (status < 0) + { + error (0, errno, + "cannot check out revision %s of %s", after, rcs->path); + goto delrev_done; + } + if (before == NULL) { /* We are deleting revisions from the head of the tree, @@ -5842,6 +4902,12 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) (RCSCHECKOUTPROC)0, NULL); if (status > 0) goto delrev_done; + else if (status < 0) + { + error (0, errno, "cannot check out revision %s of %s", + before, rcs->path); + goto delrev_done; + } outfile = cvs_temp_name(); status = diff_exec (beforefile, afterfile, "-n", outfile); @@ -5886,7 +4952,7 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) outdated. (FIXME: would it be safe to use the `dead' field for this? Doubtful.) */ for (next = rev1; - next != NULL && (after == NULL || ! STREQ (next, after)); + next != NULL && (after == NULL || strcmp (next, after) != 0); next = revp->next) { nodep = findnode (rcs->versions, next); @@ -5911,13 +4977,13 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) /* beforep's ->next field already should be equal to after, which I think is always NULL in this case. */ ; - else if (STREQ (rev1, branchpoint)) + else if (strcmp (rev1, branchpoint) == 0) { nodep = findnode (rcs->versions, before); revp = (RCSVers *) nodep->data; nodep = revp->branches->list->next; while (nodep != revp->branches->list && - ! STREQ (nodep->key, rev1)) + strcmp (nodep->key, rev1) != 0) nodep = nodep->next; assert (nodep != revp->branches->list); if (after == NULL) @@ -6437,10 +5503,9 @@ rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen) On error, give a fatal error. */ static void -RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) +RCS_deltas (rcs, fp, version, op, text, len, log, loglen) RCSNode *rcs; FILE *fp; - struct rcsbuffer *rcsbuf; char *version; enum rcs_delta_op op; char **text; @@ -6448,7 +5513,6 @@ RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) char **log; size_t *loglen; { - struct rcsbuffer rcsbuf_local; char *branchversion; char *cpversion; char *key; @@ -6458,6 +5522,7 @@ RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) RCSVers *prev_vers; RCSVers *trunk_vers; char *next; + int n; int ishead, isnext, isversion, onbranch; Node *node; struct linevector headlines; @@ -6467,8 +5532,11 @@ RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) if (fp == NULL) { - rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf_local); - rcsbuf = &rcsbuf_local; + fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); + if (fp == NULL) + error (1, 0, "unable to reopen `%s'", rcs->path); + if (fseek (fp, rcs->delta_pos, SEEK_SET) != 0) + error (1, 0, "cannot fseek RCS file"); } ishead = 1; @@ -6495,10 +5563,9 @@ RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) *cpversion = '\0'; do { - if (! rcsbuf_getrevnum (rcsbuf, &key)) - error (1, 0, "unexpected EOF reading RCS file %s", rcs->path); + getrcsrev (fp, &key); - if (next != NULL && ! STREQ (next, key)) + if (next != NULL && strcmp (next, key) != 0) { /* This is not the next version we need. It is a branch version which we want to ignore. */ @@ -6523,30 +5590,27 @@ RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) next = vers->next; /* Compare key and trunkversion now, because key points to - storage controlled by rcsbuf_getkey. */ - if (STREQ (branchversion, key)) + storage controlled by getrcskey. */ + if (strcmp (branchversion, key) == 0) isversion = 1; else isversion = 0; } - while (1) + while ((n = getrcskey (fp, &key, &value, &vallen)) >= 0) { - if (! rcsbuf_getkey (rcsbuf, &key, &value)) - error (1, 0, "%s does not appear to be a valid rcs file", - rcs->path); - if (log != NULL && isversion - && STREQ (key, "log") - && STREQ (branchversion, version)) + && strcmp (key, "log") == 0 + && strcmp (branchversion, version) == 0) { - *log = rcsbuf_valcopy (rcsbuf, value, 0, loglen); + *log = xmalloc (vallen); + memcpy (*log, value, vallen); + *loglen = vallen; } - if (STREQ (key, "text")) + if (strcmp (key, "text") == 0) { - rcsbuf_valpolish (rcsbuf, value, 0, &vallen); if (ishead) { if (! linevector_add (&curlines, value, vallen, NULL, 0)) @@ -6565,12 +5629,14 @@ RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) break; } } + if (n < 0) + goto l_error; if (isversion) { /* This is either the version we want, or it is the branchpoint to the version we want. */ - if (STREQ (branchversion, version)) + if (strcmp (branchversion, version) == 0) { /* This is the version we want. */ linevector_copy (&headlines, &curlines); @@ -6650,8 +5716,9 @@ RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) } while (next != NULL); free (branchversion); - - rcsbuf_cache (rcs, rcsbuf); + + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", rcs->path); if (! foundhead) error (1, 0, "could not find desired version %s in %s", @@ -6755,148 +5822,125 @@ RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) linevector_free (&trunklines); return; + + l_error: + if (ferror (fp)) + error (1, errno, "cannot read %s", rcs->path); + else + error (1, 0, "%s does not appear to be a valid rcs file", + rcs->path); } -/* Read the information for a single delta from the RCS buffer RCSBUF, - whose name is RCSFILE. *KEYP and *VALP are either NULL, or the - first key/value pair to read, as set by rcsbuf_getkey. Return NULL - if there are no more deltas. Store the key/value pair which - terminated the read in *KEYP and *VALP. */ - static RCSVers * -getdelta (rcsbuf, rcsfile, keyp, valp) - struct rcsbuffer *rcsbuf; +getdelta (fp, rcsfile) + FILE *fp; char *rcsfile; - char **keyp; - char **valp; { RCSVers *vnode; char *key, *value, *cp; + long fpos; Node *kv; - /* Get revision number if it wasn't passed in. This uses - rcsbuf_getkey because it doesn't croak when encountering - unexpected input. As a result, we have to play unholy games - with `key' and `value'. */ - if (*keyp != NULL) - { - key = *keyp; - value = *valp; - } - else - { - if (! rcsbuf_getkey (rcsbuf, &key, &value)) - error (1, 0, "%s: unexpected EOF", rcsfile); - } + vnode = (RCSVers *) xmalloc (sizeof (RCSVers)); + memset (vnode, 0, sizeof (RCSVers)); + + /* Get revision number. This uses getrcskey because it doesn't + croak when encountering unexpected input. As a result, we have + to play unholy games with `key' and `value'. */ + fpos = ftell (fp); + getrcskey (fp, &key, &value, NULL); /* Make sure that it is a revision number and not a cabbage or something. */ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) /* do nothing */ ; - /* Note that when comparing with RCSDATE, we are not massaging - VALUE from the string found in the RCS file. This is OK since - we know exactly what to expect. */ - if (*cp != '\0' || strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) != 0) + if (*cp != '\0' || strncmp (RCSDATE, value, strlen (RCSDATE)) != 0) { - *keyp = key; - *valp = value; + (void) fseek (fp, fpos, SEEK_SET); + free (vnode); return NULL; } - - vnode = (RCSVers *) xmalloc (sizeof (RCSVers)); - memset (vnode, 0, sizeof (RCSVers)); - vnode->version = xstrdup (key); - /* Grab the value of the date from value. Note that we are not - massaging VALUE from the string found in the RCS file. */ - cp = value + (sizeof RCSDATE) - 1; /* skip the "date" keyword */ + /* grab the value of the date from value */ + cp = value + strlen (RCSDATE);/* skip the "date" keyword */ while (whitespace (*cp)) /* take space off front of value */ cp++; vnode->date = xstrdup (cp); /* Get author field. */ - if (! rcsbuf_getkey (rcsbuf, &key, &value)) - { - error (1, 0, "unexpected end of file reading %s", rcsfile); - } - if (! STREQ (key, "author")) + (void) getrcskey (fp, &key, &value, NULL); + /* FIXME: should be using errno in case of ferror. */ + if (key == NULL || strcmp (key, "author") != 0) error (1, 0, "\ -unable to parse %s; `author' not in the expected place", rcsfile); - vnode->author = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); +unable to parse rcs file; `author' not in the expected place"); + vnode->author = xstrdup (value); /* Get state field. */ - if (! rcsbuf_getkey (rcsbuf, &key, &value)) - { - error (1, 0, "unexpected end of file reading %s", rcsfile); - } - if (! STREQ (key, "state")) + (void) getrcskey (fp, &key, &value, NULL); + /* FIXME: should be using errno in case of ferror. */ + if (key == NULL || strcmp (key, "state") != 0) error (1, 0, "\ -unable to parse %s; `state' not in the expected place", rcsfile); - vnode->state = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); - if (STREQ (value, "dead")) +unable to parse rcs file; `state' not in the expected place"); + vnode->state = xstrdup (value); + if (strcmp (value, "dead") == 0) { vnode->dead = 1; } /* Note that "branches" and "next" are in fact mandatory, according - to doc/RCSFILES. */ + to doc/RCSFILES. We perhaps should be giving an error if they + are not there. */ /* fill in the branch list (if any branches exist) */ - if (! rcsbuf_getkey (rcsbuf, &key, &value)) - { - error (1, 0, "unexpected end of file reading %s", rcsfile); - } - if (STREQ (key, RCSDESC)) + fpos = ftell (fp); + (void) getrcskey (fp, &key, &value, NULL); + /* FIXME: should be handling various error conditions better. */ + if (key != NULL && strcmp (key, RCSDESC) == 0) { - *keyp = key; - *valp = value; - /* Probably could/should be a fatal error. */ - error (0, 0, "warning: 'branches' keyword missing from %s", rcsfile); + (void) fseek (fp, fpos, SEEK_SET); return vnode; } if (value != (char *) NULL) { vnode->branches = getlist (); - /* Note that we are not massaging VALUE from the string found - in the RCS file. */ do_branches (vnode->branches, value); } /* fill in the next field if there is a next revision */ - if (! rcsbuf_getkey (rcsbuf, &key, &value)) + fpos = ftell (fp); + (void) getrcskey (fp, &key, &value, NULL); + /* FIXME: should be handling various error conditions better. */ + if (key != NULL && strcmp (key, RCSDESC) == 0) { - error (1, 0, "unexpected end of file reading %s", rcsfile); - } - if (STREQ (key, RCSDESC)) - { - *keyp = key; - *valp = value; - /* Probably could/should be a fatal error. */ - error (0, 0, "warning: 'next' keyword missing from %s", rcsfile); + (void) fseek (fp, fpos, SEEK_SET); return vnode; } if (value != (char *) NULL) - vnode->next = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); + vnode->next = xstrdup (value); /* * XXX - this is where we put the symbolic link stuff??? * (into newphrases in the deltas). */ + /* FIXME: Does not correctly handle errors, e.g. from stdio. */ while (1) { - if (! rcsbuf_getkey (rcsbuf, &key, &value)) - error (1, 0, "unexpected end of file reading %s", rcsfile); + fpos = ftell (fp); + if (getrcskey (fp, &key, &value, NULL) < 0) + break; - if (STREQ (key, RCSDESC)) + assert (key != NULL); + + if (strcmp (key, RCSDESC) == 0) break; /* Enable use of repositories created by certain obsolete versions of CVS. This code should remain indefinately; there is no procedure for converting old repositories, and checking for it is harmless. */ - if (STREQ (key, RCSDEAD)) + if (strcmp(key, RCSDEAD) == 0) { vnode->dead = 1; if (vnode->state != NULL) @@ -6907,9 +5951,6 @@ unable to parse %s; `state' not in the expected place", rcsfile); /* if we have a new revision number, we're done with this delta */ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) /* do nothing */ ; - /* Note that when comparing with RCSDATE, we are not massaging - VALUE from the string found in the RCS file. This is OK - since we know exactly what to expect. */ if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) break; @@ -6920,7 +5961,7 @@ unable to parse %s; `state' not in the expected place", rcsfile); kv = getnode (); kv->type = RCSFIELD; kv->key = xstrdup (key); - kv->data = rcsbuf_valcopy (rcsbuf, value, 1, (size_t *) NULL); + kv->data = xstrdup (value); if (addnode (vnode->other_delta, kv) != 0) { /* Complaining about duplicate keys in newphrases seems @@ -6932,11 +5973,11 @@ unable to parse %s; `state' not in the expected place", rcsfile); key, rcsfile); freenode (kv); } - } + } - /* Return the key which caused us to fail back to the caller. */ - *keyp = key; - *valp = value; + /* We got here because we read beyond the end of a delta. Seek back + to the beginning of the erroneous read. */ + (void) fseek (fp, fpos, SEEK_SET); return vnode; } @@ -6957,21 +5998,25 @@ freedeltatext (d) } static Deltatext * -RCS_getdeltatext (rcs, fp, rcsbuf) +RCS_getdeltatext (rcs, fp) RCSNode *rcs; FILE *fp; - struct rcsbuffer *rcsbuf; { char *num; char *key, *value; + int n; Node *p; Deltatext *d; + size_t textlen; /* Get the revision number. */ - if (! rcsbuf_getrevnum (rcsbuf, &num)) + n = getrevnum (fp, &num); + if (ferror (fp)) + error (1, errno, "%s: cannot read", rcs->path); + if (n == EOF) { - /* If num == NULL, it means we reached EOF naturally. That's - fine. */ + /* If n == EOF and num == NULL, it means we reached EOF + naturally. That's fine. */ if (num == NULL) return NULL; else @@ -6987,36 +6032,36 @@ RCS_getdeltatext (rcs, fp, rcsbuf) d->version = xstrdup (num); /* Get the log message. */ - if (! rcsbuf_getkey (rcsbuf, &key, &value)) + if (getrcskey (fp, &key, &value, NULL) < 0) error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); - if (! STREQ (key, "log")) + if (strcmp (key, "log") != 0) error (1, 0, "%s, delta %s: expected `log', got `%s'", rcs->path, num, key); - d->log = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); + d->log = xstrdup (value); /* Get random newphrases. */ d->other = getlist(); - while (1) + for (n = getrcskey (fp, &key, &value, &textlen); + n >= 0 && strcmp (key, "text") != 0; + n = getrcskey (fp, &key, &value, &textlen)) { - if (! rcsbuf_getkey (rcsbuf, &key, &value)) - error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); - - if (STREQ (key, "text")) - break; - p = getnode(); p->type = RCSFIELD; p->key = xstrdup (key); - p->data = rcsbuf_valcopy (rcsbuf, value, 1, (size_t *) NULL); + p->data = xstrdup (value); if (addnode (d->other, p) < 0) { error (0, 0, "warning: %s, delta %s: duplicate field `%s'", rcs->path, num, key); } } + if (n < 0) + error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); /* Get the change text. We already know that this key is `text'. */ - d->text = rcsbuf_valcopy (rcsbuf, value, 0, &d->len); + d->text = (char *) malloc (textlen + 1); + d->len = textlen; + memcpy (d->text, value, textlen); return d; } @@ -7033,23 +6078,11 @@ RCS_getdeltatext (rcs, fp, rcsbuf) not get corrupted. */ static int -putsymbol_proc (symnode, fparg) +putsymbol_proc (symnode, fp) Node *symnode; - void *fparg; + void *fp; { - FILE *fp = (FILE *) fparg; - - /* A fiddly optimization: this code used to just call fprintf, but - in an old repository with hundreds of tags this can get called - hundreds of thousands of times when doing a cvs tag. Since - tagging is a relatively common operation, and using putc and - fputs is just as comprehensible, the change is worthwhile. */ - putc ('\n', fp); - putc ('\t', fp); - fputs (symnode->key, fp); - putc (':', fp); - fputs (symnode->data, fp); - return 0; + return fprintf ((FILE *) fp, "\n\t%s:%s", symnode->key, symnode->data); } static int putlock_proc PROTO ((Node *, void *)); @@ -7101,9 +6134,9 @@ putrcsfield_proc (node, vfp) /* desc, log and text fields should not be terminated with semicolon; all other fields should be. */ - if (! STREQ (node->key, "desc") && - ! STREQ (node->key, "log") && - ! STREQ (node->key, "text")) + if (strcmp (node->key, "desc") != 0 && + strcmp (node->key, "log") != 0 && + strcmp (node->key, "text") != 0) { putc (';', fp); } @@ -7133,15 +6166,7 @@ RCS_putadmin (rcs, fp) fputs (";\n", fp); fputs (RCSSYMBOLS, fp); - /* If we haven't had to convert the symbols to a list yet, don't - force a conversion now; just write out the string. */ - if (rcs->symbols == NULL && rcs->symbols_data != NULL) - { - fputs ("\n\t", fp); - fputs (rcs->symbols_data, fp); - } - else - walklist (RCS_symbols (rcs), putsymbol_proc, (void *) fp); + walklist (RCS_symbols(rcs), putsymbol_proc, (void *) fp); fputs (";\n", fp); fputs ("locks", fp); @@ -7159,7 +6184,7 @@ RCS_putadmin (rcs, fp) expand_at_signs (rcs->comment, (off_t) strlen (rcs->comment), fp); fputs ("@;\n", fp); } - if (rcs->expand && ! STREQ (rcs->expand, "kv")) + if (rcs->expand && strcmp (rcs->expand, "kv") != 0) fprintf (fp, "%s\t@%s@;\n", RCSEXPAND, rcs->expand); walklist (rcs->other, putrcsfield_proc, (void *) fp); @@ -7281,68 +6306,40 @@ putdeltatext (fp, d) increasing order.) */ static void -RCS_copydeltas (rcs, fin, rcsbufin, fout, newdtext, insertpt) +RCS_copydeltas (rcs, fin, fout, newdtext, insertpt) RCSNode *rcs; FILE *fin; - struct rcsbuffer *rcsbufin; FILE *fout; Deltatext *newdtext; char *insertpt; { - int actions; + Deltatext *dtext; RCSVers *dadmin; Node *np; int insertbefore, found; - char *bufrest; - int nls; - size_t buflen; - char buf[8192]; - int got; - - /* Count the number of versions for which we have to do some - special operation. */ - actions = walklist (rcs->versions, count_delta_actions, (void *) NULL); /* Make a note of whether NEWDTEXT should be inserted before or after its INSERTPT. */ insertbefore = (newdtext != NULL && numdots (newdtext->version) == 1); - while (actions != 0 || newdtext != NULL) + found = 0; + while ((dtext = RCS_getdeltatext (rcs, fin)) != NULL) { - Deltatext *dtext; - - dtext = RCS_getdeltatext (rcs, fin, rcsbufin); - - /* We shouldn't hit EOF here, because that would imply that - some action was not taken, or that we could not insert - NEWDTEXT. */ - if (dtext == NULL) - error (1, 0, "internal error: EOF too early in RCS_copydeltas"); - - found = (insertpt != NULL && STREQ (dtext->version, insertpt)); + found = (insertpt != NULL && strcmp (dtext->version, insertpt) == 0); if (found && insertbefore) - { putdeltatext (fout, newdtext); - newdtext = NULL; - insertpt = NULL; - } np = findnode (rcs->versions, dtext->version); dadmin = (RCSVers *) np->data; /* If this revision has been outdated, just skip it. */ if (dadmin->outdated) - { - --actions; continue; - } /* Update the change text for this delta. New change text data may come from cvs admin -m, cvs admin -o, or cvs ci. */ if (dadmin->text != NULL) { - if (dadmin->text->log != NULL || dadmin->text->text != NULL) - --actions; if (dadmin->text->log != NULL) { free (dtext->log); @@ -7361,92 +6358,9 @@ RCS_copydeltas (rcs, fin, rcsbufin, fout, newdtext, insertpt) freedeltatext (dtext); if (found && !insertbefore) - { putdeltatext (fout, newdtext); - newdtext = NULL; - insertpt = NULL; - } - } - - /* Copy the rest of the file directly, without bothering to - interpret it. The caller will handle error checking by calling - ferror. - - We just wrote a newline to the file, either in putdeltatext or - in the caller. However, we may not have read the corresponding - newline from the file, because rcsbuf_getkey returns as soon as - it finds the end of the '@' string for the desc or text key. - Therefore, we may read three newlines when we should really - only write two, and we check for that case here. This is not - an semantically important issue; we only do it to make our RCS - files look traditional. */ - - nls = 3; - - rcsbuf_get_buffered (rcsbufin, &bufrest, &buflen); - if (buflen > 0) - { - if (bufrest[0] != '\n' - || strncmp (bufrest, "\n\n\n", buflen < 3 ? buflen : 3) != 0) - { - nls = 0; - } - else - { - if (buflen < 3) - nls -= buflen; - else - { - ++bufrest; - --buflen; - nls = 0; - } - } - - fwrite (bufrest, 1, buflen, fout); - } - - while ((got = fread (buf, 1, sizeof buf, fin)) != 0) - { - if (nls > 0 - && got >= nls - && buf[0] == '\n' - && strncmp (buf, "\n\n\n", nls) == 0) - { - fwrite (buf + 1, 1, got - 1, fout); - } - else - { - fwrite (buf, 1, got, fout); - } - - nls = 0; } -} - -/* A helper procedure for RCS_copydeltas. This is called via walklist - to count the number of RCS revisions for which some special action - is required. */ - -int -count_delta_actions (np, ignore) - Node *np; - void *ignore; -{ - RCSVers *dadmin; - - dadmin = (RCSVers *) np->data; - - if (dadmin->outdated) - return 1; - - if (dadmin->text != NULL - && (dadmin->text->log != NULL || dadmin->text->text != NULL)) - { - return 1; - } - - return 0; + putc ('\n', fout); } /* RCS_internal_lockfile and RCS_internal_unlockfile perform RCS-style @@ -7554,12 +6468,6 @@ rcs_internal_unlockfile (fp, rcsfile) corrupting the repository. */ if (ferror (fp)) - /* The only case in which using errno here would be meaningful - is if we happen to have left errno unmolested since the call - which produced the error (e.g. fprintf). That is pretty - fragile even if it happens to sometimes be true. The real - solution is to check each call to fprintf rather than waiting - until the end like this. */ error (1, 0, "error writing to lock file %s", lockfile); if (fclose (fp) == EOF) error (1, errno, "error closing lock file %s", lockfile); @@ -7603,7 +6511,6 @@ RCS_rewrite (rcs, newdtext, insertpt) char *insertpt; { FILE *fin, *fout; - struct rcsbuffer rcsbufin; if (noexec) return; @@ -7615,7 +6522,10 @@ RCS_rewrite (rcs, newdtext, insertpt) RCS_putdesc (rcs, fout); /* Open the original RCS file and seek to the first delta text. */ - rcsbuf_cache_open (rcs, rcs->delta_pos, &fin, &rcsbufin); + if ((fin = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ)) == NULL) + error (1, errno, "cannot open RCS file `%s' for reading", rcs->path); + if (fseek (fin, rcs->delta_pos, SEEK_SET) < 0) + error (1, errno, "cannot fseek in RCS file %s", rcs->path); /* Update delta_pos to the current position in the output file. Do NOT move these statements: they must be done after fin has @@ -7625,19 +6535,10 @@ RCS_rewrite (rcs, newdtext, insertpt) if (rcs->delta_pos == -1) error (1, errno, "cannot ftell in RCS file %s", rcs->path); - RCS_copydeltas (rcs, fin, &rcsbufin, fout, newdtext, insertpt); + RCS_copydeltas (rcs, fin, fout, newdtext, insertpt); - /* We don't want to call rcsbuf_cache here, since we're about to - delete the file. */ - rcsbuf_close (&rcsbufin); if (ferror (fin)) - /* The only case in which using errno here would be meaningful - is if we happen to have left errno unmolested since the call - which produced the error (e.g. fread). That is pretty - fragile even if it happens to sometimes be true. The real - solution is to make sure that all the code which reads - from fin checks for errors itself (some does, some doesn't). */ - error (0, 0, "warning: when closing RCS file `%s'", rcs->path); + error (0, errno, "warning: when closing RCS file `%s'", rcs->path); if (fclose (fin) < 0) error (0, errno, "warning: closing RCS file `%s'", rcs->path); @@ -7662,18 +6563,13 @@ annotate_fileproc (callerdat, finfo) struct file_info *finfo; { FILE *fp = NULL; - struct rcsbuffer *rcsbufp = NULL; - struct rcsbuffer rcsbuf; char *version; if (finfo->rcs == NULL) return (1); if (finfo->rcs->flags & PARTIAL) - { - RCS_reparsercsfile (finfo->rcs, &fp, &rcsbuf); - rcsbufp = &rcsbuf; - } + RCS_reparsercsfile (finfo->rcs, &fp); version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, (int *) NULL); @@ -7686,7 +6582,7 @@ annotate_fileproc (callerdat, finfo) cvs_outerr (finfo->fullname, 0); cvs_outerr ("\n***************\n", 0); - RCS_deltas (finfo->rcs, fp, rcsbufp, version, RCS_ANNOTATE, (char **) NULL, + RCS_deltas (finfo->rcs, fp, version, RCS_ANNOTATE, (char **) NULL, (size_t) NULL, (char **) NULL, (size_t *) NULL); free (version); return 0; @@ -7826,3 +6722,104 @@ make_file_label (path, rev, rcs) return label; } +void +RCS_setlocalid (arg) + const char *arg; +{ + char *copy, *next, *key; + + copy = xstrdup(arg); + next = copy; + key = strtok(next, "="); + + keywords[KEYWORD_LOCALID].string = xstrdup(key); + keywords[KEYWORD_LOCALID].len = strlen(key); + keywords[KEYWORD_LOCALID].expandit = 1; + + /* options? */ + while (key = strtok(NULL, ",")) { + if (!strcmp(key, keywords[KEYWORD_ID].string)) + keyword_local = KEYWORD_ID; + else if (!strcmp(key, keywords[KEYWORD_HEADER].string)) + keyword_local = KEYWORD_HEADER; + else if (!strcmp(key, keywords[KEYWORD_CVSHEADER].string)) + keyword_local = KEYWORD_CVSHEADER; + else + error(1, 0, "Unknown LocalId mode: %s", key); + } + free(copy); +} + +void +RCS_setincexc (arg) + const char *arg; +{ + char *key; + char *copy, *next; + int include = 0; + struct rcs_keyword *keyword; + + copy = xstrdup(arg); + next = copy; + switch (*next++) { + case 'e': + include = 0; + break; + case 'i': + include = 1; + break; + default: + free(copy); + return; + } + + if (include) + for (keyword = keywords; keyword->string != NULL; keyword++) + { + keyword->expandit = 0; + } + + key = strtok(next, ","); + while (key) { + for (keyword = keywords; keyword->string != NULL; keyword++) { + if (strcmp (keyword->string, key) == 0) + keyword->expandit = include; + } + key = strtok(NULL, ","); + } + free(copy); + return; +} + +#define ATTIC "/" CVSATTIC +static char * +getfullCVSname(CVSname, pathstore) + char *CVSname, **pathstore; +{ + if (CVSroot_directory) { + int rootlen; + char *c = NULL; + int alen = sizeof(ATTIC) - 1; + + *pathstore = xstrdup(CVSname); + if ((c = strrchr(*pathstore, '/')) != NULL) { + if (c - *pathstore >= alen) { + if (!strncmp(c - alen, ATTIC, alen)) { + while (*c != '\0') { + *(c - alen) = *c; + c++; + } + *(c - alen) = '\0'; + } + } + } + + rootlen = strlen(CVSroot_directory); + if (!strncmp(*pathstore, CVSroot_directory, rootlen) && + (*pathstore)[rootlen] == '/') + CVSname = (*pathstore + rootlen + 1); + else + CVSname = (*pathstore); + } + return CVSname; +} diff --git a/contrib/cvs/src/rcs.h b/contrib/cvs/src/rcs.h index 400d1a0..60c4f3f 100644 --- a/contrib/cvs/src/rcs.h +++ b/contrib/cvs/src/rcs.h @@ -169,17 +169,13 @@ typedef struct rcsversnode RCSVers; /* The type of a function passed to RCS_checkout. */ typedef void (*RCSCHECKOUTPROC) PROTO ((void *, const char *, size_t)); -#ifdef __STDC__ -struct rcsbuffer; -#endif - /* * exported interfaces */ RCSNode *RCS_parse PROTO((const char *file, const char *repos)); RCSNode *RCS_parsercsfile PROTO((char *rcsfile)); void RCS_fully_parse PROTO((RCSNode *)); -void RCS_reparsercsfile PROTO((RCSNode *, FILE **, struct rcsbuffer *)); +void RCS_reparsercsfile PROTO((RCSNode *, FILE **)); char *RCS_check_kflag PROTO((const char *arg)); char *RCS_getdate PROTO((RCSNode * rcs, char *date, int force_tag_match)); @@ -219,9 +215,10 @@ char *RCS_getaccess PROTO ((RCSNode *)); void RCS_rewrite PROTO ((RCSNode *, Deltatext *, char *)); int rcs_change_text PROTO ((const char *, char *, size_t, const char *, size_t, char **, size_t *)); -char *make_file_label PROTO ((char *, char *, RCSNode *)); +void RCS_setincexc PROTO ((const char *arg)); -extern int preserve_perms; +void RCS_setlocalid PROTO ((const char *arg)); +char *make_file_label PROTO ((char *, char *, RCSNode *)); /* From import.c. */ extern int add_rcs_file PROTO ((char *, char *, char *, char *, char *, diff --git a/contrib/cvs/src/rcscmds.c b/contrib/cvs/src/rcscmds.c index 3086b57..ea638b3 100644 --- a/contrib/cvs/src/rcscmds.c +++ b/contrib/cvs/src/rcscmds.c @@ -135,10 +135,29 @@ call_diff_add_arg (s) /* diff_run is imported from libdiff.a. */ extern int diff_run PROTO ((int argc, char **argv, char *out)); +#if defined(__FreeBSD__) && defined(SERVER_SUPPORT) +/* Some 4.4BSD Glue to hack stdout and stderr to call cvs_output */ + +static +int writehook(cookie, buf, len) + void *cookie; /* really [struct bufcookie *] */ + const char *buf; /* characters to copy */ + int len; /* length to copy */ +{ + void (*fn)(const char *, size_t) = + (void (*)(const char *, size_t))cookie; + + (*fn)(buf, len); + + return 0; +} +#endif + static int call_diff (out) char *out; { +#if !defined(__FreeBSD__) || !defined(SERVER_SUPPORT) /* Try to keep the out-of-order bugs at bay (protocol_pipe for cvs_output with has "Index: foo" and such; stdout and/or stderr for diff's output). I think the only reason that this used to not be such @@ -148,12 +167,58 @@ call_diff (out) The real fix, of course, will be to have the diff library do all its output through callbacks (which CVS will supply as cvs_output and cvs_outerr). */ - sleep (1); +#if defined(SERVER_SUPPORT) + /* only do this on the server if it's in protocol mode */ + if (error_use_protocol || server_active) + sleep (1); +#endif if (out == RUN_TTY) return diff_run (call_diff_argc, call_diff_argv, NULL); else return diff_run (call_diff_argc, call_diff_argv, out); +#else + /* avoid that sleep "by any means necessary".. */ + void *save_out, *save_err; + void *cookie_out, *cookie_err; + int rv; + + /* XXX: the cvs_out*() funcs call the buf routines which can call + cvs_outerr(), and also calls malloc which might printf something. + FreeBSD's malloc doesn't do this at the moment, so recursion should + be avoided. No guarantees for other BSD4.4-Lite* systems though. */ + if (error_use_protocol || server_active) { + save_out = stdout->_write; + save_err = stderr->_write; + cookie_out = stdout->_cookie; + cookie_err = stderr->_cookie; + + fflush(stdout); + fflush(stderr); + + stdout->_write = writehook; + stderr->_write = writehook; + stdout->_cookie = cvs_output; + stderr->_cookie = cvs_outerr; + } + + if (out == RUN_TTY) + rv = diff_run (call_diff_argc, call_diff_argv, NULL); + else + rv = diff_run (call_diff_argc, call_diff_argv, out); + + if (error_use_protocol || server_active) { + fflush(stdout); + fflush(stderr); + + stdout->_write = save_out; + stderr->_write = save_err; + stdout->_cookie = cookie_out; + stderr->_cookie = cookie_err; + } + + return (rv); +#endif } extern int diff3_run PROTO ((int argc, char **argv, char *out)); @@ -162,6 +227,7 @@ static int call_diff3 (out) char *out; { +#if !defined(__FreeBSD__) || !defined(SERVER_SUPPORT) /* Try to keep the out-of-order bugs at bay (protocol_pipe for cvs_output with has "Index: foo" and such; stdout and/or stderr for diff's output). I think the only reason that this used to not be such @@ -171,12 +237,58 @@ call_diff3 (out) The real fix, of course, will be to have the diff library do all its output through callbacks (which CVS will supply as cvs_output and cvs_outerr). */ - sleep (1); +#if defined(SERVER_SUPPORT) + /* only do this on the server if it's in protocol mode */ + if (error_use_protocol || server_active) + sleep (1); +#endif if (out == RUN_TTY) return diff3_run (call_diff_argc, call_diff_argv, NULL); else return diff3_run (call_diff_argc, call_diff_argv, out); +#else + /* avoid that sleep "by any means necessary".. */ + void *save_out, *save_err; + void *cookie_out, *cookie_err; + int rv; + + /* XXX: the cvs_out*() funcs call the buf routines which can call + cvs_outerr(), and also calls malloc which might printf something. + FreeBSD's malloc doesn't do this at the moment, so recursion should + be avoided. No guarantees for other BSD4.4-Lite* systems though. */ + if (error_use_protocol || server_active) { + save_out = stdout->_write; + save_err = stderr->_write; + cookie_out = stdout->_cookie; + cookie_err = stderr->_cookie; + + fflush(stdout); + fflush(stderr); + + stdout->_write = writehook; + stderr->_write = writehook; + stdout->_cookie = cvs_output; + stderr->_cookie = cvs_outerr; + } + + if (out == RUN_TTY) + rv = diff3_run (call_diff_argc, call_diff_argv, NULL); + else + rv = diff3_run (call_diff_argc, call_diff_argv, out); + + if (error_use_protocol || server_active) { + fflush(stdout); + fflush(stderr); + + stdout->_write = save_out; + stderr->_write = save_err; + stdout->_cookie = cookie_out; + stderr->_cookie = cookie_err; + } + + return (rv); +#endif } @@ -487,42 +599,7 @@ diff_exec (file1, file2, options, out) char *options; char *out; { - char *args; - -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* If either file1 or file2 are special files, pretend they are - /dev/null. Reason: suppose a file that represents a block - special device in one revision becomes a regular file. CVS - must find the `difference' between these files, but a special - file contains no data useful for calculating this metric. The - safe thing to do is to treat the special file as an empty file, - thus recording the regular file's full contents. Doing so will - create extremely large deltas at the point of transition - between device files and regular files, but this is probably - very rare anyway. - - There may be ways around this, but I think they are fraught - with danger. -twp */ - - if (preserve_perms && - strcmp (file1, DEVNULL) != 0 && - strcmp (file2, DEVNULL) != 0) - { - struct stat sb1, sb2; - - if (CVS_LSTAT (file1, &sb1) < 0) - error (1, errno, "cannot get file information for %s", file1); - if (CVS_LSTAT (file2, &sb2) < 0) - error (1, errno, "cannot get file information for %s", file2); - - if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) - file1 = DEVNULL; - if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) - file2 = DEVNULL; - } -#endif - - args = xmalloc (strlen (options) + 10); + char *args = xmalloc (strlen (options) + 10); /* The first word in this string is used only for error reporting. */ sprintf (args, "diff %s", options); call_diff_setup (args); @@ -542,31 +619,7 @@ diff_execv (file1, file2, label1, label2, options, out) char *options; char *out; { - char *args; - -#ifdef PRESERVE_PERMISSIONS_SUPPORT - /* Pretend that special files are /dev/null for purposes of making - diffs. See comments in diff_exec. */ - - if (preserve_perms && - strcmp (file1, DEVNULL) != 0 && - strcmp (file2, DEVNULL) != 0) - { - struct stat sb1, sb2; - - if (CVS_LSTAT (file1, &sb1) < 0) - error (1, errno, "cannot get file information for %s", file1); - if (CVS_LSTAT (file2, &sb2) < 0) - error (1, errno, "cannot get file information for %s", file2); - - if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) - file1 = DEVNULL; - if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) - file2 = DEVNULL; - } -#endif - - args = xmalloc (strlen (options) + 10); + char *args = xmalloc (strlen (options) + 10); /* The first word in this string is used only for error reporting. */ /* I guess we are pretty confident that options starts with a space. */ sprintf (args, "diff%s", options); diff --git a/contrib/cvs/src/recurse.c b/contrib/cvs/src/recurse.c index 3896bc7..25b5b71 100644 --- a/contrib/cvs/src/recurse.c +++ b/contrib/cvs/src/recurse.c @@ -448,7 +448,7 @@ do_recursion (frame) { /* we will process files, so pre-parse entries */ if (frame->which & W_LOCAL) - entries = Entries_Open (frame->aflag, NULL); + entries = Entries_Open (frame->aflag); } } @@ -790,6 +790,7 @@ but CVS uses %s for its own purposes; skipping %s directory", repository = srepository; } +#if 0 /* Put back update_dir. I think this is the same as just setting update_dir back to saved_update_dir, but there are a few cases I'm not sure about (in particular, if DIR is "." and update_dir is @@ -800,6 +801,14 @@ but CVS uses %s for its own purposes; skipping %s directory", else update_dir[0] = '\0'; free (saved_update_dir); +#else + /* The above code is cactus!!! - it doesn't handle descending + multiple directories at once! ie: it recurses down several + dirs and then back up one. This breaks 'diff', 'update', + 'commit', etc. */ + free (update_dir); + update_dir = saved_update_dir; +#endif return (err); } diff --git a/contrib/cvs/src/server.c b/contrib/cvs/src/server.c index a005646..e820388 100644 --- a/contrib/cvs/src/server.c +++ b/contrib/cvs/src/server.c @@ -584,6 +584,9 @@ serve_root (arg) nothing. But for rsh, we need to do it now. */ parse_config (CVSroot_directory); + /* Now is a good time to read CVSROOT/options too. */ + parseopts(CVSroot_directory); + path = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + sizeof (CVSROOTADM_HISTORY) @@ -599,7 +602,7 @@ serve_root (arg) } (void) strcat (path, "/"); (void) strcat (path, CVSROOTADM_HISTORY); - if (isfile (path) && !isaccessible (path, R_OK | W_OK)) + if (readonlyfs == 0 && isfile (path) && !isaccessible (path, R_OK | W_OK)) { save_errno = errno; pending_error_text = malloc (80 + strlen (path)); @@ -1194,7 +1197,7 @@ serve_modified (arg) } { - int status = change_mode (arg, mode_text, 0); + int status = change_mode (arg, mode_text); free (mode_text); if (status) { @@ -3355,13 +3358,12 @@ server_modtime (finfo, vers_ts) /* See server.h for description. */ void -server_updated (finfo, vers, updated, mode, checksum, filebuf) +server_updated (finfo, vers, updated, file_info, checksum) struct file_info *finfo; Vers_TS *vers; enum server_updated_arg4 updated; - mode_t mode; + struct stat *file_info; unsigned char *checksum; - struct buffer *filebuf; { if (noexec) { @@ -3380,43 +3382,25 @@ server_updated (finfo, vers, updated, mode, checksum, filebuf) if (entries_line != NULL && scratched_file == NULL) { FILE *f; + struct stat sb; struct buffer_data *list, *last; unsigned long size; char size_text[80]; - if (filebuf != NULL) - { - size = buf_length (filebuf); - if (mode == (mode_t) -1) - error (1, 0, "\ -CVS server internal error: no mode in server_updated"); - } - else + if ( CVS_STAT (finfo->file, &sb) < 0) { - struct stat sb; - - if ( CVS_STAT (finfo->file, &sb) < 0) - { - if (existence_error (errno)) - { - /* If we have a sticky tag for a branch on which - the file is dead, and cvs update the directory, - it gets a T_CHECKOUT but no file. So in this - case just forget the whole thing. */ - free (entries_line); - entries_line = NULL; - goto done; - } - error (1, errno, "reading %s", finfo->fullname); - } - size = sb.st_size; - if (mode == (mode_t) -1) + if (existence_error (errno)) { - /* FIXME: When we check out files the umask of the - server (set in .bashrc if rsh is in use) affects - what mode we send, and it shouldn't. */ - mode = sb.st_mode; + /* + * If we have a sticky tag for a branch on which the + * file is dead, and cvs update the directory, it gets + * a T_CHECKOUT but no file. So in this case just + * forget the whole thing. */ + free (entries_line); + entries_line = NULL; + goto done; } + error (1, errno, "reading %s", finfo->fullname); } if (checksum != NULL) @@ -3485,14 +3469,21 @@ CVS server internal error: no mode in server_updated"); { char *mode_string; - mode_string = mode_to_string (mode); + /* FIXME: When we check out files the umask of the server + (set in .bashrc if rsh is in use) affects what mode we + send, and it shouldn't. */ + if (file_info != NULL) + mode_string = mode_to_string (file_info->st_mode); + else + mode_string = mode_to_string (sb.st_mode); buf_output0 (protocol, mode_string); buf_output0 (protocol, "\n"); free (mode_string); } list = last = NULL; - if (size > 0) + size = 0; + if (sb.st_size > 0) { /* Throughout this section we use binary mode to read the file we are sending. The client handles any line ending @@ -3505,19 +3496,11 @@ CVS server internal error: no mode in server_updated"); * might be computable somehow; using 100 here is just * a first approximation. */ - && size > 100) + && sb.st_size > 100) { int status, fd, gzip_status; pid_t gzip_pid; - /* Callers must avoid passing us a buffer if - file_gzip_level is set. We could handle this case, - but it's not worth it since this case never arises - with a current client and server. */ - if (filebuf != NULL) - error (1, 0, "\ -CVS server internal error: unhandled case in server_updated"); - fd = CVS_OPEN (finfo->file, O_RDONLY | OPEN_BINARY, 0); if (fd < 0) error (1, errno, "reading %s", finfo->fullname); @@ -3540,14 +3523,15 @@ CVS server internal error: unhandled case in server_updated"); /* Prepending length with "z" is flag for using gzip here. */ buf_output0 (protocol, "z"); } - else if (filebuf == NULL) + else { long status; + size = sb.st_size; f = CVS_FOPEN (finfo->file, "rb"); if (f == NULL) error (1, errno, "reading %s", finfo->fullname); - status = buf_read_file (f, size, &list, &last); + status = buf_read_file (f, sb.st_size, &list, &last); if (status == -2) (*protocol->memory_error) (protocol); else if (status != 0) @@ -3561,13 +3545,7 @@ CVS server internal error: unhandled case in server_updated"); sprintf (size_text, "%lu\n", size); buf_output0 (protocol, size_text); - if (filebuf == NULL) - buf_append_data (protocol, list, last); - else - { - buf_append_buffer (protocol, filebuf); - buf_free (filebuf); - } + buf_append_data (protocol, list, last); /* Note we only send a newline here if the file ended with one. */ /* @@ -3580,7 +3558,6 @@ CVS server internal error: unhandled case in server_updated"); if ((updated == SERVER_UPDATED || updated == SERVER_PATCHED || updated == SERVER_RCS_DIFF) - && filebuf != NULL /* But if we are joining, we'll need the file when we call join_file. */ && !joining ()) @@ -5634,7 +5611,7 @@ this client does not support writing binary files to stdout"); I assume that what they are talking about can also be helped by flushing the stream before changing the mode. */ fflush (stdout); - oldmode = _setmode (_fileno (stdout), OPEN_BINARY); + oldmode = _setmode (_fileno (stdout), _O_BINARY); if (oldmode < 0) error (0, errno, "failed to setmode on stdout"); #endif @@ -5649,7 +5626,7 @@ this client does not support writing binary files to stdout"); } #ifdef USE_SETMODE_STDOUT fflush (stdout); - if (_setmode (_fileno (stdout), oldmode) != OPEN_BINARY) + if (_setmode (_fileno (stdout), oldmode) != _O_BINARY) error (0, errno, "failed to setmode on stdout"); #endif } diff --git a/contrib/cvs/src/update.c b/contrib/cvs/src/update.c index 2e7bda3..2aa3032 100644 --- a/contrib/cvs/src/update.c +++ b/contrib/cvs/src/update.c @@ -42,14 +42,9 @@ #include "fileattr.h" #include "edit.h" #include "getline.h" -#include "buffer.h" -#include "hardlink.h" static int checkout_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts, - int adding, int merging, int update_server)); -#ifdef SERVER_SUPPORT -static void checkout_to_buffer PROTO ((void *, const char *, size_t)); -#endif + int adding)); #ifdef SERVER_SUPPORT static int patch_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts, @@ -69,9 +64,6 @@ static int update_fileproc PROTO ((void *callerdat, struct file_info *)); static int update_filesdone_proc PROTO ((void *callerdat, int err, char *repository, char *update_dir, List *entries)); -#ifdef PRESERVE_PERMISSIONS_SUPPORT -static int get_linkinfo_proc PROTO ((void *callerdat, struct file_info *)); -#endif static void write_letter PROTO ((struct file_info *finfo, int letter)); static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts)); @@ -87,10 +79,6 @@ static char *date = NULL; static int rewrite_tag; static int nonbranch; -/* If we set the tag or date for a subdirectory, we use this to undo - the setting. See update_dirent_proc. */ -static char *tag_update_dir; - static char *join_rev1, *date_rev1; static char *join_rev2, *date_rev2; static int aflag = 0; @@ -449,32 +437,6 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, else date_rev2 = (char *) NULL; -#ifdef PRESERVE_PERMISSIONS_SUPPORT - if (preserve_perms) - { - /* We need to do an extra recursion, bleah. It's to make sure - that we know as much as possible about file linkage. */ - hardlist = getlist(); - working_dir = xgetwd(); /* save top-level working dir */ - - /* FIXME-twp: the arguments to start_recursion make me dizzy. This - function call was copied from the update_fileproc call that - follows it; someone should make sure that I did it right. */ - err = start_recursion (get_linkinfo_proc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, - argc, argv, local, which, aflag, 1, - preload_update_dir, 1); - if (err) - return (err); - - /* FIXME-twp: at this point we should walk the hardlist - and update the `links' field of each hardlink_info struct - to list the files that are linked on dist. That would make - it easier & more efficient to compare the disk linkage with - the repository linkage (a simple strcmp). */ - } -#endif - /* call the recursion processor */ err = start_recursion (update_fileproc, update_filesdone_proc, update_dirent_proc, update_dirleave_proc, NULL, @@ -494,50 +456,6 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, return (err); } -#ifdef PRESERVE_PERMISSIONS_SUPPORT -/* - * The get_linkinfo_proc callback adds each file to the hardlist - * (see hardlink.c). - */ - -static int -get_linkinfo_proc (callerdat, finfo) - void *callerdat; - struct file_info *finfo; -{ - char *fullpath; - Node *linkp; - struct hardlink_info *hlinfo; - - /* Get the full pathname of the current file. */ - fullpath = xmalloc (strlen(working_dir) + - strlen(finfo->fullname) + 2); - sprintf (fullpath, "%s/%s", working_dir, finfo->fullname); - - /* To permit recursing into subdirectories, files - are keyed on the full pathname and not on the basename. */ - linkp = lookup_file_by_inode (fullpath); - if (linkp == NULL) - { - /* The file isn't on disk; we are probably restoring - a file that was removed. */ - return 0; - } - - /* Create a new, empty hardlink_info node. */ - hlinfo = (struct hardlink_info *) - xmalloc (sizeof (struct hardlink_info)); - - hlinfo->status = (Ctype) 0; /* is this dumb? */ - hlinfo->checked_out = 0; - hlinfo->links = NULL; - - linkp->data = (char *) hlinfo; - - return 0; -} -#endif - /* * This is the callback proc for update. It is called for each file in each * directory by the recursion code. The current directory is the local @@ -572,7 +490,7 @@ update_fileproc (callerdat, finfo) && tag != NULL && finfo->rcs != NULL) { - char *rev = RCS_getversion (finfo->rcs, tag, NULL, 1, NULL); + char *rev = RCS_getversion (finfo->rcs, tag, date, 1, NULL); if (rev != NULL && !RCS_nodeisbranch (finfo->rcs, tag)) nonbranch = 1; @@ -608,7 +526,7 @@ update_fileproc (callerdat, finfo) #ifdef SERVER_SUPPORT case T_PATCH: /* needs patch */ #endif - retval = checkout_file (finfo, vers, 0, 0, 0); + retval = checkout_file (finfo, vers, 0); break; default: /* can't ever happen :-) */ @@ -711,8 +629,7 @@ update_fileproc (callerdat, finfo) (rcs_diff_patches ? SERVER_RCS_DIFF : SERVER_PATCHED), - file_info.st_mode, checksum, - (struct buffer *) NULL); + &file_info, checksum); break; } } @@ -722,7 +639,13 @@ update_fileproc (callerdat, finfo) /* Fall through. */ #endif case T_CHECKOUT: /* needs checkout */ - retval = checkout_file (finfo, vers, 0, 0, 1); + retval = checkout_file (finfo, vers, 0); +#ifdef SERVER_SUPPORT + if (server_active && retval == 0) + server_updated (finfo, vers, + SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); +#endif break; case T_ADDED: /* added but not committed */ write_letter (finfo, 'A'); @@ -740,9 +663,8 @@ update_fileproc (callerdat, finfo) if (vers->ts_user == NULL) server_scratch_entry_only (); server_updated (finfo, vers, - SERVER_UPDATED, (mode_t) -1, - (unsigned char *) NULL, - (struct buffer *) NULL); + SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); } #endif break; @@ -884,26 +806,6 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries) else { /* otherwise, create the dir and appropriate adm files */ - - /* If no tag or date were specified on the command line, - and we're not using -A, we want the subdirectory to use - the tag and date, if any, of the current directory. - That way, update -d will work correctly when working on - a branch. - - We use TAG_UPDATE_DIR to undo the tag setting in - update_dirleave_proc. If we did not do this, we would - not correctly handle a working directory with multiple - tags (and maybe we should prohibit such working - directories, but they work now and we shouldn't make - them stop working without more thought). */ - if ((tag == NULL && date == NULL) && ! aflag) - { - ParseTag (&tag, &date, &nonbranch); - if (tag != NULL || date != NULL) - tag_update_dir = xstrdup (update_dir); - } - make_directory (dir); Create_Admin (dir, update_dir, repository, tag, date, /* This is a guess. We will rewrite it later @@ -995,27 +897,6 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries) { FILE *fp; - /* If we set the tag or date for a new subdirectory in - update_dirent_proc, and we're now done with that subdirectory, - undo the tag/date setting. Note that we know that the tag and - date were both originally NULL in this case. */ - if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0) - { - if (tag != NULL) - { - free (tag); - tag = NULL; - } - if (date != NULL) - { - free (date); - date = NULL; - } - nonbranch = 0; - free (tag_update_dir); - tag_update_dir = NULL; - } - /* run the update_prog if there is one */ /* FIXME: should be checking for errors from CVS_FOPEN and printing them if not existence_error. */ @@ -1135,7 +1016,7 @@ isemptydir (dir, might_not_exist) if (CVS_CHDIR (dir) < 0) error (1, errno, "cannot change directory to %s", dir); - l = Entries_Open (0, NULL); + l = Entries_Open (0); files_removed = walklist (l, isremoved, 0); Entries_Close (l); @@ -1182,29 +1063,22 @@ scratch_file (finfo) * Check out a file. */ static int -checkout_file (finfo, vers_ts, adding, merging, update_server) +checkout_file (finfo, vers_ts, adding) struct file_info *finfo; Vers_TS *vers_ts; int adding; - int merging; - int update_server; { char *backup; int set_time, retval = 0; + int retcode = 0; int status; int file_is_dead; - struct buffer *revbuf; + /* Solely to suppress a warning from gcc -Wall. */ backup = NULL; - revbuf = NULL; - /* Don't screw with backup files if we're going to stdout, or if - we are the server. */ - if (!pipeout -#ifdef SERVER_SUPPORT - && ! server_active -#endif - ) + /* don't screw with backup files if we're going to stdout */ + if (!pipeout) { backup = xmalloc (strlen (finfo->file) + sizeof (CVSADM) @@ -1214,7 +1088,6 @@ checkout_file (finfo, vers_ts, adding, merging, update_server) if (isfile (finfo->file)) rename_file (finfo->file, backup); else - { /* If -f/-t wrappers are being used to wrap up a directory, then backup might be a directory instead of just a file. */ if (unlink_file_dir (backup) < 0) @@ -1224,9 +1097,6 @@ checkout_file (finfo, vers_ts, adding, merging, update_server) /* FIXME: should include update_dir in message. */ error (0, errno, "error removing %s", backup); } - free (backup); - backup = NULL; - } } file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs); @@ -1254,64 +1124,22 @@ VERS: ", 0); } } -#ifdef SERVER_SUPPORT - if (update_server - && server_active - && ! pipeout - && ! file_gzip_level - && ! joining () - && ! wrap_name_has (finfo->file, WRAP_FROMCVS)) - { - revbuf = buf_nonio_initialize ((BUFMEMERRPROC) NULL); - status = RCS_checkout (vers_ts->srcfile, (char *) NULL, - vers_ts->vn_rcs, vers_ts->vn_tag, - vers_ts->options, RUN_TTY, - checkout_to_buffer, revbuf); - } - else -#endif - status = RCS_checkout (vers_ts->srcfile, - pipeout ? NULL : finfo->file, - vers_ts->vn_rcs, vers_ts->vn_tag, - vers_ts->options, RUN_TTY, - (RCSCHECKOUTPROC) NULL, (void *) NULL); + status = RCS_checkout (vers_ts->srcfile, + pipeout ? NULL : finfo->file, + vers_ts->vn_rcs, vers_ts->vn_tag, + vers_ts->options, RUN_TTY, + (RCSCHECKOUTPROC) NULL, (void *) NULL); } if (file_is_dead || status == 0) { - mode_t mode; - - mode = (mode_t) -1; - if (!pipeout) { Vers_TS *xvers_ts; - if (revbuf != NULL) - { - struct stat sb; - - /* FIXME: We should have RCS_checkout return the mode. */ - if (stat (vers_ts->srcfile->path, &sb) < 0) - error (1, errno, "cannot stat %s", - vers_ts->srcfile->path); - mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH); - } - if (cvswrite && !file_is_dead && !fileattr_get (finfo->file, "_watched")) - { - if (revbuf == NULL) - xchmod (finfo->file, 1); - else - { - /* We know that we are the server here, so - although xchmod checks umask, we don't bother. */ - mode |= (((mode & S_IRUSR) ? S_IWUSR : 0) - | ((mode & S_IRGRP) ? S_IWGRP : 0) - | ((mode & S_IROTH) ? S_IWOTH : 0)); - } - } + xchmod (finfo->file, 1); { /* A newly checked out file is never under the spell @@ -1343,27 +1171,6 @@ VERS: ", 0); if (strcmp (xvers_ts->options, "-V4") == 0) xvers_ts->options[0] = '\0'; - if (revbuf != NULL) - { - /* If we stored the file data into a buffer, then we - didn't create a file at all, so xvers_ts->ts_user - is wrong. The correct value is to have it be the - same as xvers_ts->ts_rcs, meaning that the working - file is unchanged from the RCS file. - - FIXME: We should tell Version_TS not to waste time - statting the nonexistent file. - - FIXME: Actually, I don't think the ts_user value - matters at all here. The only use I know of is - that it is printed in a trace message by - Server_Register. */ - - if (xvers_ts->ts_user != NULL) - free (xvers_ts->ts_user); - xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs); - } - (void) time (&last_register_time); if (file_is_dead) @@ -1372,7 +1179,7 @@ VERS: ", 0); { error (0, 0, "warning: %s is not (any longer) pertinent", - finfo->fullname); + finfo->fullname); } Scratch_Entry (finfo->entries, finfo->file); #ifdef SERVER_SUPPORT @@ -1419,29 +1226,21 @@ VERS: ", 0); write_letter (finfo, 'U'); } } - -#ifdef SERVER_SUPPORT - if (update_server && server_active) - server_updated (finfo, vers_ts, - merging ? SERVER_MERGED : SERVER_UPDATED, - mode, (unsigned char *) NULL, revbuf); -#endif } else { - if (backup != NULL) - { + int old_errno = errno; /* save errno value over the rename */ + + if (!pipeout && isfile (backup)) rename_file (backup, finfo->file); - free (backup); - backup = NULL; - } - error (0, 0, "could not check out %s", finfo->fullname); + error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0, + "could not check out %s", finfo->fullname); - retval = status; + retval = retcode; } - if (backup != NULL) + if (!pipeout) { /* If -f/-t wrappers are being used to wrap up a directory, then backup might be a directory instead of just a file. */ @@ -1460,24 +1259,6 @@ VERS: ", 0); #ifdef SERVER_SUPPORT -/* This function is used to write data from a file being checked out - into a buffer. */ - -static void -checkout_to_buffer (callerdat, data, len) - void *callerdat; - const char *data; - size_t len; -{ - struct buffer *buf = (struct buffer *) callerdat; - - buf_output (buf, data, len); -} - -#endif /* SERVER_SUPPORT */ - -#ifdef SERVER_SUPPORT - /* This structure is used to pass information between patch_file and patch_file_write. */ @@ -1553,14 +1334,6 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) free (rev); } - /* If the revision is dead, let checkout_file handle it rather - than duplicating the processing here. */ - if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs)) - { - *docheckout = 1; - return 0; - } - backup = xmalloc (strlen (finfo->file) + sizeof (CVSADM) + sizeof (CVSPREFIX) @@ -1865,32 +1638,25 @@ merge_file (finfo, vers) xchmod (finfo->file, 1); if (strcmp (vers->options, "-kb") == 0 - || wrap_merge_is_copy (finfo->file) - || special_file_mismatch (finfo, NULL, vers->vn_rcs)) + || wrap_merge_is_copy (finfo->file)) { - /* For binary files, a merge is always a conflict. Same for - files whose permissions or linkage do not match. We give the + /* For binary files, a merge is always a conflict. We give the user the two files, and let them resolve it. It is possible that we should require a "touch foo" or similar step before we allow a checkin. */ - - /* TODO: it may not always be necessary to regard a permission - mismatch as a conflict. The working file and the RCS file - have a common ancestor `A'; if the working file's permissions - match A's, then it's probably safe to overwrite them with the - RCS permissions. Only if the working file, the RCS file, and - A all disagree should this be considered a conflict. But more - thought needs to go into this, and in the meantime it is safe - to treat any such mismatch as an automatic conflict. -twp */ - + status = checkout_file (finfo, vers, 0); #ifdef SERVER_SUPPORT + /* Send the new contents of the file before the message. If we + wanted to be totally correct, we would have the client write + the message only after the file has safely been written. */ if (server_active) + { server_copy_file (finfo->file, finfo->update_dir, finfo->repository, backup); + server_updated (finfo, vers, SERVER_MERGED, + (struct stat *) NULL, (unsigned char *) NULL); + } #endif - - status = checkout_file (finfo, vers, 0, 1, 1); - /* Is there a better term than "nonmergeable file"? What we really mean is, not something that CVS cannot or does not want to merge (there might be an external manual or @@ -1951,8 +1717,7 @@ merge_file (finfo, vers) server_copy_file (finfo->file, finfo->update_dir, finfo->repository, backup); server_updated (finfo, vers, SERVER_MERGED, - (mode_t) -1, (unsigned char *) NULL, - (struct buffer *) NULL); + (struct stat *) NULL, (unsigned char *) NULL); } #endif @@ -2186,8 +1951,8 @@ join_file (finfo, vers) if (server_active) { server_scratch (finfo->file); - server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1, - (unsigned char *) NULL, (struct buffer *) NULL); + server_updated (finfo, vers, SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); } #endif mrev = xmalloc (strlen (vers->vn_user) + 2); @@ -2240,7 +2005,14 @@ join_file (finfo, vers) /* FIXME: If checkout_file fails, we should arrange to return a non-zero exit status. */ - status = checkout_file (finfo, xvers, 1, 0, 1); + status = checkout_file (finfo, xvers, 1); + +#ifdef SERVER_SUPPORT + if (server_active && status == 0) + server_updated (finfo, xvers, + SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); +#endif freevers_ts (&xvers); @@ -2302,7 +2074,7 @@ join_file (finfo, vers) (char *) NULL, RUN_TTY, (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) - error (1, 0, + error (1, retcode == -1 ? errno : 0, "failed to check out %s file", finfo->fullname); } #endif @@ -2375,11 +2147,9 @@ join_file (finfo, vers) write_letter (finfo, 'U'); } else if (strcmp (options, "-kb") == 0 - || wrap_merge_is_copy (finfo->file) - || special_file_mismatch (finfo, rev1, rev2)) + || wrap_merge_is_copy (finfo->file)) { - /* We are dealing with binary files, or files with a - permission/linkage mismatch, and real merging would + /* We are dealing with binary files, but real merging would need to take place. This is a conflict. We give the user the two files, and let them resolve it. It is possible that we should require a "touch foo" or similar step before @@ -2457,318 +2227,12 @@ join_file (finfo, vers) server_copy_file (finfo->file, finfo->update_dir, finfo->repository, backup); server_updated (finfo, vers, SERVER_MERGED, - (mode_t) -1, (unsigned char *) NULL, - (struct buffer *) NULL); + (struct stat *) NULL, (unsigned char *) NULL); } #endif free (backup); } -/* - * Report whether revisions REV1 and REV2 of FINFO agree on: - * . file ownership - * . permissions - * . major and minor device numbers - * . symbolic links - * . hard links - * - * If either REV1 or REV2 is NULL, the working copy is used instead. - * - * Return 1 if the files differ on these data. - */ - -int -special_file_mismatch (finfo, rev1, rev2) - struct file_info *finfo; - char *rev1; - char *rev2; -{ -#ifdef PRESERVE_PERMISSIONS_SUPPORT - struct stat sb; - RCSVers *vp; - Node *n; - uid_t rev1_uid, rev2_uid; - gid_t rev1_gid, rev2_gid; - mode_t rev1_mode, rev2_mode; - unsigned long dev_long; - dev_t rev1_dev, rev2_dev; - char *rev1_symlink = NULL; - char *rev2_symlink = NULL; - char *rev1_hardlinks = NULL; - char *rev2_hardlinks = NULL; - int check_uids, check_gids, check_modes; - int result; - - /* If we don't care about special file info, then - don't report a mismatch in any case. */ - if (!preserve_perms) - return 0; - - /* When special_file_mismatch is called from No_Difference, the - RCS file has been only partially parsed. We must read the - delta tree in order to compare special file info recorded in - the delta nodes. (I think this is safe. -twp) */ - if (finfo->rcs->flags & PARTIAL) - RCS_reparsercsfile (finfo->rcs, NULL, NULL); - - check_uids = check_gids = check_modes = 1; - - /* Obtain file information for REV1. If this is null, then stat - finfo->file and use that info. */ - /* If a revision does not know anything about its status, - then presumably it doesn't matter, and indicates no conflict. */ - - if (rev1 == NULL) - { - if (islink (finfo->file)) - rev1_symlink = xreadlink (finfo->file); - else - { - if (CVS_LSTAT (finfo->file, &sb) < 0) - error (1, errno, "could not get file information for %s", - finfo->file); - rev1_uid = sb.st_uid; - rev1_gid = sb.st_gid; - rev1_mode = sb.st_mode; - if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode)) - rev1_dev = sb.st_rdev; - } - rev1_hardlinks = list_files_linked_to (finfo->file); - } - else - { - n = findnode (finfo->rcs->versions, rev1); - vp = (RCSVers *) n->data; - - n = findnode (vp->other_delta, "symlink"); - if (n != NULL) - rev1_symlink = xstrdup (n->data); - else - { - n = findnode (vp->other_delta, "owner"); - if (n == NULL) - check_uids = 0; /* don't care */ - else - rev1_uid = strtoul (n->data, NULL, 10); - - n = findnode (vp->other_delta, "group"); - if (n == NULL) - check_gids = 0; /* don't care */ - else - rev1_gid = strtoul (n->data, NULL, 10); - - n = findnode (vp->other_delta, "permissions"); - if (n == NULL) - check_modes = 0; /* don't care */ - else - rev1_mode = strtoul (n->data, NULL, 8); - - n = findnode (vp->other_delta, "special"); - if (n == NULL) - rev1_mode |= S_IFREG; - else - { - /* If the size of `ftype' changes, fix the sscanf call also */ - char ftype[16]; - if (sscanf (n->data, "%16s %lu", ftype, - &dev_long) < 2) - error (1, 0, "%s:%s has bad `special' newphrase %s", - finfo->file, rev1, n->data); - rev1_dev = dev_long; - if (strcmp (ftype, "character") == 0) - rev1_mode |= S_IFCHR; - else if (strcmp (ftype, "block") == 0) - rev1_mode |= S_IFBLK; - else - error (0, 0, "%s:%s unknown file type `%s'", - finfo->file, rev1, ftype); - } - - n = findnode (vp->other_delta, "hardlinks"); - if (n == NULL) - rev1_hardlinks = xstrdup (""); - else - rev1_hardlinks = xstrdup (n->data); - } - } - - /* Obtain file information for REV2. */ - if (rev2 == NULL) - { - if (islink (finfo->file)) - rev2_symlink = xreadlink (finfo->file); - else - { - if (CVS_LSTAT (finfo->file, &sb) < 0) - error (1, errno, "could not get file information for %s", - finfo->file); - rev2_uid = sb.st_uid; - rev2_gid = sb.st_gid; - rev2_mode = sb.st_mode; - if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode)) - rev2_dev = sb.st_rdev; - } - rev2_hardlinks = list_files_linked_to (finfo->file); - } - else - { - n = findnode (finfo->rcs->versions, rev2); - vp = (RCSVers *) n->data; - - n = findnode (vp->other_delta, "symlink"); - if (n != NULL) - rev2_symlink = xstrdup (n->data); - else - { - n = findnode (vp->other_delta, "owner"); - if (n == NULL) - check_uids = 0; /* don't care */ - else - rev2_uid = strtoul (n->data, NULL, 10); - - n = findnode (vp->other_delta, "group"); - if (n == NULL) - check_gids = 0; /* don't care */ - else - rev2_gid = strtoul (n->data, NULL, 10); - - n = findnode (vp->other_delta, "permissions"); - if (n == NULL) - check_modes = 0; /* don't care */ - else - rev2_mode = strtoul (n->data, NULL, 8); - - n = findnode (vp->other_delta, "special"); - if (n == NULL) - rev2_mode |= S_IFREG; - else - { - /* If the size of `ftype' changes, fix the sscanf call also */ - char ftype[16]; - if (sscanf (n->data, "%16s %lu", ftype, - &dev_long) < 2) - error (1, 0, "%s:%s has bad `special' newphrase %s", - finfo->file, rev2, n->data); - rev2_dev = dev_long; - if (strcmp (ftype, "character") == 0) - rev2_mode |= S_IFCHR; - else if (strcmp (ftype, "block") == 0) - rev2_mode |= S_IFBLK; - else - error (0, 0, "%s:%s unknown file type `%s'", - finfo->file, rev2, ftype); - } - - n = findnode (vp->other_delta, "hardlinks"); - if (n == NULL) - rev2_hardlinks = xstrdup (""); - else - rev2_hardlinks = xstrdup (n->data); - } - } - - /* Check the user/group ownerships and file permissions, printing - an error for each mismatch found. Return 0 if all characteristics - matched, and 1 otherwise. */ - - result = 0; - - /* Compare symlinks first, since symlinks are simpler (don't have - any other characteristics). */ - if (rev1_symlink != NULL && rev2_symlink == NULL) - { - error (0, 0, "%s is a symbolic link", - (rev1 == NULL ? "working file" : rev1)); - result = 1; - } - else if (rev1_symlink == NULL && rev2_symlink != NULL) - { - error (0, 0, "%s is a symbolic link", - (rev2 == NULL ? "working file" : rev2)); - result = 1; - } - else if (rev1_symlink != NULL) - result = (strcmp (rev1_symlink, rev2_symlink) == 0); - else - { - /* Compare user ownership. */ - if (check_uids && rev1_uid != rev2_uid) - { - error (0, 0, "%s: owner mismatch between %s and %s", - finfo->file, - (rev1 == NULL ? "working file" : rev1), - (rev2 == NULL ? "working file" : rev2)); - result = 1; - } - - /* Compare group ownership. */ - if (check_gids && rev1_gid != rev2_gid) - { - error (0, 0, "%s: group mismatch between %s and %s", - finfo->file, - (rev1 == NULL ? "working file" : rev1), - (rev2 == NULL ? "working file" : rev2)); - result = 1; - } - - /* Compare permissions. */ - if (check_modes && - (rev1_mode & 07777) != (rev2_mode & 07777)) - { - error (0, 0, "%s: permission mismatch between %s and %s", - finfo->file, - (rev1 == NULL ? "working file" : rev1), - (rev2 == NULL ? "working file" : rev2)); - result = 1; - } - - /* Compare device file characteristics. */ - if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT)) - { - error (0, 0, "%s: %s and %s are different file types", - finfo->file, - (rev1 == NULL ? "working file" : rev1), - (rev2 == NULL ? "working file" : rev2)); - result = 1; - } - else if (S_ISBLK (rev1_mode)) - { - if (rev1_dev != rev2_dev) - { - error (0, 0, "%s: device numbers of %s and %s do not match", - finfo->file, - (rev1 == NULL ? "working file" : rev1), - (rev2 == NULL ? "working file" : rev2)); - result = 1; - } - } - - /* Compare hard links. */ - if (strcmp (rev1_hardlinks, rev2_hardlinks) != 0) - { - error (0, 0, "%s: hard linkage of %s and %s do not match", - finfo->file, - (rev1 == NULL ? "working file" : rev1), - (rev2 == NULL ? "working file" : rev2)); - result = 1; - } - } - - if (rev1_symlink != NULL) - free (rev1_symlink); - if (rev2_symlink != NULL) - free (rev2_symlink); - if (rev1_hardlinks != NULL) - free (rev1_hardlinks); - if (rev2_hardlinks != NULL) - free (rev2_hardlinks); - - return result; -#else - return 0; -#endif -} - int joining () { |