diff options
author | eadler <eadler@FreeBSD.org> | 2013-10-07 02:23:00 +0000 |
---|---|---|
committer | eadler <eadler@FreeBSD.org> | 2013-10-07 02:23:00 +0000 |
commit | ba3f99676351684264da1513884a8aae3359a3fb (patch) | |
tree | 5fdf6b3f346166d536a79653631f395ca5f6a1fc /gnu/usr.bin/rcs/ci | |
parent | 34d055e746b4dad0722315b2096024ed9e08cd69 (diff) | |
download | FreeBSD-src-ba3f99676351684264da1513884a8aae3359a3fb.zip FreeBSD-src-ba3f99676351684264da1513884a8aae3359a3fb.tar.gz |
Good bye RCS. You will be missed.
(devel/rcs and devel/rcs57 are available as alternatives)
Approved by: core
Approved by: re (hrs)
Diffstat (limited to 'gnu/usr.bin/rcs/ci')
-rw-r--r-- | gnu/usr.bin/rcs/ci/Makefile | 8 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/ci/ci.1 | 898 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/ci/ci.c | 1318 |
3 files changed, 0 insertions, 2224 deletions
diff --git a/gnu/usr.bin/rcs/ci/Makefile b/gnu/usr.bin/rcs/ci/Makefile deleted file mode 100644 index 2fbb74f..0000000 --- a/gnu/usr.bin/rcs/ci/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -PROG= ci -SRCS= ci.c -CFLAGS+= -I${.CURDIR}/../lib -LDADD= ${LIBRCS} -DPADD= ${LIBRCS} - -.include "../../Makefile.inc" -.include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/ci/ci.1 b/gnu/usr.bin/rcs/ci/ci.1 deleted file mode 100644 index 1378af2..0000000 --- a/gnu/usr.bin/rcs/ci/ci.1 +++ /dev/null @@ -1,898 +0,0 @@ -.de Id -.ds Rv \\$3 -.ds Dt \\$4 -.. -.Id $FreeBSD$ -.ds i \&\s-1ISO\s0 -.ds r \&\s-1RCS\s0 -.ds u \&\s-1UTC\s0 -.if n .ds - \%-- -.if t .ds - \(em -.TH CI 1 \*(Dt GNU -.SH NAME -ci \- check in RCS revisions -.SH SYNOPSIS -.B ci -.RI [ options ] " file " .\|.\|. -.SH DESCRIPTION -.B ci -stores new revisions into \*r files. -Each pathname matching an \*r suffix -is taken to be an \*r file. -All others -are assumed to be working files containing new revisions. -.B ci -deposits the contents of each working file -into the corresponding \*r file. -If only a working file is given, -.B ci -tries to find the corresponding \*r file in an \*r subdirectory -and then in the working file's directory. -For more details, see -.SM "FILE NAMING" -below. -.PP -For -.B ci -to work, the caller's login must be on the access list, -except if the access list is empty or the caller is the superuser or the -owner of the file. -To append a new revision to an existing branch, the tip revision on -that branch must be locked by the caller. Otherwise, only a -new branch can be created. This restriction is not enforced -for the owner of the file if non-strict locking is used -(see -.BR rcs (1)). -A lock held by someone else can be broken with the -.B rcs -command. -.PP -Unless the -.B \-f -option is given, -.B ci -checks whether the revision to be deposited differs from the preceding one. -If not, instead of creating a new revision -.B ci -reverts to the preceding one. -To revert, ordinary -.B ci -removes the working file and any lock; -.B "ci\ \-l" -keeps and -.B "ci\ \-u" -removes any lock, and then they both generate a new working file much as if -.B "co\ \-l" -or -.B "co\ \-u" -had been applied to the preceding revision. -When reverting, any -.B \-n -and -.B \-s -options apply to the preceding revision. -.PP -For each revision deposited, -.B ci -prompts for a log message. -The log message should summarize the change and must be terminated by -end-of-file or by a line containing -.BR \&. "\ by" -itself. -If several files are checked in -.B ci -asks whether to reuse the -previous log message. -If the standard input is not a terminal, -.B ci -suppresses the prompt -and uses the same log message for all files. -See also -.BR \-m . -.PP -If the \*r file does not exist, -.B ci -creates it and -deposits the contents of the working file as the initial revision -(default number: -.BR 1.1 ). -The access list is initialized to empty. -Instead of the log message, -.B ci -requests descriptive text (see -.B \-t -below). -.PP -The number -.I rev -of the deposited revision can be given by any of the options -.BR \-f , -.BR \-i , -.BR \-I , -.BR \-j , -.BR \-k , -.BR \-l , -.BR \-M , -.BR \-q , -.BR \-r , -or -.BR \-u . -.I rev -can be symbolic, numeric, or mixed. -Symbolic names in -.I rev -must already be defined; -see the -.B \-n -and -.B \-N -options for assigning names during checkin. -If -.I rev -is -.BR $ , -.B ci -determines the revision number from keyword values in the working file. -.PP -If -.I rev -begins with a period, -then the default branch (normally the trunk) is prepended to it. -If -.I rev -is a branch number followed by a period, -then the latest revision on that branch is used. -.PP -If -.I rev -is a revision number, it must be higher than the latest -one on the branch to which -.I rev -belongs, or must start a new branch. -.PP -If -.I rev -is a branch rather than a revision number, -the new revision is appended to that branch. The level number is obtained -by incrementing the tip revision number of that branch. -If -.I rev -indicates a non-existing branch, -that branch is created with the initial revision numbered -.IB rev .1\f1.\fP -.br -.ne 8 -.PP -If -.I rev -is omitted, -.B ci -tries to derive the new revision number from -the caller's last lock. If the caller has locked the tip revision of a branch, -the new revision is appended to that branch. -The new revision number is obtained -by incrementing the tip revision number. -If the caller locked a non-tip revision, a new branch is started at -that revision by incrementing the highest branch number at that revision. -The default initial branch and level numbers are -.BR 1 . -.PP -If -.I rev -is omitted and the caller has no lock, but owns -the file and locking -is not set to -.IR strict , -then the revision is appended to the -default branch (normally the trunk; see the -.B \-b -option of -.BR rcs (1)). -.PP -Exception: On the trunk, revisions can be appended to the end, but -not inserted. -.SH OPTIONS -.TP -.BI \-r rev -Check in revision -.IR rev . -.TP -.BR \-r -The bare -.B \-r -option (without any revision) has an unusual meaning in -.BR ci . -With other \*r commands, a bare -.B \-r -option specifies the most recent revision on the default branch, -but with -.BR ci , -a bare -.B \-r -option reestablishes the default behavior of releasing a lock and -removing the working file, and is used to override any default -.B \-l -or -.B \-u -options established by shell aliases or scripts. -.TP -.BR \-l [\f2rev\fP] -works like -.BR \-r , -except it performs an additional -.B "co\ \-l" -for the -deposited revision. Thus, the deposited revision is immediately -checked out again and locked. -This is useful for saving a revision although one wants to continue -editing it after the checkin. -.TP -.BR \-u [\f2rev\fP] -works like -.BR \-l , -except that the deposited revision is not locked. -This lets one read the working file -immediately after checkin. -.RS -.PP -The -.BR \-l , -bare -.BR \-r , -and -.B \-u -options are mutually exclusive and silently override each other. -For example, -.B "ci\ \-u\ \-r" -is equivalent to -.B "ci\ \-r" -because bare -.B \-r -overrides -.BR \-u . -.RE -.TP -.BR \-f [\f2rev\fP] -forces a deposit; the new revision is deposited even it is not different -from the preceding one. -.TP -.BR \-k [\f2rev\fP] -searches the working file for keyword values to determine its revision number, -creation date, state, and author (see -.BR co (1)), -and assigns these -values to the deposited revision, rather than computing them locally. -It also generates a default login message noting the login of the caller -and the actual checkin date. -This option is useful for software distribution. A revision that is sent to -several sites should be checked in with the -.B \-k -option at these sites to -preserve the original number, date, author, and state. -The extracted keyword values and the default log message can be overridden -with the options -.BR \-d , -.BR \-m , -.BR \-s , -.BR \-w , -and any option that carries a revision number. -.TP -.BR \-q [\f2rev\fP] -quiet mode; diagnostic output is not printed. -A revision that is not different from the preceding one is not deposited, -unless -.B \-f -is given. -.TP -.BR \-i [\f2rev\fP] -initial checkin; report an error if the \*r file already exists. -This avoids race conditions in certain applications. -.TP -.BR \-j [\f2rev\fP] -just checkin and do not initialize; -report an error if the \*r file does not already exist. -.TP -.BR \-I [\f2rev\fP] -interactive mode; -the user is prompted and questioned -even if the standard input is not a terminal. -.TP -.BR \-d "[\f2date\fP]" -uses -.I date -for the checkin date and time. -The -.I date -is specified in free format as explained in -.BR co (1). -This is useful for lying about the checkin date, and for -.B \-k -if no date is available. -If -.I date -is empty, the working file's time of last modification is used. -.TP -.BR \-M [\f2rev\fP] -Set the modification time on any new working file -to be the date of the retrieved revision. -For example, -.BI "ci\ \-d\ \-M\ \-u" "\ f" -does not alter -.IR f 's -modification time, even if -.IR f 's -contents change due to keyword substitution. -Use this option with care; it can confuse -.BR make (1). -.TP -.BI \-m "msg" -uses the string -.I msg -as the log message for all revisions checked in. -By convention, log messages that start with -.B # -are comments and are ignored by programs like GNU Emacs's -.B vc -package. -Also, log messages that start with -.BI { clumpname } -(followed by white space) are meant to be clumped together if possible, -even if they are associated with different files; the -.BI { clumpname } -label is used only for clumping, -and is not considered to be part of the log message itself. -.TP -.BI \-n "name" -assigns the symbolic name -.I name -to the number of the checked-in revision. -.B ci -prints an error message if -.I name -is already assigned to another -number. -.TP -.BI \-N "name" -same as -.BR \-n , -except that it overrides a previous assignment of -.IR name . -.TP -.BI \-s "state" -sets the state of the checked-in revision to the identifier -.IR state . -The default state is -.BR Exp . -.TP -.BI \-t file -writes descriptive text from the contents of the named -.I file -into the \*r file, -deleting the existing text. -The -.I file -cannot begin with -.BR \- . -.TP -.BI \-t\- string -Write descriptive text from the -.I string -into the \*r file, deleting the existing text. -.RS -.PP -The -.B \-t -option, in both its forms, has effect only during an initial checkin; -it is silently ignored otherwise. -.PP -During the initial checkin, if -.B \-t -is not given, -.B ci -obtains the text from standard input, -terminated by end-of-file or by a line containing -.BR \&. "\ by" -itself. -The user is prompted for the text if interaction is possible; see -.BR \-I . -.PP -For backward compatibility with older versions of \*r, a bare -.B \-t -option is ignored. -.RE -.TP -.B \-T -Set the \*r file's modification time to the new revision's time -if the former precedes the latter and there is a new revision; -preserve the \*r file's modification time otherwise. -If you have locked a revision, -.B ci -usually updates the \*r file's modification time to the current time, -because the lock is stored in the \*r file -and removing the lock requires changing the \*r file. -This can create an \*r file newer than the working file in one of two ways: -first, -.B "ci\ \-M" -can create a working file with a date before the current time; -second, when reverting to the previous revision -the \*r file can change while the working file remains unchanged. -These two cases can cause excessive recompilation caused by a -.BR make (1) -dependency of the working file on the \*r file. -The -.B \-T -option inhibits this recompilation by lying about the \*r file's date. -Use this option with care; it can suppress recompilation even when -a checkin of one working file should affect -another working file associated with the same \*r file. -For example, suppose the \*r file's time is 01:00, -the (changed) working file's time is 02:00, -some other copy of the working file has a time of 03:00, -and the current time is 04:00. -Then -.B "ci\ \-d\ \-T" -sets the \*r file's time to 02:00 instead of the usual 04:00; -this causes -.BR make (1) -to think (incorrectly) that the other copy is newer than the \*r file. -.TP -.BI \-w "login" -uses -.I login -for the author field of the deposited revision. -Useful for lying about the author, and for -.B \-k -if no author is available. -.TP -.BI \-V -Print \*r's version number. -.TP -.BI \-V n -Emulate \*r version -.IR n . -See -.BR co (1) -for details. -.TP -.BI \-x "suffixes" -specifies the suffixes for \*r files. -A nonempty suffix matches any pathname ending in the suffix. -An empty suffix matches any pathname of the form -.BI RCS/ path -or -.IB path1 /RCS/ path2. -The -.B \-x -option can specify a list of suffixes -separated by -.BR / . -For example, -.B \-x,v/ -specifies two suffixes: -.B ,v -and the empty suffix. -If two or more suffixes are specified, -they are tried in order when looking for an \*r file; -the first one that works is used for that file. -If no \*r file is found but an \*r file can be created, -the suffixes are tried in order -to determine the new \*r file's name. -The default for -.IR suffixes -is installation-dependent; normally it is -.B ,v/ -for hosts like Unix that permit commas in filenames, -and is empty (i.e. just the empty suffix) for other hosts. -.TP -.BI \-z zone -specifies the date output format in keyword substitution, -and specifies the default time zone for -.I date -in the -.BI \-d date -option. -The -.I zone -should be empty, a numeric \*u offset, or the special string -.B LT -for local time. -The default is an empty -.IR zone , -which uses the traditional \*r format of \*u without any time zone indication -and with slashes separating the parts of the date; -otherwise, times are output in \*i 8601 format with time zone indication. -For example, if local time is January 11, 1990, 8pm Pacific Standard Time, -eight hours west of \*u, -then the time is output as follows: -.RS -.LP -.RS -.nf -.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u -.ne 4 -\f2option\fP \f2time output\fP -\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP -\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP -\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP -.ta 4n +4n +4n +4n -.fi -.RE -.LP -The -.B \-z -option does not affect dates stored in \*r files, -which are always \*u. -.SH "FILE NAMING" -Pairs of \*r files and working files can be specified in three ways -(see also the -example section). -.PP -1) Both the \*r file and the working file are given. The \*r pathname is of -the form -.IB path1 / workfileX -and the working pathname is of the form -.IB path2 / workfile -where -.IB path1 / -and -.IB path2 / -are (possibly different or empty) paths, -.I workfile -is a filename, and -.I X -is an \*r suffix. -If -.I X -is empty, -.IB path1 / -must start with -.B RCS/ -or must contain -.BR /RCS/ . -.PP -2) Only the \*r file is given. Then the working file is created in the current -directory and its name is derived from the name of the \*r file -by removing -.IB path1 / -and the suffix -.IR X . -.PP -3) Only the working file is given. -Then -.B ci -considers each \*r suffix -.I X -in turn, looking for an \*r file of the form -.IB path2 /RCS/ workfileX -or (if the former is not found and -.I X -is nonempty) -.IB path2 / workfileX. -.PP -If the \*r file is specified without a path in 1) and 2), -.B ci -looks for the \*r file first in the directory -.B ./RCS -and then in the current -directory. -.PP -.B ci -reports an error if an attempt to open an \*r file fails for an unusual reason, -even if the \*r file's pathname is just one of several possibilities. -For example, to suppress use of \*r commands in a directory -.IR d , -create a regular file named -.IB d /RCS -so that casual attempts to use \*r commands in -.I d -fail because -.IB d /RCS -is not a directory. -.SH EXAMPLES -Suppose -.B ,v -is an \*r suffix and the current directory contains a subdirectory -.B RCS -with an \*r file -.BR io.c,v . -Then each of the following commands check in a copy of -.B io.c -into -.B RCS/io.c,v -as the latest revision, removing -.BR io.c . -.LP -.RS -.nf -.ft 3 -ci io.c; ci RCS/io.c,v; ci io.c,v; -ci io.c RCS/io.c,v; ci io.c io.c,v; -ci RCS/io.c,v io.c; ci io.c,v io.c; -.ft -.fi -.RE -.PP -Suppose instead that the empty suffix -is an \*r suffix and the current directory contains a subdirectory -.B RCS -with an \*r file -.BR io.c . -The each of the following commands checks in a new revision. -.LP -.RS -.nf -.ft 3 -ci io.c; ci RCS/io.c; -ci io.c RCS/io.c; -ci RCS/io.c io.c; -.ft -.fi -.RE -.SH "FILE MODES" -An \*r file created by -.B ci -inherits the read and execute permissions -from the working file. If the \*r file exists already, -.B ci -preserves its read and execute permissions. -.B ci -always turns off all write permissions of \*r files. -.SH FILES -Temporary files are created in the directory containing -the working file, and also in the temporary directory (see -.B \s-1TMPDIR\s0 -under -.BR \s-1ENVIRONMENT\s0 ). -A semaphore file or files are created in the directory containing the \*r file. -With a nonempty suffix, the semaphore names begin with -the first character of the suffix; therefore, do not specify an suffix -whose first character could be that of a working filename. -With an empty suffix, the semaphore names end with -.B _ -so working filenames should not end in -.BR _ . -.PP -.B ci -never changes an \*r or working file. -Normally, -.B ci -unlinks the file and creates a new one; -but instead of breaking a chain of one or more symbolic links to an \*r file, -it unlinks the destination file instead. -Therefore, -.B ci -breaks any hard or symbolic links to any working file it changes; -and hard links to \*r files are ineffective, -but symbolic links to \*r files are preserved. -.PP -The effective user must be able to -search and write the directory containing the \*r file. -Normally, the real user must be able to -read the \*r and working files -and to search and write the directory containing the working file; -however, some older hosts -cannot easily switch between real and effective users, -so on these hosts the effective user is used for all accesses. -The effective user is the same as the real user -unless your copies of -.B ci -and -.B co -have setuid privileges. -As described in the next section, -these privileges yield extra security if -the effective user owns all \*r files and directories, -and if only the effective user can write \*r directories. -.PP -Users can control access to \*r files by setting the permissions -of the directory containing the files; only users with write access -to the directory can use \*r commands to change its \*r files. -For example, in hosts that allow a user to belong to several groups, -one can make a group's \*r directories writable to that group only. -This approach suffices for informal projects, -but it means that any group member can arbitrarily change the group's \*r files, -and can even remove them entirely. -Hence more formal projects sometimes distinguish between an \*r administrator, -who can change the \*r files at will, and other project members, -who can check in new revisions but cannot otherwise change the \*r files. -.SH "SETUID USE" -To prevent anybody but their \*r administrator from deleting revisions, -a set of users can employ setuid privileges as follows. -.nr n \w'\(bu'+2n-1/1n -.ds n \nn -.if \n(.g .if r an-tag-sep .ds n \w'\(bu'u+\n[an-tag-sep]u -.IP \(bu \*n -Check that the host supports \*r setuid use. -Consult a trustworthy expert if there are any doubts. -It is best if the -.B seteuid -system call works as described in Posix 1003.1a Draft 5, -because \*r can switch back and forth easily -between real and effective users, even if the real user is -.BR root . -If not, the second best is if the -.B setuid -system call supports saved setuid -(the {\s-1_POSIX_SAVED_IDS\s0} behavior of Posix 1003.1-1990); -this fails only if the real or effective user is -.BR root . -If \*r detects any failure in setuid, it quits immediately. -.IP \(bu \nn -Choose a user -.I A -to serve as \*r administrator for the set of users. -Only -.I A -can invoke the -.B rcs -command on the users' \*r files. -.I A -should not be -.B root -or any other user with special powers. -Mutually suspicious sets of users should use different administrators. -.IP \(bu \nn -Choose a pathname -.I B -to be a directory of files to be executed by the users. -.IP \(bu \nn -Have -.I A -set up -.I B -to contain copies of -.B ci -and -.B co -that are setuid to -.I A -by copying the commands from their standard installation directory -.I D -as follows: -.LP -.RS -.nf -.ne 3 -\f3mkdir\fP \f2B\fP -\f3cp\fP \f2D\fP\^\f3/c[io]\fP \f2B\fP -\f3chmod go\-w,u+s\fP \f2B\fP\f3/c[io]\fP -.fi -.RE -.IP \(bu \nn -Have each user prepend -.I B -to their path as follows: -.LP -.RS -.nf -.ne 2 -\f3PATH=\fP\f2B\fP\f3:$PATH; export PATH\fP # ordinary shell -\f3set path=(\fP\f2B\fP \f3$path)\fP # C shell -.fi -.RE -.IP \(bu \nn -Have -.I A -create each \*r directory -.I R -with write access only to -.I A -as follows: -.LP -.RS -.nf -.ne 2 -\f3mkdir\fP \f2R\fP -\f3chmod go\-w\fP \f2R\fP -.fi -.RE -.IP \(bu \nn -If you want to let only certain users read the \*r files, -put the users into a group -.IR G , -and have -.I A -further protect the \*r directory as follows: -.LP -.RS -.nf -.ne 2 -\f3chgrp\fP \f2G R\fP -\f3chmod g\-w,o\-rwx\fP \f2R\fP -.fi -.RE -.IP \(bu \nn -Have -.I A -copy old \*r files (if any) into -.IR R , -to ensure that -.I A -owns them. -.IP \(bu \nn -An \*r file's access list limits who can check in and lock revisions. -The default access list is empty, -which grants checkin access to anyone who can read the \*r file. -If you want limit checkin access, -have -.I A -invoke -.B "rcs\ \-a" -on the file; see -.BR rcs (1). -In particular, -.BI "rcs\ \-e\ \-a" A -limits access to just -.IR A . -.IP \(bu \nn -Have -.I A -initialize any new \*r files with -.B "rcs\ \-i" -before initial checkin, adding the -.B \-a -option if you want to limit checkin access. -.IP \(bu \nn -Give setuid privileges only to -.BR ci , -.BR co , -and -.BR rcsclean ; -do not give them to -.B rcs -or to any other command. -.IP \(bu \nn -Do not use other setuid commands to invoke \*r commands; -setuid is trickier than you think! -.SH ENVIRONMENT -.TP -.B \s-1RCSINIT\s0 -options prepended to the argument list, separated by spaces. -A backslash escapes spaces within an option. -The -.B \s-1RCSINIT\s0 -options are prepended to the argument lists of most \*r commands. -Useful -.B \s-1RCSINIT\s0 -options include -.BR \-q , -.BR \-V , -.BR \-x , -and -.BR \-z . -.TP -.B \s-1TMPDIR\s0 -Name of the temporary directory. -If not set, the environment variables -.B \s-1TMP\s0 -and -.B \s-1TEMP\s0 -are inspected instead and the first value found is taken; -if none of them are set, -a host-dependent default is used, typically -.BR /tmp . -.SH DIAGNOSTICS -For each revision, -.B ci -prints the \*r file, the working file, and the number -of both the deposited and the preceding revision. -The exit status is zero if and only if all operations were successful. -.SH IDENTIFICATION -Author: Walter F. Tichy. -.br -Manual Page Revision: \*(Rv; Release Date: \*(Dt. -.br -Copyright \(co 1982, 1988, 1989 Walter F. Tichy. -.br -Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. -.SH "SEE ALSO" -co(1), -ident(1), make(1), rcs(1), rcsclean(1), rcsdiff(1), -rcsintro(1), rcsmerge(1), rlog(1), setuid(2), rcsfile(5) -.br -Walter F. Tichy, -\*r\*-A System for Version Control, -.I "Software\*-Practice & Experience" -.BR 15 , -7 (July 1985), 637-654. -.br diff --git a/gnu/usr.bin/rcs/ci/ci.c b/gnu/usr.bin/rcs/ci/ci.c deleted file mode 100644 index 749a4cf..0000000 --- a/gnu/usr.bin/rcs/ci/ci.c +++ /dev/null @@ -1,1318 +0,0 @@ -/* Check in revisions of RCS files from working files. */ - -/* Copyright 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert - Distributed under license by the Free Software Foundation, Inc. - -This file is part of RCS. - -RCS 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. - -RCS 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 RCS; see the file COPYING. -If not, write to the Free Software Foundation, -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -Report problems and direct all questions to: - - rcs-bugs@cs.purdue.edu - -*/ - -/* - * Revision 5.30 1995/06/16 06:19:24 eggert - * Update FSF address. - * - * Revision 5.29 1995/06/01 16:23:43 eggert - * (main): Add -kb. - * Use `cmpdate', not `cmpnum', to compare dates. - * This is for MKS RCS's incompatible 20th-century date format. - * Don't worry about errno after ftruncate fails. - * Fix input file rewinding bug when large_memory && !maps_memory - * and checking in a branch tip. - * - * (fixwork): Fall back on chmod if fchmod fails, since it might be ENOSYS. - * - * Revision 5.28 1994/03/20 04:52:58 eggert - * Do not generate a corrupted RCS file if the user modifies the working file - * while `ci' is running. - * Do not remove the lock when `ci -l' reverts. - * Move buffer-flushes out of critical sections, since they aren't critical. - * Use ORCSerror to clean up after a fatal error. - * Specify subprocess input via file descriptor, not file name. - * - * Revision 5.27 1993/11/09 17:40:15 eggert - * -V now prints version on stdout and exits. Don't print usage twice. - * - * Revision 5.26 1993/11/03 17:42:27 eggert - * Add -z. Don't subtract from RCS file timestamp even if -T. - * Scan for and use Name keyword if -k. - * Don't discard ignored phrases. Improve quality of diagnostics. - * - * Revision 5.25 1992/07/28 16:12:44 eggert - * Add -i, -j, -V. Check that working and RCS files are distinct. - * - * Revision 5.24 1992/02/17 23:02:06 eggert - * `-rREV' now just specifies a revision REV; only bare `-r' reverts to default. - * Add -T. - * - * Revision 5.23 1992/01/27 16:42:51 eggert - * Always unlock branchpoint if caller has a lock. - * Add support for bad_chmod_close, bad_creat0. lint -> RCS_lint - * - * Revision 5.22 1992/01/06 02:42:34 eggert - * Invoke utime() before chmod() to keep some buggy systems happy. - * - * Revision 5.21 1991/11/20 17:58:07 eggert - * Don't read the delta tree from a nonexistent RCS file. - * - * Revision 5.20 1991/10/07 17:32:46 eggert - * Fix log bugs. Remove lint. - * - * Revision 5.19 1991/09/26 23:10:30 eggert - * Plug file descriptor leak. - * - * Revision 5.18 1991/09/18 07:29:10 eggert - * Work around a common ftruncate() bug. - * - * Revision 5.17 1991/09/10 22:15:46 eggert - * Fix test for redirected stdin. - * - * Revision 5.16 1991/08/19 23:17:54 eggert - * When there are no changes, revert to previous revision instead of aborting. - * Add piece tables, -M, -r$. Tune. - * - * Revision 5.15 1991/04/21 11:58:14 eggert - * Ensure that working file is newer than RCS file after ci -[lu]. - * Add -x, RCSINIT, MS-DOS support. - * - * Revision 5.14 1991/02/28 19:18:47 eggert - * Don't let a setuid ci create a new RCS file; rcs -i -a must be run first. - * Fix ci -ko -l mode bug. Open work file at most once. - * - * Revision 5.13 1991/02/25 07:12:33 eggert - * getdate -> getcurdate (SVR4 name clash) - * - * Revision 5.12 1990/12/31 01:00:12 eggert - * Don't use uninitialized storage when handling -{N,n}. - * - * Revision 5.11 1990/12/04 05:18:36 eggert - * Use -I for prompts and -q for diagnostics. - * - * Revision 5.10 1990/11/05 20:30:10 eggert - * Don't remove working file when aborting due to no changes. - * - * Revision 5.9 1990/11/01 05:03:23 eggert - * Add -I and new -t behavior. Permit arbitrary data in logs. - * - * Revision 5.8 1990/10/04 06:30:09 eggert - * Accumulate exit status across files. - * - * Revision 5.7 1990/09/25 20:11:46 hammer - * fixed another small typo - * - * Revision 5.6 1990/09/24 21:48:50 hammer - * added cleanups from Paul Eggert. - * - * Revision 5.5 1990/09/21 06:16:38 hammer - * made it handle multiple -{N,n}'s. Also, made it treat re-directed stdin - * the same as the terminal - * - * Revision 5.4 1990/09/20 02:38:51 eggert - * ci -k now checks dates more thoroughly. - * - * Revision 5.3 1990/09/11 02:41:07 eggert - * Fix revision bug with `ci -k file1 file2'. - * - * Revision 5.2 1990/09/04 08:02:10 eggert - * Permit adjacent revisions with identical time stamps (possible on fast hosts). - * Improve incomplete line handling. Standardize yes-or-no procedure. - * - * Revision 5.1 1990/08/29 07:13:44 eggert - * Expand locker value like co. Clean old log messages too. - * - * Revision 5.0 1990/08/22 08:10:00 eggert - * Don't require a final newline. - * Make lock and temp files faster and safer. - * Remove compile-time limits; use malloc instead. - * Permit dates past 1999/12/31. Switch to GMT. - * Add setuid support. Don't pass +args to diff. Check diff's output. - * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. - * Check diff's output. - * - * Revision 4.9 89/05/01 15:10:54 narten - * changed copyright header to reflect current distribution rules - * - * Revision 4.8 88/11/08 13:38:23 narten - * changes from root@seismo.CSS.GOV (Super User) - * -d with no arguments uses the mod time of the file it is checking in - * - * Revision 4.7 88/08/09 19:12:07 eggert - * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one. - * Use execv(), not system(); allow cc -R; remove lint. - * isatty(fileno(stdin)) -> ttystdin() - * - * Revision 4.6 87/12/18 11:34:41 narten - * lint cleanups (from Guy Harris) - * - * Revision 4.5 87/10/18 10:18:48 narten - * Updating version numbers. Changes relative to revision 1.1 are actually - * relative to 4.3 - * - * Revision 1.3 87/09/24 13:57:19 narten - * Sources now pass through lint (if you ignore printf/sprintf/fprintf - * warnings) - * - * Revision 1.2 87/03/27 14:21:33 jenkins - * Port to suns - * - * Revision 4.3 83/12/15 12:28:54 wft - * ci -u and ci -l now set mode of working file properly. - * - * Revision 4.2 83/12/05 13:40:54 wft - * Merged with 3.9.1.1: added calls to clearerr(stdin). - * made rewriteflag external. - * - * Revision 4.1 83/05/10 17:03:06 wft - * Added option -d and -w, and updated assingment of date, etc. to new delta. - * Added handling of default branches. - * Option -k generates std. log message; fixed undef. pointer in reading of log. - * Replaced getlock() with findlock(), link--unlink with rename(), - * getpwuid() with getcaller(). - * Moved all revision number generation to new routine addelta(). - * Removed calls to stat(); now done by pairfilenames(). - * Changed most calls to catchints() with restoreints(). - * Directed all interactive messages to stderr. - * - * Revision 3.9.1.1 83/10/19 04:21:03 lepreau - * Added clearerr(stdin) to getlogmsg() for re-reading stdin. - * - * Revision 3.9 83/02/15 15:25:44 wft - * 4.2 prerelease - * - * Revision 3.9 83/02/15 15:25:44 wft - * Added call to fastcopy() to copy remainder of RCS file. - * - * Revision 3.8 83/01/14 15:34:05 wft - * Added ignoring of interrupts while new RCS file is renamed; - * Avoids deletion of RCS files by interrupts. - * - * Revision 3.7 82/12/10 16:09:20 wft - * Corrected checking of return code from diff. - * - * Revision 3.6 82/12/08 21:34:49 wft - * Using DATEFORM to prepare date of checked-in revision; - * Fixed return from addbranch(). - * - * Revision 3.5 82/12/04 18:32:42 wft - * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated - * field lockedby in removelock(), moved getlogmsg() before calling diff. - * - * Revision 3.4 82/12/02 13:27:13 wft - * added option -k. - * - * Revision 3.3 82/11/28 20:53:31 wft - * Added mustcheckin() to check for redundant checkins. - * Added xpandfile() to do keyword expansion for -u and -l; - * -m appends linefeed to log message if necessary. - * getlogmsg() suppresses prompt if stdin is not a terminal. - * Replaced keeplock with lockflag, fclose() with ffclose(), - * %02d with %.2d, getlogin() with getpwuid(). - * - * Revision 3.2 82/10/18 20:57:23 wft - * An RCS file inherits its mode during the first ci from the working file, - * otherwise it stays the same, except that write permission is removed. - * Fixed ci -l, added ci -u (both do an implicit co after the ci). - * Fixed call to getlogin(), added call to getfullRCSname(), added check - * for write error. - * Changed conflicting identifiers. - * - * Revision 3.1 82/10/13 16:04:59 wft - * fixed type of variables receiving from getc() (char -> int). - * added include file dbm.h for getting BYTESIZ. This is used - * to check the return code from diff portably. - */ - -#include "rcsbase.h" - -struct Symrev { - char const *ssymbol; - int override; - struct Symrev * nextsym; -}; - -static char const *getcurdate P((void)); -static int addbranch P((struct hshentry*,struct buf*,int)); -static int addelta P((void)); -static int addsyms P((char const*)); -static int fixwork P((mode_t,time_t)); -static int removelock P((struct hshentry*)); -static int xpandfile P((RILE*,struct hshentry const*,char const**,int)); -static struct cbuf getlogmsg P((void)); -static void cleanup P((void)); -static void incnum P((char const*,struct buf*)); -static void addassoclst P((int,char const*)); - -static FILE *exfile; -static RILE *workptr; /* working file pointer */ -static struct buf newdelnum; /* new revision number */ -static struct cbuf msg; -static int exitstatus; -static int forceciflag; /* forces check in */ -static int keepflag, keepworkingfile, rcsinitflag; -static struct hshentries *gendeltas; /* deltas to be generated */ -static struct hshentry *targetdelta; /* old delta to be generated */ -static struct hshentry newdelta; /* new delta to be inserted */ -static struct stat workstat; -static struct Symrev *assoclst, **nextassoc; - -mainProg(ciId, "ci", "$FreeBSD$") -{ - static char const cmdusage[] = - "\nci usage: ci -{fIklMqru}[rev] -d[date] -mmsg -{nN}name -sstate -ttext -T -Vn -wwho -xsuff -zzone file ..."; - static char const default_state[] = DEFAULTSTATE; - - char altdate[datesize]; - char olddate[datesize]; - char newdatebuf[datesize + zonelenmax]; - char targetdatebuf[datesize + zonelenmax]; - char *a, **newargv, *textfile; - char const *author, *krev, *rev, *state; - char const *diffname, *expname; - char const *newworkname; - int initflag, mustread; - int lockflag, lockthis, mtimeflag, removedlock, Ttimeflag; - int r; - int changedRCS, changework, dolog, newhead; - int usestatdate; /* Use mod time of file for -d. */ - mode_t newworkmode; /* mode for working file */ - time_t mtime, wtime; - struct hshentry *workdelta; - - setrid(); - - author = rev = state = textfile = 0; - initflag = lockflag = mustread = false; - mtimeflag = false; - Ttimeflag = false; - altdate[0]= '\0'; /* empty alternate date for -d */ - usestatdate=false; - suffixes = X_DEFAULT; - nextassoc = &assoclst; - - argc = getRCSINIT(argc, argv, &newargv); - argv = newargv; - while (a = *++argv, 0<--argc && *a++=='-') { - switch (*a++) { - - case 'r': - if (*a) - goto revno; - keepworkingfile = lockflag = false; - break; - - case 'l': - keepworkingfile = lockflag = true; - revno: - if (*a) { - if (rev) warn("redefinition of revision number"); - rev = a; - } - break; - - case 'u': - keepworkingfile=true; lockflag=false; - goto revno; - - case 'i': - initflag = true; - goto revno; - - case 'j': - mustread = true; - goto revno; - - case 'I': - interactiveflag = true; - goto revno; - - case 'q': - quietflag=true; - goto revno; - - case 'f': - forceciflag=true; - goto revno; - - case 'k': - keepflag=true; - goto revno; - - case 'm': - if (msg.size) redefined('m'); - msg = cleanlogmsg(a, strlen(a)); - if (!msg.size) - error("missing message for -m option"); - break; - - case 'n': - if (!*a) { - error("missing symbolic name after -n"); - break; - } - checkssym(a); - addassoclst(false, a); - break; - - case 'N': - if (!*a) { - error("missing symbolic name after -N"); - break; - } - checkssym(a); - addassoclst(true, a); - break; - - case 's': - if (*a) { - if (state) redefined('s'); - checksid(a); - state = a; - } else - error("missing state for -s option"); - break; - - case 't': - if (*a) { - if (textfile) redefined('t'); - textfile = a; - } - break; - - case 'd': - if (altdate[0] || usestatdate) - redefined('d'); - altdate[0] = '\0'; - if (!(usestatdate = !*a)) - str2date(a, altdate); - break; - - case 'M': - mtimeflag = true; - goto revno; - - case 'w': - if (*a) { - if (author) redefined('w'); - checksid(a); - author = a; - } else - error("missing author for -w option"); - break; - - case 'x': - suffixes = a; - break; - - case 'V': - setRCSversion(*argv); - break; - - case 'z': - zone_set(a); - break; - - case 'T': - if (!*a) { - Ttimeflag = true; - break; - } - /* fall into */ - default: - error("unknown option: %s%s", *argv, cmdusage); - }; - } /* end processing of options */ - - /* Handle all pathnames. */ - if (nerror) cleanup(); - else if (argc < 1) faterror("no input file%s", cmdusage); - else for (; 0 < argc; cleanup(), ++argv, --argc) { - targetdelta = 0; - ffree(); - - switch (pairnames(argc, argv, rcswriteopen, mustread, false)) { - - case -1: /* New RCS file */ -# if has_setuid && has_getuid - if (euid() != ruid()) { - workerror("setuid initial checkin prohibited; use `rcs -i -a' first"); - continue; - } -# endif - rcsinitflag = true; - break; - - case 0: /* Error */ - continue; - - case 1: /* Normal checkin with prev . RCS file */ - if (initflag) { - rcserror("already exists"); - continue; - } - rcsinitflag = !Head; - } - - /* - * RCSname contains the name of the RCS file, and - * workname contains the name of the working file. - * If the RCS file exists, finptr contains the file descriptor for the - * RCS file, and RCSstat is set. The admin node is initialized. - */ - - diagnose("%s <-- %s\n", RCSname, workname); - - if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) { - eerror(workname); - continue; - } - - if (finptr) { - if (same_file(RCSstat, workstat, 0)) { - rcserror("RCS file is the same as working file %s.", - workname - ); - continue; - } - if (!checkaccesslist()) - continue; - } - - krev = rev; - if (keepflag) { - /* get keyword values from working file */ - if (!getoldkeys(workptr)) continue; - if (!rev && !*(krev = prevrev.string)) { - workerror("can't find a revision number"); - continue; - } - if (!*prevdate.string && *altdate=='\0' && usestatdate==false) - workwarn("can't find a date"); - if (!*prevauthor.string && !author) - workwarn("can't find an author"); - if (!*prevstate.string && !state) - workwarn("can't find a state"); - } /* end processing keepflag */ - - /* Read the delta tree. */ - if (finptr) - gettree(); - - /* expand symbolic revision number */ - if (!fexpandsym(krev, &newdelnum, workptr)) - continue; - - /* splice new delta into tree */ - if ((removedlock = addelta()) < 0) - continue; - - newdelta.num = newdelnum.string; - newdelta.branches = 0; - newdelta.lockedby = 0; /* This might be changed by addlock(). */ - newdelta.selector = true; - newdelta.name = 0; - clear_buf(&newdelta.ig); - clear_buf(&newdelta.igtext); - /* set author */ - if (author) - newdelta.author=author; /* set author given by -w */ - else if (keepflag && *prevauthor.string) - newdelta.author=prevauthor.string; /* preserve old author if possible*/ - else newdelta.author=getcaller();/* otherwise use caller's id */ - newdelta.state = default_state; - if (state) - newdelta.state=state; /* set state given by -s */ - else if (keepflag && *prevstate.string) - newdelta.state=prevstate.string; /* preserve old state if possible */ - if (usestatdate) { - time2date(workstat.st_mtime, altdate); - } - if (*altdate!='\0') - newdelta.date=altdate; /* set date given by -d */ - else if (keepflag && *prevdate.string) { - /* Preserve old date if possible. */ - str2date(prevdate.string, olddate); - newdelta.date = olddate; - } else - newdelta.date = getcurdate(); /* use current date */ - /* now check validity of date -- needed because of -d and -k */ - if (targetdelta && - cmpdate(newdelta.date,targetdelta->date) < 0) { - rcserror("Date %s precedes %s in revision %s.", - date2str(newdelta.date, newdatebuf), - date2str(targetdelta->date, targetdatebuf), - targetdelta->num - ); - continue; - } - - - if (lockflag && addlock(&newdelta, true) < 0) continue; - - if (keepflag && *prevname.string) - if (addsymbol(newdelta.num, prevname.string, false) < 0) - continue; - if (!addsyms(newdelta.num)) - continue; - - - putadmin(); - puttree(Head,frewrite); - putdesc(false,textfile); - - changework = Expand < MIN_UNCHANGED_EXPAND; - dolog = true; - lockthis = lockflag; - workdelta = &newdelta; - - /* build rest of file */ - if (rcsinitflag) { - diagnose("initial revision: %s\n", newdelta.num); - /* get logmessage */ - newdelta.log=getlogmsg(); - putdftext(&newdelta, workptr, frewrite, false); - RCSstat.st_mode = workstat.st_mode; - RCSstat.st_nlink = 0; - changedRCS = true; - } else { - diffname = maketemp(0); - newhead = Head == &newdelta; - if (!newhead) - foutptr = frewrite; - expname = buildrevision( - gendeltas, targetdelta, (FILE*)0, false - ); - if ( - !forceciflag && - strcmp(newdelta.state, targetdelta->state) == 0 && - (changework = rcsfcmp( - workptr, &workstat, expname, targetdelta - )) <= 0 - ) { - diagnose("file is unchanged; reverting to previous revision %s\n", - targetdelta->num - ); - if (removedlock < lockflag) { - diagnose("previous revision was not locked; ignoring -l option\n"); - lockthis = 0; - } - dolog = false; - if (! (changedRCS = lockflag<removedlock || assoclst)) - workdelta = targetdelta; - else { - /* - * We have started to build the wrong new RCS file. - * Start over from the beginning. - */ - long hwm = ftell(frewrite); - int bad_truncate; - Orewind(frewrite); - - /* - * Work around a common ftruncate() bug: - * NFS won't let you truncate a file that you - * currently lack permissions for, even if you - * had permissions when you opened it. - * Also, Posix 1003.1b-1993 sec 5.6.7.2 p 128 l 1022 - * says ftruncate might fail because it's not supported. - */ -# if !has_ftruncate -# undef ftruncate -# define ftruncate(fd,length) (-1) -# endif - bad_truncate = ftruncate(fileno(frewrite), (off_t)0); - - Irewind(finptr); - Lexinit(); - getadmin(); - gettree(); - if (!(workdelta = genrevs( - targetdelta->num, (char*)0, (char*)0, (char*)0, - &gendeltas - ))) - continue; - workdelta->log = targetdelta->log; - if (newdelta.state != default_state) - workdelta->state = newdelta.state; - if (lockthis<removedlock && removelock(workdelta)<0) - continue; - if (!addsyms(workdelta->num)) - continue; - if (dorewrite(true, true) != 0) - continue; - fastcopy(finptr, frewrite); - if (bad_truncate) - while (ftell(frewrite) < hwm) - /* White out any earlier mistake with '\n's. */ - /* This is unlikely. */ - afputc('\n', frewrite); - } - } else { - int wfd = Ifileno(workptr); - struct stat checkworkstat; - char const *diffv[6 + !!OPEN_O_BINARY], **diffp; -# if large_memory && !maps_memory - FILE *wfile = workptr->stream; - long wfile_off; -# endif -# if !has_fflush_input && !(large_memory && maps_memory) - off_t wfd_off; -# endif - - diagnose("new revision: %s; previous revision: %s\n", - newdelta.num, targetdelta->num - ); - newdelta.log = getlogmsg(); -# if !large_memory - Irewind(workptr); -# if has_fflush_input - if (fflush(workptr) != 0) - Ierror(); -# endif -# else -# if !maps_memory - if ( - (wfile_off = ftell(wfile)) == -1 - || fseek(wfile, 0L, SEEK_SET) != 0 -# if has_fflush_input - || fflush(wfile) != 0 -# endif - ) - Ierror(); -# endif -# endif -# if !has_fflush_input && !(large_memory && maps_memory) - wfd_off = lseek(wfd, (off_t)0, SEEK_CUR); - if (wfd_off == -1 - || (wfd_off != 0 - && lseek(wfd, (off_t)0, SEEK_SET) != 0)) - Ierror(); -# endif - diffp = diffv; - *++diffp = DIFF; - *++diffp = DIFFFLAGS; -# if OPEN_O_BINARY - if (Expand == BINARY_EXPAND) - *++diffp = "--binary"; -# endif - *++diffp = newhead ? "-" : expname; - *++diffp = newhead ? expname : "-"; - *++diffp = 0; - switch (runv(wfd, diffname, diffv)) { - case DIFF_FAILURE: case DIFF_SUCCESS: break; - default: rcsfaterror("diff failed"); - } -# if !has_fflush_input && !(large_memory && maps_memory) - if (lseek(wfd, wfd_off, SEEK_CUR) == -1) - Ierror(); -# endif -# if large_memory && !maps_memory - if (fseek(wfile, wfile_off, SEEK_SET) != 0) - Ierror(); -# endif - if (newhead) { - Irewind(workptr); - putdftext(&newdelta, workptr, frewrite, false); - if (!putdtext(targetdelta,diffname,frewrite,true)) continue; - } else - if (!putdtext(&newdelta,diffname,frewrite,true)) continue; - - /* - * Check whether the working file changed during checkin, - * to avoid producing an inconsistent RCS file. - */ - if ( - fstat(wfd, &checkworkstat) != 0 - || workstat.st_mtime != checkworkstat.st_mtime - || workstat.st_size != checkworkstat.st_size - ) { - workerror("file changed during checkin"); - continue; - } - - changedRCS = true; - } - } - - /* Deduce time_t of new revision if it is needed later. */ - wtime = (time_t)-1; - if (mtimeflag | Ttimeflag) - wtime = date2time(workdelta->date); - - if (donerewrite(changedRCS, - !Ttimeflag ? (time_t)-1 - : finptr && wtime < RCSstat.st_mtime ? RCSstat.st_mtime - : wtime - ) != 0) - continue; - - if (!keepworkingfile) { - Izclose(&workptr); - r = un_link(workname); /* Get rid of old file */ - } else { - newworkmode = WORKMODE(RCSstat.st_mode, - ! (Expand==VAL_EXPAND || lockthis < StrictLocks) - ); - mtime = mtimeflag ? wtime : (time_t)-1; - - /* Expand if it might change or if we can't fix mode, time. */ - if (changework || (r=fixwork(newworkmode,mtime)) != 0) { - Irewind(workptr); - /* Expand keywords in file. */ - locker_expansion = lockthis; - workdelta->name = - namedrev( - assoclst ? assoclst->ssymbol - : keepflag && *prevname.string ? prevname.string - : rev, - workdelta - ); - switch (xpandfile( - workptr, workdelta, &newworkname, dolog - )) { - default: - continue; - - case 0: - /* - * No expansion occurred; try to reuse working file - * unless we already tried and failed. - */ - if (changework) - if ((r=fixwork(newworkmode,mtime)) == 0) - break; - /* fall into */ - case 1: - Izclose(&workptr); - aflush(exfile); - ignoreints(); - r = chnamemod(&exfile, newworkname, - workname, 1, newworkmode, mtime - ); - keepdirtemp(newworkname); - restoreints(); - } - } - } - if (r != 0) { - eerror(workname); - continue; - } - diagnose("done\n"); - - } - - tempunlink(); - exitmain(exitstatus); -} /* end of main (ci) */ - - static void -cleanup() -{ - if (nerror) exitstatus = EXIT_FAILURE; - Izclose(&finptr); - Izclose(&workptr); - Ozclose(&exfile); - Ozclose(&fcopy); - ORCSclose(); - dirtempunlink(); -} - -#if RCS_lint -# define exiterr ciExit -#endif - void -exiterr() -{ - ORCSerror(); - dirtempunlink(); - tempunlink(); - _exit(EXIT_FAILURE); -} - -/*****************************************************************/ -/* the rest are auxiliary routines */ - - - static int -addelta() -/* Function: Appends a delta to the delta tree, whose number is - * given by newdelnum. Updates Head, newdelnum, newdelnumlength, - * and the links in newdelta. - * Return -1 on error, 1 if a lock is removed, 0 otherwise. - */ -{ - register char *tp; - register int i; - int removedlock; - int newdnumlength; /* actual length of new rev. num. */ - - newdnumlength = countnumflds(newdelnum.string); - - if (rcsinitflag) { - /* this covers non-existing RCS file and a file initialized with rcs -i */ - if (newdnumlength==0 && Dbranch) { - bufscpy(&newdelnum, Dbranch); - newdnumlength = countnumflds(Dbranch); - } - if (newdnumlength==0) bufscpy(&newdelnum, "1.1"); - else if (newdnumlength==1) bufscat(&newdelnum, ".1"); - else if (newdnumlength>2) { - rcserror("Branch point doesn't exist for revision %s.", - newdelnum.string - ); - return -1; - } /* newdnumlength == 2 is OK; */ - Head = &newdelta; - newdelta.next = 0; - return 0; - } - if (newdnumlength==0) { - /* derive new revision number from locks */ - switch (findlock(true, &targetdelta)) { - - default: - /* found two or more old locks */ - return -1; - - case 1: - /* found an old lock */ - /* check whether locked revision exists */ - if (!genrevs(targetdelta->num,(char*)0,(char*)0,(char*)0,&gendeltas)) - return -1; - if (targetdelta==Head) { - /* make new head */ - newdelta.next=Head; - Head= &newdelta; - } else if (!targetdelta->next && countnumflds(targetdelta->num)>2) { - /* new tip revision on side branch */ - targetdelta->next= &newdelta; - newdelta.next = 0; - } else { - /* middle revision; start a new branch */ - bufscpy(&newdelnum, ""); - return addbranch(targetdelta, &newdelnum, 1); - } - incnum(targetdelta->num, &newdelnum); - return 1; /* successful use of existing lock */ - - case 0: - /* no existing lock; try Dbranch */ - /* update newdelnum */ - if (StrictLocks || !myself(RCSstat.st_uid)) { - rcserror("no lock set by %s", getcaller()); - return -1; - } - if (Dbranch) { - bufscpy(&newdelnum, Dbranch); - } else { - incnum(Head->num, &newdelnum); - } - newdnumlength = countnumflds(newdelnum.string); - /* now fall into next statement */ - } - } - if (newdnumlength<=2) { - /* add new head per given number */ - if(newdnumlength==1) { - /* make a two-field number out of it*/ - if (cmpnumfld(newdelnum.string,Head->num,1)==0) - incnum(Head->num, &newdelnum); - else - bufscat(&newdelnum, ".1"); - } - if (cmpnum(newdelnum.string,Head->num) <= 0) { - rcserror("revision %s too low; must be higher than %s", - newdelnum.string, Head->num - ); - return -1; - } - targetdelta = Head; - if (0 <= (removedlock = removelock(Head))) { - if (!genrevs(Head->num,(char*)0,(char*)0,(char*)0,&gendeltas)) - return -1; - newdelta.next = Head; - Head = &newdelta; - } - return removedlock; - } else { - /* put new revision on side branch */ - /*first, get branch point */ - tp = newdelnum.string; - for (i = newdnumlength - ((newdnumlength&1) ^ 1); --i; ) - while (*tp++ != '.') - continue; - *--tp = 0; /* Kill final dot to get old delta temporarily. */ - if (!(targetdelta=genrevs(newdelnum.string,(char*)0,(char*)0,(char*)0,&gendeltas))) - return -1; - if (cmpnum(targetdelta->num, newdelnum.string) != 0) { - rcserror("can't find branch point %s", newdelnum.string); - return -1; - } - *tp = '.'; /* Restore final dot. */ - return addbranch(targetdelta, &newdelnum, 0); - } -} - - - - static int -addbranch(branchpoint, num, removedlock) - struct hshentry *branchpoint; - struct buf *num; - int removedlock; -/* adds a new branch and branch delta at branchpoint. - * If num is the null string, appends the new branch, incrementing - * the highest branch number (initially 1), and setting the level number to 1. - * the new delta and branchhead are in globals newdelta and newbranch, resp. - * the new number is placed into num. - * Return -1 on error, 1 if a lock is removed, 0 otherwise. - * If REMOVEDLOCK is 1, a lock was already removed. - */ -{ - struct branchhead *bhead, **btrail; - struct buf branchnum; - int result; - int field, numlength; - static struct branchhead newbranch; /* new branch to be inserted */ - - numlength = countnumflds(num->string); - - if (!branchpoint->branches) { - /* start first branch */ - branchpoint->branches = &newbranch; - if (numlength==0) { - bufscpy(num, branchpoint->num); - bufscat(num, ".1.1"); - } else if (numlength&1) - bufscat(num, ".1"); - newbranch.nextbranch = 0; - - } else if (numlength==0) { - /* append new branch to the end */ - bhead=branchpoint->branches; - while (bhead->nextbranch) bhead=bhead->nextbranch; - bhead->nextbranch = &newbranch; - bufautobegin(&branchnum); - getbranchno(bhead->hsh->num, &branchnum); - incnum(branchnum.string, num); - bufautoend(&branchnum); - bufscat(num, ".1"); - newbranch.nextbranch = 0; - } else { - /* place the branch properly */ - field = numlength - ((numlength&1) ^ 1); - /* field of branch number */ - btrail = &branchpoint->branches; - while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) { - btrail = &(*btrail)->nextbranch; - if (!*btrail) { - result = -1; - break; - } - } - if (result < 0) { - /* insert/append new branchhead */ - newbranch.nextbranch = *btrail; - *btrail = &newbranch; - if (numlength&1) bufscat(num, ".1"); - } else { - /* branch exists; append to end */ - bufautobegin(&branchnum); - getbranchno(num->string, &branchnum); - targetdelta = genrevs( - branchnum.string, (char*)0, (char*)0, (char*)0, - &gendeltas - ); - bufautoend(&branchnum); - if (!targetdelta) - return -1; - if (cmpnum(num->string,targetdelta->num) <= 0) { - rcserror("revision %s too low; must be higher than %s", - num->string, targetdelta->num - ); - return -1; - } - if (!removedlock - && 0 <= (removedlock = removelock(targetdelta)) - ) { - if (numlength&1) - incnum(targetdelta->num,num); - targetdelta->next = &newdelta; - newdelta.next = 0; - } - return removedlock; - /* Don't do anything to newbranch. */ - } - } - newbranch.hsh = &newdelta; - newdelta.next = 0; - if (branchpoint->lockedby) - if (strcmp(branchpoint->lockedby, getcaller()) == 0) - return removelock(branchpoint); /* This returns 1. */ - return removedlock; -} - - static int -addsyms(num) - char const *num; -{ - register struct Symrev *p; - - for (p = assoclst; p; p = p->nextsym) - if (addsymbol(num, p->ssymbol, p->override) < 0) - return false; - return true; -} - - - static void -incnum(onum,nnum) - char const *onum; - struct buf *nnum; -/* Increment the last field of revision number onum by one and - * place the result into nnum. - */ -{ - register char *tp, *np; - register size_t l; - - l = strlen(onum); - bufalloc(nnum, l+2); - np = tp = nnum->string; - VOID strcpy(np, onum); - for (tp = np + l; np != tp; ) - if (isdigit(*--tp)) { - if (*tp != '9') { - ++*tp; - return; - } - *tp = '0'; - } else { - tp++; - break; - } - /* We changed 999 to 000; now change it to 1000. */ - *tp = '1'; - tp = np + l; - *tp++ = '0'; - *tp = 0; -} - - - - static int -removelock(delta) -struct hshentry * delta; -/* function: Finds the lock held by caller on delta, - * removes it, and returns nonzero if successful. - * Print an error message and return -1 if there is no such lock. - * An exception is if !StrictLocks, and caller is the owner of - * the RCS file. If caller does not have a lock in this case, - * return 0; return 1 if a lock is actually removed. - */ -{ - register struct rcslock *next, **trail; - char const *num; - - num=delta->num; - for (trail = &Locks; (next = *trail); trail = &next->nextlock) - if (next->delta == delta) - if (strcmp(getcaller(), next->login) == 0) { - /* We found a lock on delta by caller; delete it. */ - *trail = next->nextlock; - delta->lockedby = 0; - return 1; - } else { - rcserror("revision %s locked by %s", num, next->login); - return -1; - } - if (!StrictLocks && myself(RCSstat.st_uid)) - return 0; - rcserror("no lock set by %s for revision %s", getcaller(), num); - return -1; -} - - - - static char const * -getcurdate() -/* Return a pointer to the current date. */ -{ - static char buffer[datesize]; /* date buffer */ - - if (!buffer[0]) - time2date(now(), buffer); - return buffer; -} - - static int -#if has_prototypes -fixwork(mode_t newworkmode, time_t mtime) - /* The `#if has_prototypes' is needed because mode_t might promote to int. */ -#else - fixwork(newworkmode, mtime) - mode_t newworkmode; - time_t mtime; -#endif -{ - return - 1 < workstat.st_nlink - || (newworkmode&S_IWUSR && !myself(workstat.st_uid)) - || setmtime(workname, mtime) != 0 - ? -1 - : workstat.st_mode == newworkmode ? 0 -#if has_fchmod - : fchmod(Ifileno(workptr), newworkmode) == 0 ? 0 -#endif -#if bad_chmod_close - : -1 -#else - : chmod(workname, newworkmode) -#endif - ; -} - - static int -xpandfile(unexfile, delta, exname, dolog) - RILE *unexfile; - struct hshentry const *delta; - char const **exname; - int dolog; -/* - * Read unexfile and copy it to a - * file, performing keyword substitution with data from delta. - * Return -1 if unsuccessful, 1 if expansion occurred, 0 otherwise. - * If successful, stores the stream descriptor into *EXFILEP - * and its name into *EXNAME. - */ -{ - char const *targetname; - int e, r; - - targetname = makedirtemp(1); - if (!(exfile = fopenSafer(targetname, FOPEN_W_WORK))) { - eerror(targetname); - workerror("can't build working file"); - return -1; - } - r = 0; - if (MIN_UNEXPAND <= Expand) - fastcopy(unexfile,exfile); - else { - for (;;) { - e = expandline( - unexfile, exfile, delta, false, (FILE*)0, dolog - ); - if (e < 0) - break; - r |= e; - if (e <= 1) - break; - } - } - *exname = targetname; - return r & 1; -} - - - - -/* --------------------- G E T L O G M S G --------------------------------*/ - - - static struct cbuf -getlogmsg() -/* Obtain and yield a log message. - * If a log message is given with -m, yield that message. - * If this is the initial revision, yield a standard log message. - * Otherwise, reads a character string from the terminal. - * Stops after reading EOF or a single '.' on a - * line. getlogmsg prompts the first time it is called for the - * log message; during all later calls it asks whether the previous - * log message can be reused. - */ -{ - static char const - emptych[] = EMPTYLOG, - initialch[] = "Initial revision"; - static struct cbuf const - emptylog = { emptych, sizeof(emptych)-sizeof(char) }, - initiallog = { initialch, sizeof(initialch)-sizeof(char) }; - static struct buf logbuf; - static struct cbuf logmsg; - - register char *tp; - register size_t i; - char const *caller; - - if (msg.size) return msg; - - if (keepflag) { - /* generate std. log message */ - caller = getcaller(); - i = sizeof(ciklog)+strlen(caller)+3; - bufalloc(&logbuf, i + datesize + zonelenmax); - tp = logbuf.string; - VOID sprintf(tp, "%s%s at ", ciklog, caller); - VOID date2str(getcurdate(), tp+i); - logmsg.string = tp; - logmsg.size = strlen(tp); - return logmsg; - } - - if (!targetdelta && ( - cmpnum(newdelnum.string,"1.1")==0 || - cmpnum(newdelnum.string,"1.0")==0 - )) - return initiallog; - - if (logmsg.size) { - /*previous log available*/ - if (yesorno(true, "reuse log message of previous file? [yn](y): ")) - return logmsg; - } - - /* now read string from stdin */ - logmsg = getsstdin("m", "log message", "", &logbuf); - - /* now check whether the log message is not empty */ - if (logmsg.size) - return logmsg; - return emptylog; -} - -/* Make a linked list of Symbolic names */ - - static void -addassoclst(flag, sp) - int flag; - char const *sp; -{ - struct Symrev *pt; - - pt = talloc(struct Symrev); - pt->ssymbol = sp; - pt->override = flag; - pt->nextsym = 0; - *nextassoc = pt; - nextassoc = &pt->nextsym; -} |