From 4d7f45e2595cdda6921d9ed62f7ce9ba08453d13 Mon Sep 17 00:00:00 2001 From: bde Date: Sun, 6 Jul 1997 06:54:14 +0000 Subject: Finish importing Lite2's src/usr.bin, except for ex, diff, grep, mail, pascal and vmstat.sparc. All changed files on the vendor branch should already have been imported. --- usr.bin/sccs/Makefile | 5 + usr.bin/sccs/PSD.doc/Makefile | 7 + usr.bin/sccs/PSD.doc/sccs.me | 1609 ++++++++++++++++++++++++++++++++++++++++ usr.bin/sccs/PSD.doc/spell.ok | 77 ++ usr.bin/sccs/pathnames.h | 51 ++ usr.bin/sccs/sccs.1 | 398 ++++++++++ usr.bin/sccs/sccs.c | 1621 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 3768 insertions(+) create mode 100644 usr.bin/sccs/Makefile create mode 100644 usr.bin/sccs/PSD.doc/Makefile create mode 100644 usr.bin/sccs/PSD.doc/sccs.me create mode 100644 usr.bin/sccs/PSD.doc/spell.ok create mode 100644 usr.bin/sccs/pathnames.h create mode 100644 usr.bin/sccs/sccs.1 create mode 100644 usr.bin/sccs/sccs.c (limited to 'usr.bin/sccs') diff --git a/usr.bin/sccs/Makefile b/usr.bin/sccs/Makefile new file mode 100644 index 0000000..0ee9d1b --- /dev/null +++ b/usr.bin/sccs/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= sccs + +.include diff --git a/usr.bin/sccs/PSD.doc/Makefile b/usr.bin/sccs/PSD.doc/Makefile new file mode 100644 index 0000000..4e8ebba --- /dev/null +++ b/usr.bin/sccs/PSD.doc/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.2 (Berkeley) 5/23/94 + +DIR= psd/14.sccs +SRCS= sccs.me +MACROS= -me + +.include diff --git a/usr.bin/sccs/PSD.doc/sccs.me b/usr.bin/sccs/PSD.doc/sccs.me new file mode 100644 index 0000000..16dc3fb --- /dev/null +++ b/usr.bin/sccs/PSD.doc/sccs.me @@ -0,0 +1,1609 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sccs.me 8.2 (Berkeley) 6/1/94 +.\" +.eh '\fRPSD:14-%\fP''\fRAn Introduction to the Source Code Control System\fP' +.oh '\fRAn Introduction to the Source Code Control System\fP''\fRPSD:14-%\fP' +.ds S \s-1SCCS\s0 +.ds I \s-1SID\s0 +.nr bi 8n +.ev 1 \" only for keeps +.ss 16 +.ev +.\".he '\*S Introduction''%' +.+c +.(l C +.sz 14 +.b +An Introduction to the +Source Code Control System +.sz +.r +.sp +Eric Allman +.i "Project Ingres" +.i "University of California at Berkeley" +.)l +.sp 3 +.pp +.(f +This is version 1.21 of this document. +It was last modified on 12/5/80. +.)f +This document gives a quick introduction +to using the Source Code Control System +(\*S). +The presentation is geared to programmers +who are more concerned with +what +to do to get a task done +rather than how it works; +for this reason some of the examples +are not well explained. +For details of what the magic options do, +see the section on +.q "Further Information" . +.(l F +This is a working document. +Please send any comments or suggestions +to eric@Berkeley.Edu. +.)l +.sh 1 "Introduction" +.pp +\*S is a source management system. +Such a system maintains a record of versions of a system; +a record is kept with each set of changes +of what the changes are, +why they were made, +and who made them and when. +Old versions can be recovered, +and different versions can be maintained simultaneously. +In projects with more than one person, +\*S will insure that two people are not +editing the same file at the same time. +.pp +All versions of your program, +plus the log and other information, +is kept in a file called the +.q "s-file" . +There are three major operations +that can be performed on the s-file: +.np +Get a file for compilation (not for editing). +This operation retrieves a version of the file +from the s-file. +By default, the latest version is retrieved. +This file is intended for compilation, printing, or whatever; +it is specifically NOT intended to be edited +or changed in any way; +any changes made to a file retrieved +in this way will probably be lost. +.np +Get a file for editing. +This operation also retrieves a version of the file +from the s-file, +but this file is intended to be edited and then +incorporated back into the s-file. +Only one person may be editing a file at one time. +.np +Merge a file back into the s-file. +This is the companion operation to (2). +A new version number is assigned, +and comments are saved explaining why this change was made. +.sh 1 "Learning the Lingo" +.pp +There are a number of terms that are worth learning +before we go any farther. +.sh 2 "S-file" +.pp +The s-file +is a single file that holds all the different versions +of your file. +The s-file is stored in +differential format; +.i i.e. , +only the differences between versions are stored, +rather than the entire text of the new version. +This saves disk space +and allows selective changes to be removed later. +Also included in the s-file +is some header information for each version, +including the comments given by the person who +created the version explaining why the changes were made. +.sh 2 "Deltas" +.pp +Each set of changes to the s-file +(which is approximately [but not exactly!] equivalent +to a version of the file) +is called a +.i delta . +Although technically a delta only includes the +.i changes +made, +in practice +it is usual for +each delta to be made with respect to +all the deltas that have occurred before\**. +.(f +\**This matches normal usage, where the previous changes are not saved +at all, +so all changes are automatically based on all other changes +that have happened through history. +.)f +However, +it is possible to get a version of the file +that has selected deltas removed out of the middle +of the list of changes \*- +equivalent to removing your changes later. +.sh 2 "\*I's (or, version numbers)" +.pp +A \*I +(\*S Id) +is a number that represents a delta. +This is normally a two-part number +consisting of a +.q release +number and a +.q level +number. +Normally the release number stays the same, +however, +it is possible to move into a new release +if some major change is being made. +.pp +Since all past deltas are normally applied, +the \*I of the final delta applied +can be used to represent a version number of the file +as a whole. +.sh 2 "Id keywords" +.pp +When you get a version of a file +with intent to compile and install it +(\c +.i i.e. , +something other than edit it), +some special keywords are expanded inline +by \*S. +These +.i "Id Keywords" +can be used to include the current version number +or other information into the file. +All id keywords are of the form +.b % \c +.i x \c +.b % , +where +.i x +is an upper case letter. +For example, +.b %\&I\&% +is the \*I of the latest delta applied, +.b %\&W\&% +includes the module name, +\*I, +and a mark that makes it findable by a program, +and +.b %\&G\&% +is the date of the latest delta applied. +There are many others, +most of which are of dubious usefulness. +.pp +When you get a file for editing, +the id keywords are not expanded; +this is so that after you put them back in to the s-file, +they will be expanded automatically on each new version. +But notice: if you were to get them +expanded accidently, +then your file would appear to be the same version +forever more, +which would of course defeat the purpose. +Also, +if you should install a version of the program +without expanding the id keywords, +it will be impossible to tell what version it is +(since all it will have is +.q %\&W\&% +or whatever). +.sh 1 "Creating \*S Files" +.pp +To put source files +into +\*S +format, run the following shell script from csh: +.(b +mkdir SCCS save +foreach i (*.[ch]) + sccs admin \-i$i $i + mv $i save/$i +end +.)b +This will put the named files +into s-files +in the subdirectory +.q SCCS +The files will be removed from the current directory +and hidden away in the directory +.q save , +so the next thing you will probably want to do +is to get all the files +(described below). +When you are convinced that +\*S has correctly created the s-files, +you should remove the directory +.q save . +.pp +If you want to have id keywords in the files, +it is best to put them in before you create the s-files. +If you do not, +.i admin +will print +.q "No Id Keywords (cm7)" , +which is a warning message only. +.sh 1 "Getting Files for Compilation" +.pp +To get a copy of the latest version +of a file, +run +.(b +sccs get prog.c +.)b +\*S will respond: +.(b +1.1 +87 lines +.)b +meaning that version 1.1 was retrieved\** +.(f +\**Actually, +the \*I of the final delta applied was 1.1. +.)f +and that it has 87 lines. +The file +.i prog.c +will be created +in the current directory. +The file will be read-only +to remind you that you are not +supposed to change it. +.pp +This copy of the file +should not be changed, +since \*S is unable +to merge the changes +back into the s-file. +If you do make changes, +they will be lost the next time +someone does a +.i get . +.sh 1 "Changing Files (or, Creating Deltas)" +.sh 2 "Getting a copy to edit" +.pp +To edit a source file, +you must first get it, +requesting permission to edit it\**: +.(f +\**The +.q "edit" +command is equivalent to using the \-e +flag to +.i "get" , +as: +.(l +sccs get \-e prog.c +.)l +Keep this in mind when reading other documentation. +.)f +.(b +sccs edit prog.c +.)b +The response will be the same as with +.i get +except that it will also say: +.(b +New delta 1.2 +.)b +You then edit it, +using a standard text editor: +.(b +vi prog.c +.)b +.sh 2 "Merging the changes back into the s-file" +.pp +When the desired changes are made, +you can put your changes into the +\*S +file using the +.i delta +command: +.(b +sccs delta prog.c +.)b +.pp +Delta will prompt you for +.q "comments?" +before it merges the changes in. +At this prompt you should type a one-line description +of what the changes mean +(more lines can be entered by ending each line +except the last with a backslash\**). +.(f +\**Yes, this is a stupid default. +.)f +.i Delta +will then type: +.(b +1.2 +5 inserted +3 deleted +84 unchanged +.)b +saying that delta 1.2 was created, +and it inserted five lines, +removed three lines, +and left 84 lines unchanged\**. +.(f +\**Changes to a line are counted as a line deleted +and a line inserted. +.)f +The +.i prog.c +file will be removed; +it can be retrieved +using +.i get . +.sh 2 "When to make deltas" +.pp +It is probably unwise to make a delta +before every recompilation or test; +otherwise, +you tend to get a lot of deltas with comments like +.q "fixed compilation problem in previous delta" +or +.q "fixed botch in 1.3" . +However, +it is very important to delta everything +before installing a module for general use. +A good technique is to edit the files you need, +make all necessary changes and tests, +compiling and editing as often as necessary +without making deltas. +When you are satisfied that you have a working version, +delta everything being edited, +re-get them, +and recompile everything. +.sh 2 "What's going on: the info command" +.pp +To find out what files where being edited, +you can use: +.(b +sccs info +.)b +to print out all the files being edited +and other information such as the name of the user +who did the edit. +Also, +the command: +.(b +sccs check +.)b +is nearly equivalent to the +.i info +command, +except that it is silent if nothing is being edited, +and returns non-zero exit status if anything is being edited; +it can be used in an +.q install +entry in a makefile +to abort the install +if anything has not been properly deltaed. +.pp +If you know that everything being edited should be deltaed, +you can use: +.(b +sccs delta \`sccs tell\` +.)b +The +.i tell +command is similar to +.i info +except that only the names of files being edited +are output, +one per line. +.pp +All of these commands take a +.b \-b +flag +to ignore +.q branches +(alternate versions, described later) +and the +.b \-u +flag to only give files being edited by you. +The +.b \-u +flag takes an optional +.i user +argument, +giving only files being edited by that user. +For example, +.(b +sccs info \-ujohn +.)b +gives a listing of files being edited by john. +.sh 2 "ID keywords" +.pp +Id keywords can be inserted into your file +that will be expanded automatically by +.i get . +For example, +a line such as: +.(b +static char SccsId[] = "%\&W\&%\et%\&G\&%"; +.)b +will be replaced with something like: +.(b +static char SccsId[] = "@\&(#)prog.c 1.2 08/29/80"; +.)b +This tells you +the name and version +of the source file +and the time the delta was created. +The string +.q "@\&(#)" +is a special string +which signals the beginning +of an +\*S +Id keyword. +.sh 3 "The what command" +.pp +To find out what version of a program +is being run, +use: +.(b +sccs what prog.c /usr/bin/prog +.)b +which will print all strings +it finds that +begin with +.q "@\&(#)" . +This works on all types of files, +including binaries and libraries. +For example, the above command will output something like: +.(b +prog.c: + prog.c 1.2 08/29/80 +/usr/bin/prog: + prog.c 1.1 02/05/79 +.)b +From this I can see +that the source that I have in prog.c +will not compile into the same version +as the binary in /usr/bin/prog. +.sh 3 "Where to put id keywords" +.pp +ID keywords can be inserted anywhere, +including in comments, +but +Id Keywords that are compiled into the object module +are especially useful, +since it lets you find out what version of +the object is being run, +as well as the source. +However, +there is a cost: +data space is used up to store +the keywords, +and on small address space machines +this may be prohibitive. +.pp +When you put id keywords into header files, +it is important that you assign them to different variables. +For example, you might use: +.(b +static char AccessSid[] = "%\&W\&% %\&G\&%"; +.)b +in the file +.i access.h +and: +.(b +static char OpsysSid[] = "%\&W\&% %\&G\&%"; +.)b +in the file +.i opsys.h . +Otherwise, +you will get compilation errors because +.q SccsId +is redefined. +The problem with this is that if the header file +is included by many modules that are loaded together, +the version number of that header file is included +in the object module many times; +you may find it more to your taste +to put id keywords in header files +in comments. +.sh 2 "Keeping \*I's consistent across files" +.pp +With some care, +it is possible to keep the \*I's consistent +in multi-file systems. +The trick here is to always +.i edit +all files +at once. +The changes can then be made +to whatever files are necessary +and then all files +(even those not changed) +are redeltaed. +This can be done fairly easily +by just specifying the name of the directory +that the \*S files are in: +.(b +sccs edit SCCS +.)b +which will +.i edit +all files in that directory. +To make the delta, use: +.(b +sccs delta SCCS +.)b +You will be prompted for comments only once. +.sh 2 "Creating new releases" +.pp +When you want to create a new release +of a program, +you can specify the release number you want to create +on the +.i edit +command. +For example: +.(b +sccs edit \-r2 prog.c +.)b +will cause the next delta to be in release two +(that is, +it will be numbered 2.1). +Future deltas will automatically be in release two. +To change the release number +of an entire system, +use: +.(b +sccs edit \-r2 SCCS +.)b +.sh 1 "Restoring Old Versions" +.sh 2 "Reverting to old versions" +.pp +Suppose that after delta 1.2 +was stable +you made and released a delta 1.3. +But this introduced a bug, +so you made a delta 1.4 to correct it. +But 1.4 was still buggy, +and you decided you wanted to go back +to the old version. +You could +revert to delta 1.2 +by choosing the \*I in a get: +.(b +sccs get \-r1.2 prog.c +.)b +This will produce a version of +.i prog.c +that is delta 1.2 +that can be reinstalled so that work can proceed. +.pp +In some cases you don't know +what the \*I of the delta you want is. +However, +you can revert to the version of the program +that was running as of a certain date +by using the +.b \-c +(cutoff) flag. +For example, +.(b +sccs get \-c800722120000 prog.c +.)b +will retrieve whatever version was current +as of July 22, 1980 +at 12:00 noon. +Trailing components can be stripped off +(defaulting to their highest legal value), +and punctuation can be inserted in the obvious +places; +for example, +the above line could be equivalently stated: +.(b +sccs get \-c"80/07/22 12:00:00" prog.c +.)b +.sh 2 "Selectively deleting old deltas" +.pp +Suppose that you later decided +that you liked the changes in delta 1.4, +but that delta 1.3 should be removed. +You could do this by +.i excluding +delta 1.3: +.(b +sccs edit \-x1.3 prog.c +.)b +.ne 1i +When delta 1.5 is made, +it will include the changes made +in delta 1.4, +but will exclude the changes made +in delta 1.3. +You can exclude a range of deltas +using a dash. +For example, +if you want to get rid of 1.3 and 1.4 +you can use: +.(b +sccs edit \-x1.3\-1.4 prog.c +.)b +which will exclude all deltas from 1.3 to 1.4. +Alternatively, +.(b +sccs edit \-x1.3\-1 prog.c +.)b +will exclude a range of deltas +from 1.3 to the current highest delta in release 1. +.pp +In certain cases when using +.b \-x +(or +.b \-i ; +see below) +there will be conflicts +between versions; +for example, it may be necessary +to both include and delete +a particular line. +If this happens, +\*S always prints out a message +telling the range of lines effected; +these lines should then be examined very carefully +to see if the version \*S got +is ok. +.pp +Since each delta +(in the sense of +.q "a set of changes" ) +can be excluded at will, +that this makes it most useful +to put each semantically distinct change +into its own delta. +.sh 1 "Auditing Changes" +.sh 2 "The prt command" +.pp +When you created a delta, +you presumably gave a reason for the delta +to the +.q "comments?" +prompt. +To print out these comments later, +use: +.(b +sccs prt prog.c +.)b +This will produce +a report +for each delta +of the \*I, +time and date of creation, +user who created the delta, +number of lines inserted, deleted, and unchanged, +and the comments associated with the delta. +For example, the output of the above command might be: +.(b +D 1.2 80/08/29 12:35:31 bill 2 1 00005/00003/00084 +removed "-q" option +.sp \n(psu +D 1.1 79/02/05 00:19:31 eric 1 0 00087/00000/00000 +date and time created 80/06/10 00:19:31 by eric +.)b +.sh 2 "Finding why lines were inserted" +.pp +To find out +why you inserted lines, +you can get a copy of the file +with each line +preceded by the \*I that created it: +.(b +sccs get \-m prog.c +.)b +You can then find out +what this delta did +by printing the comments using +.i prt . +.pp +To find out what lines are associated with a particular delta +(\c +.i e.g. , +1.3), +use: +.(b +sccs get \-m \-p prog.c \(bv grep \'^1.3\' +.)b +The +.b \-p +flag causes \*S to output the generated source +to the standard output rather than to a file. +.sh 2 "Finding what changes you have made" +.pp +When you are editing a file, +you can find out what changes you have made using: +.(b +sccs diffs prog.c +.)b +Most of the ``diff'' flags can be used. +To pass the +.b \-c +flag, +use +.b \-C . +.pp +To compare two versions that are in deltas, +use: +.(b +sccs sccsdiff -r1.3 -r1.6 prog.c +.)b +to see the differences between delta 1.3 and delta 1.6. +.sh 1 "Shorthand Notations" +.pp +There are several sequences of commands that get +executed frequently. +.i Sccs +tries to make it easy to do these. +.sh 2 "Delget" +.pp +A frequent requirement is to make a delta of some file +and then get that file. +This can be done by using: +.(b +sccs delget prog.c +.)b +which is entirely equivalent to using: +.(b +sccs delta prog.c +sccs get prog.c +.)b +The +.q deledit +command is equivalent to +.q delget +except that the +.q edit +command is used +instead of the +.q get +command. +.sh 2 "Fix" +.pp +Frequently, there are small bugs +in deltas, +e.g., compilation errors, +for which there is no reason to maintain an audit trail. +To +.i replace +a delta, use: +.(b +sccs fix \-r1.4 prog.c +.)b +This will get a copy of delta 1.4 of prog.c for you to edit +and then delete delta 1.4 from the \*S file. +When you do a delta of prog.c, +it will be delta 1.4 again. +The \-r flag must be specified, +and the delta that is specified must be a leaf delta, +i.e., no other deltas may have been made subsequent +to the creation of that delta. +.sh 2 "Unedit" +.pp +If you found you edited a file +that you did not want to edit, +you can back out by using: +.(b +sccs unedit prog.c +.)b +.sh 2 "The \-d flag" +.pp +If you are working on a project +where the \*S code is in a directory somewhere, +you may be able to simplify things +by using a shell alias. +For example, +the alias: +.(b +alias syssccs sccs \-d/usr/src +.)b +will allow you to issue commands such as: +.(b +syssccs edit cmd/who.c +.)b +which will look for the file +.q "/usr/src/cmd/SCCS/who.c" . +The file +.q who.c +will always be created in your current directory +regardless of the value of the \-d flag. +.sh 1 "Using \*S on a Project" +.pp +Working on a project with several people +has its own set of special problems. +The main problem occurs when two people +modify a file at the same time. +\*S prevents this by locking an s-file +while it is being edited. +.pp +As a result, +files should not be reserved for editing +unless they are actually being edited at the time, +since this will prevent other people on the project +from making necessary changes. +For example, +a good scenario for working might be: +.(b +sccs edit a.c g.c t.c +vi a.c g.c t.c +# do testing of the (experimental) version +sccs delget a.c g.c t.c +sccs info +# should respond "Nothing being edited" +make install +.)b +.pp +As a general rule, +all source files should be deltaed +before installing the program for general use. +This will insure that it is possible +to restore any version in use at any time. +.sh 1 "Saving Yourself" +.sh 2 "Recovering a munged edit file" +.pp +Sometimes you may find +that you have destroyed or trashed +a file that you were trying to edit\**. +.(f +\**Or given up and decided to start over. +.)f +Unfortunately, +you can't just remove it +and re-\c +.i edit +it; +\*S keeps track +of the fact +that someone is trying to edit it, +so it won't let you do it again. +Neither can you just get it using +.i get , +since that would expand the Id keywords. +Instead, +you can say: +.(b +sccs get \-k prog.c +.)b +This will not expand the Id keywords, +so it is safe to do a delta +with it. +.pp +Alternately, +you can +.i unedit +and +.i edit +the file. +.sh 2 "Restoring the s-file" +.pp +In particularly bad circumstances, +the \*S file itself +may get munged. +The most common way this happens +is that it gets edited. +Since \*S keeps a checksum, +you will get errors every time you read the file. +To fix this checksum, use: +.(b +sccs admin \-z prog.c +.)b +.sh 1 "Using the Admin Command" +.pp +There are a number of parameters that can be set +using the +.i admin +command. +The most interesting of these are flags. +Flags can be added by using the +.b \-f +flag. +For example: +.(b +sccs admin \-fd1 prog.c +.)b +sets the +.q d +flag to the value +.q 1 . +This flag can be deleted by using: +.(b +sccs admin \-dd prog.c +.)b +The most useful flags are: +.nr ii 7n +.ip "b" +Allow branches to be made using the +\-b +flag to +.i edit . +.ip "d\fISID\fP" +Default \*I to be used on a +.i get +or +.i edit . +If this is just a release number +it constrains the +version +to a particular release only. +.ip "i" +Give a fatal error +if there are no Id Keywords in a file. +This is useful to guarantee that a version of the +file does not get merged into the s-file +that has the Id Keywords inserted as constants +instead of internal forms. +.ip "y" +The +.q type +of the module. +Actually, +the value of this flag is unused by \*S +except that it replaces the +.b %\&Y\&% +keyword. +.pp +The +.b \-t\fIfile\fR +flag can be used +to store descriptive text +from +.i file . +This descriptive text might be the documentation +or a design and implementation document. +Using the +.b \-t +flag insures that if the \*S file is sent, +the documentation will be sent also. +If +.i file +is omitted, +the descriptive text is deleted. +To see the descriptive text, +use +.q "prt \-t" . +.pp +The +.i admin +command can be used safely +any number of times on files. +A file need not be gotten +for +.i admin +to work. +.sh 1 "Maintaining Different Versions (Branches)" +.pp +Sometimes it is convenient +to maintain an experimental version of a program +for an extended period +while normal maintenance continues +on the version in production. +This can be done using a +.q branch. +Normally deltas continue in a straight line, +each depending on the delta before. +Creating a branch +.q "forks off" +a version of the program. +.pp +The ability to create branches +must be enabled in advance using: +.(b +sccs admin \-fb prog.c +.)b +The +.b \-fb +flag can be specified when the +\*S file is first created. +.sh 2 "Creating a branch" +.pp +To create a branch, use: +.(b +sccs edit \-b prog.c +.)b +This will create a branch +with (for example) \*I 1.5.1.1. +The deltas for this version +will be numbered +1.5.1.\c +.i n . +.sh 2 "Getting from a branch" +.pp +Deltas in a branch are normally not included +when you do a get. +To get these versions, +you will have to say: +.(b +sccs get \-r1.5.1 prog.c +.)b +.sh 2 "Merging a branch back into the main trunk" +.pp +At some point you will have finished the experiment, +and if it was successful +you will want to incorporate it into the release version. +But in the meantime +someone may have created a delta 1.6 +that you don't want to lose. +The commands: +.(b +sccs edit \-i1.5.1.1\-1.5.1 prog.c +sccs delta prog.c +.)b +will merge all of your changes +into the release system. +If some of the changes conflict, +get will print an error; +the generated result +should be carefully examined +before the delta is made. +.sh 2 "A more detailed example" +.pp +The following technique might be used +to maintain a different version of a program. +First, +create a directory to contain the new version: +.(b +mkdir ../newxyz +cd ../newxyz +.)b +Edit a copy of the program +on a branch: +.(b +sccs \-d../xyz edit prog.c +.)b +When using the old version, +be sure to use the +.b \-b +flag to info, check, tell, and clean +to avoid confusion. +For example, use: +.(b +sccs info \-b +.)b +when in the directory +.q xyz . +.pp +If you want to save a copy of the program +(still on the branch) +back in the s-file, +you can use: +.(b +sccs -d../xyz deledit prog.c +.)b +which will do a delta on the branch +and reedit it for you. +.pp +When the experiment is complete, merge it back into the s-file +using delta: +.(b +sccs -d../xyz delta prog.c +.)b +At this point you must decide whether this version +should be merged back into the trunk +(\c +.i i.e. +the default version), +which may have undergone changes. +If so, it can be merged using the +.b \-i +flag to +.i edit +as described above. +.sh 2 "A warning" +.pp +Branches should be kept to a minimum. +After the first branch from the trunk, +\*I's are assigned rather haphazardly, +and the structure gets complex fast. +.sh 1 "Using \*S with Make" +.pp +\*S and make can be made to work together +with a little care. +A few sample makefiles +for common applications are shown. +.pp +There are a few basic entries that every makefile +ought to have. +These are: +.nr ii 1i +.ip a.out +(or whatever the makefile generates.) +This entry regenerates whatever this makefile is +supposed to regenerate. +If the makefile regenerates many things, +this should be called +.q all +and should in turn +have dependencies on everything +the makefile can generate. +.ip install +Moves the objects to the final +resting place, +doing any special +.i chmod 's +or +.i ranlib 's +as appropriate. +.ip sources +Creates all the source files from \*S files. +.ip clean +Removes all files from the current directory +that can be regenerated from \*S files. +.ip print +Prints the contents of the directory. +.lp +The examples shown below are only partial examples, +and may omit some of these entries +when they are deemed to be obvious. +.pp +The +.i clean +entry should not remove files that can be +regenerated from the \*S files. +It is sufficiently important to have the +source files around at all times +that the only time they should be removed +is when the directory is being mothballed. +To do this, the command: +.(b +sccs clean +.)b +can be used. +This will remove all files for which an s-file +exists, +but which is not being edited. +.sh 2 "To maintain single programs" +.pp +Frequently there are directories with several +largely unrelated programs +(such as simple commands). +These can be put into a single makefile: +.(b +LDFLAGS= \-i \-s +.sp \n(psu +prog: prog.o + $(CC) $(LDFLAGS) \-o prog prog.o +prog.o: prog.c prog.h +.sp \n(psu +example: example.o + $(CC) $(LDFLAGS) \-o example example.o +example.o: example.c +.sp \n(psu +\&.DEFAULT: + sccs get $< +.)b +The trick here +is that the .DEFAULT rule +is called every time +something is needed +that does not exist, +and no other rule exists to make it. +The explicit dependency of the +.b \&.o +file on the +.b \&.c +file is important. +Another way of doing the same thing is: +.(b +SRCS= prog.c prog.h example.c +.sp \n(psu +LDFLAGS= \-i \-s +.sp \n(psu +prog: prog.o + $(CC) $(LDFLAGS) \-o prog prog.o +prog.o: prog.h +.sp \n(psu +example: example.o + $(CC) $(LDFLAGS) \-o example example.o +.sp \n(psu +sources: $(SRCS) +$(SRCS): + sccs get $@ +.)b +There are a couple of advantages to this approach: +(1) the explicit dependencies of the .o on the .c files are +not needed, +(2) there is an entry called "sources" so if you want to get +all the sources you can just say +.q "make sources" , +and +(3) the makefile is less likely to do confusing things +since it won't try to +.i get +things that do not exist. +.sh 2 "To maintain a library" +.pp +Libraries that are largely static +are best updated using explicit commands, +since +.i make +doesn't know about updating them properly. +However, +libraries that are in the process of being developed +can be handled quite adequately. +The problem is that the .o files +have to be kept out of the library +as well as in the library. +.(b +# configuration information +OBJS= a.o b.o c.o d.o +SRCS= a.c b.c c.c d.s x.h y.h z.h +TARG= /usr/lib +.sp \n(psu +# programs +GET= sccs get +REL= +AR= \-ar +RANLIB= ranlib +.sp \n(psu +lib.a: $(OBJS) + $(AR) rvu lib.a $(OBJS) + $(RANLIB) lib.a +.sp \n(psu +install: lib.a + sccs check + cp lib.a $(TARG)/lib.a + $(RANLIB) $(TARG)/lib.a +.sp \n(psu +sources: $(SRCS) +$(SRCS): + $(GET) $(REL) $@ +.sp \n(psu +print: sources + pr *.h *.[cs] +clean: + rm \-f *.o + rm \-f core a.out $(LIB) +.)b +.pp +The +.q "$(REL)" +in the get +can be used to get old versions +easily; for example: +.(b +make b.o REL=\-r1.3 +.)b +.pp +The +.i install +entry includes the line +.q "sccs check" +before anything else. +This guarantees that all the s-files +are up to date +(\c +.i i.e. , +nothing is being edited), +and will abort the +.i make +if this condition is not met. +.sh 2 "To maintain a large program" +.(b +OBJS= a.o b.o c.o d.o +SRCS= a.c b.c c.y d.s x.h y.h z.h +.sp \n(psu +GET= sccs get +REL= +.sp \n(psu +a.out: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LIBS) +.sp \n(psu +sources: $(SRCS) +$(SRCS): + $(GET) $(REL) $@ +.)b +(The +.i print +and +.i clean +entries are identical to the previous case.) +This makefile requires copies of the source and object files +to be kept during development. +It is probably also wise to include lines of the form: +.(b +a.o: x.h y.h +b.o: z.h +c.o: x.h y.h z.h +z.h: x.h +.)b +so that modules will be recompiled +if header files change. +.pp +Since +.i make +does not do transitive closure on dependencies, +you may find in some makefiles lines like: +.(b +z.h: x.h + touch z.h +.)b +This would be used in cases where file z.h +has a line: +.(b +#include "x.h" +.)b +in order to bring the mod date of z.h in line +with the mod date of x.h. +When you have a makefile such as above, +the +.i touch +command can be removed completely; +the equivalent effect will be achieved +by doing an automatic +.i get +on z.h. +.sh 1 "Further Information" +.pp +The +.i "SCCS/PWB User's Manual" +gives a deeper description +of how to use \*S. +Of particular interest +are the numbering of branches, +the l-file, +which gives a description of what deltas were used on a get, +and certain other \*S commands. +.pp +The \*S manual pages +are a good last resort. +These should be read by software managers +and by people who want to know +everything about everything. +.pp +Both of these documents were written without the +.i sccs +front end in mind, +so most of the examples are slightly different from those +in this document. +.bp +.sz 12 +.ce +.b "Quick Reference" +.sz +.sp 2 +.sh 1 Commands 1 +.pp +The following commands should all be preceded with +.q sccs . +This list is not exhaustive; +for more options see +.i "Further Information" . +.ip get 9n +Gets files for compilation (not for editing). +Id keywords are expanded. +.ba 9n +.nr ii 8n +.ip \-r\fI\*I\fP +Version to get. +.ip \-p +Send to standard output rather than to the actual file. +.ip \-k +Don't expand id keywords. +.ip \-i\fIlist\fP +List of deltas to include. +.ip \-x\fIlist\fP +List of deltas to exclude. +.ip \-m +Precede each line with \*I of creating delta. +.ip \-c\fIdate\fP +Don't apply any deltas created after +.i date. +.ba +.ip edit 9n +Gets files for editing. +Id keywords are not expanded. +Should be matched with a +.i delta +command. +.ba 9n +.nr ii 8n +.ip \-r\fI\*I\fP +Same as +.i get . +If +.i \*I +specifies a release that does not yet exist, +the highest numbered delta is retrieved +and the new delta is numbered with +.i \*I . +.ip \-b +Create a branch. +.ip \-i\fIlist\fP +Same as +.i get . +.ip \-x\fIlist\fP +Same as +.i get . +.ba +.ip delta 9n +Merge a file gotten using +.i edit +back into the s-file. +Collect comments about why this delta was made. +.ip unedit 9n +Remove a file that has been edited previously +without merging the changes into the s-file. +.ip prt 9n +Produce a report of changes. +.ba 9n +.nr ii 5n +.ip \-t +Print the descriptive text. +.ip \-e +Print (nearly) everything. +.ba +.ip info 9n +Give a list of all files being edited. +.ba 9n +.nr ii 5n +.ip \-b +Ignore branches. +.ip \-u[\fIuser\fP] +Ignore files not being edited by +.i user . +.ba +.ip check 9n +Same as +.i info , +except that nothing is printed if nothing is being edited +and exit status is returned. +.ip tell 9n +Same as +.i info , +except that one line is produced per file being edited containing +only the file name. +.ip clean 9n +Remove all files that can be regenerated from the +s-file. +.ip what 9n +Find and print id keywords. +.ip admin 9n +Create or set parameters on s-files. +.ba 9n +.nr ii 8n +.ip \-i\fIfile\fP +Create, using +.i file +as the initial contents. +.ip \-z +Rebuild the checksum in case +the file has been trashed. +.ip \-f\fIflag\fP +Turn on the +.i flag . +.ip \-d\fIflag\fP +Turn off (delete) the +.i flag . +.ip \-t\fIfile\fP +Replace the descriptive text +in the s-file with the contents of +.i file . +If +.i file +is omitted, +the text is deleted. +Useful for storing documentation +or +.q "design & implementation" +documents to insure they get distributed with the +s-file. +.lp +Useful flags are: +.ip b +Allow branches to be made using the \-b flag to +.i edit. +.ip d\fI\*I\fP +Default \*I to be used +on a +.i get +or +.i edit . +.ip i +Cause +.q "No Id Keywords" +error message +to be a fatal error rather than a warning. +.ip t +The module +.q type ; +the value of this flag replaces the +.b %\&Y\&% +keyword. +.ba +.ip fix 9n +Remove a delta and reedit it. +.ip delget 9n +Do a +.i delta +followed by a +.i get . +.ip deledit 9n +Do a +.i delta +followed by an +.i edit . +.sh 1 "Id Keywords" +.nr ii 6n +.ip "%\&Z\&%" +Expands to +.q @\&(#) +for the +.i what +command to find. +.ip "%\&M\&%" +The current module name, +.i e.g., +.q prog.c . +.ip "%\&I\&%" +The highest \*I applied. +.ip "%\&W\&%" +A shorthand for +.q "%\&Z\&%%\&M\&% %\&I\&%" . +.ip "%\&G\&%" +The date of the delta +corresponding to the +.q "%\&I\&%" +keyword. +.ip "%\&R\&%" +The current release number, +.i i.e. , +the first component of the +.q "%\&I\&%" +keyword. +.ip "%\&Y\&%" +Replaced by the value of the +.b t +flag +(set by +.i admin ). diff --git a/usr.bin/sccs/PSD.doc/spell.ok b/usr.bin/sccs/PSD.doc/spell.ok new file mode 100644 index 0000000..fb2fe24 --- /dev/null +++ b/usr.bin/sccs/PSD.doc/spell.ok @@ -0,0 +1,77 @@ +AccessSid +Admin +Allman +Berkeley.Edu +Delget +Ingres +LDFLAGS +LIB +LIBS +OBJS +OpsysSid +PS1:14 +PWB +REL +SCCS +SID +SRCS +Sccs +SccsId +System''PS1:14 +TARG +a.c +a.o +a.out +access.h +admin +b.c +b.o +backslash +bi +c.c +c.o +c.y +ch +cm7 +cmd +cs +d.o +d.s +deledit +delget +eric +example.c +example.o +fb +fd1 +foreach +g.c +info +inline +john +lib +lib.a +makefile +makefiles +mod +mothballed +newxyz +ok +opsys.h +prog +prog.c +prog.h +prog.o +prt +rvu +sccs +sccsdiff +src +syssccs +t.c +ujohn +who.c +x.h +xyz +y.h +z.h diff --git a/usr.bin/sccs/pathnames.h b/usr.bin/sccs/pathnames.h new file mode 100644 index 0000000..4da6874 --- /dev/null +++ b/usr.bin/sccs/pathnames.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#include + +#define _PATH_SCCSADMIN "/usr/local/bin/admin" +#define _PATH_SCCSBDIFF "/usr/local/bin/bdiff" +#define _PATH_SCCSCOMB "/usr/local/bin/comb" +#define _PATH_SCCSDELTA "/usr/local/bin/delta" +#define _PATH_SCCSDIFF "/usr/local/bin/sccsdiff" +#define _PATH_SCCSGET "/usr/local/bin/get" +#define _PATH_SCCSHELP "/usr/local/bin/help" +#define _PATH_SCCSPRS "/usr/local/bin/prs" +#define _PATH_SCCSPRT "/usr/local/bin/prt" +#define _PATH_SCCSRMDEL "/usr/local/bin/rmdel" +#define _PATH_SCCSVAL "/usr/local/bin/val" +#define _PATH_SCCSWHAT "/usr/local/bin/what" +#undef _PATH_TMP +#define _PATH_TMP "/tmp/sccsXXXXX" diff --git a/usr.bin/sccs/sccs.1 b/usr.bin/sccs/sccs.1 new file mode 100644 index 0000000..7f4990c --- /dev/null +++ b/usr.bin/sccs/sccs.1 @@ -0,0 +1,398 @@ +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sccs.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt SCCS 1 +.Os BSD 4.2 +.Sh NAME +.Nm sccs +.Nd front end for the +.Li SCCS +subsystem +.Sh SYNOPSIS +.Nm sccs +.Op Fl r +.Op Fl d Ar path +.Op Fl p Ar path +.Ar command +.Op flags +.Op Ar +.Sh DESCRIPTION +.Nm Sccs +is a front end to the +.Li SCCS +programs +that +helps them mesh more cleanly +with +the rest of UNIX. +It +also includes the capability to run +.Dq set user id +to another user +to +provide additional protection. +.Pp +Basically, +.Nm sccs +runs the command with the specified +.Ar flags +and +.Ar args . +Each argument is normally modified to be prepended with +.Dq Li SCCS/s. . +.Pp +Flags to be interpreted by the +.Nm sccs +program must be before the +.Ar command +argument. +Flags to be passed to the actual +.Li SCCS +program must come after the +.Ar command +argument. +These flags are specific to the command and +are discussed in the documentation for that command. +.Pp +Besides the usual +.Li SCCS +commands, +several +.Dq pseudo-commands +can be issued. +These are: +.Bl -tag -width deledit +.It Cm edit +Equivalent +to +.Dq Li get \-e . +.It Cm delget +Perform a delta on the named files and +then get new versions. +The new versions will have id keywords expanded, and +will not be editable. +The +.Fl m , +.Fl p , +.Fl r , +.Fl s , +and +.Fl y +flags will be passed to +.Nm delta , +and the +.Fl b, +.Fl c , +.Fl e , +.Fl i , +.Fl k , +.Fl l , +.Fl s , +.\" anybody who has a bad xterm which is almost anyone +and +.Fl x +flags will be passed to get. +.It Cm deledit +Equivalent +to +.Nm delget +except that the +.Nm get +phase includes the +.Fl e +flag. +This +option is useful for making a +.Em checkpoint +of your current editing phase. The same flags will be passed to delta +as described above, and +all the flags listed for +.om get +above except +.Fl e +and +.Fl k +are +passed to +.Nm edit . +.It Cm create +Creates +an +.Li SCCS +file , +taking +the initial contents from the file of the same name. +Any +flags to +.Nm admin +are accepted. If the creation is successful, +the files are renamed with a comma on the front. +These should be removed when you are convinced that the +.Li SCCS +files +have been created successfully. +.It Cm fix +Must +be followed by a +.Fl r +flag. +This command essentially removes the named delta, but +leaves you with a copy of the delta +with the changes that were in it. It +is useful for fixing small compiler bugs, etc. +Since it doesn't leave audit trails, it should be used carefully. +.It Cm clean +This routine removes everything from the current directory +that can be recreated from SCCS files. +It will not remove any files being edited. +If the +.Fl b +flag is given, branches are ignored in the determination of +whether they are being edited; this +is dangerous if you are keeping the branches in the +same directory. +.It Cm unedit +This +is the opposite of an +.Nm edit +or +a +.Dq Li get \-e . +It should be used with extreme caution, since +any changes you made since the get will be irretrievably lost. +.It Cm info +Gives a listing of all files being edited. +If the +.Fl b +flag +is given, branches (i.e., +.Li SID Ns \&\'s +with two or fewer components) +are ignored. If the +.Fl u +flag is given (with an optional argument) then +only files being edited by you (or the named user) are listed. +.It Cm check +Like +.Nm info +except that nothing is printed if nothing is being edited, and +a non-zero exit status is returned if anything is being edited. +The intent is to have this included in an +.Em install +entry in a makefile to insure that everything is included into the +.Li SCCS +file before a version is installed. +.It Cm tell +Gives a newline-separated list of the files being edited +on the standard output. Takes the +.Fl b +and +.Fl u +flags like +.Nm info +and +.Nm check . +.It Cm diffs +Gives a +.Nm diff +listing between the current version of the +program(s) you have out for editing and the versions in +.Li SCCS +format. +The +.Fl r , +.Fl c , +.Fl i , +.Fl x , +and +.Fl t +flags are passed to +.if n \{\ +. br +.\} +.Nm get ; +the +.Fl l , +.Fl s , +.Fl e , +.Fl f , +.Fl h , +and +.Fl b +options are passed to +.if n \{\ +. br +.\} +.Nm diff . +The +.Fl C +flag is passed to +.Nm diff +as +.Fl c . +.It Cm print +This command prints out verbose information +about the named files. +.Pp +.It Fl r +Runs +.Nm sccs +as the real user rather than as whatever effective user +.Nm sccs +is +.Dq Li set user id +to. +.It Fl d +Specifies a root directory for the +.Li SCCS +files. +The default is the current directory. +If environment variable +.Ev PROJECT +is set, +it will be used to determine the +.Fl d +flag. +.It Fl p +Defines the pathname of the directory in which the +.Li SCCS +files will be found; +.Dq Li SCCS +is the default. +The +.Fl p +flag +differs from the +.Fl d +flag +in that the +.Fl d +argument is prepended to the entire pathname and the +.Fl p +argument is inserted before the final component of the pathname. +For example, +.Dq Li sccs \-d/x \-py get a/b +will convert to +.Dq Li get /x/a/y/s.b . +The intent here is to create aliases such as +.Dq Li alias syssccs sccs -d/usr/src +which +will be used as +.Dq Li syssccs get cmd/who.c . +.Pp +Certain +commands (such as +.Nm admin ) +cannot be run +.Dq Li set user id +by all users, since this would allow anyone to change the authorizations. +These commands are always run as the real user. +.Sh EXAMPLES +To get a file for editing, +edit it, +and produce a new delta: +.Pp +.Dl sccs get \-e file.c +.Dl ex file.c +.Dl sccs delta file.c +.Pp +To get a file from another directory: +.Pp +.Dl sccs \-p/usr/src/sccs/s. get cc.c +.Pp +or +.Pp +.Dl sccs get /usr/src/sccs/s.cc.c +.Pp +To make a delta of a large number of files +in the current directory: +.Pp +.Dl sccs delta *.c +.Pp +To get a list of files being edited that are not on branches: +.Pp +.Dl sccs info \-b +.Pp +To delta everything being edited by you: +.Pp +.Dl sccs delta \`sccs tell \-u\` +.Pp +In a makefile, to get source files +from an +.Li SCCS +file if it does not already exist: +.Pp +.Dl SRCS = +.Dl $(SRCS): +.Dl \&\tsccs get $(REL) $@ +.Sh ENVIRONMENT +.Bl -tag -width Ar +.It Ev PROJECT +The PROJECT environment variable is checked by the +.Fl d +flag. If +it begins with a slash, it is taken directly; otherwise, +the home directory of a user of that name is +examined for a subdirectory +.Dq Li src +or +.Dq Li source . +If such a directory is found, it is used. +.El +.Sh SEE ALSO +.Xr what 1 +.Xr admin SCCS , +.Xr chghist SCCS , +.Xr comb SCCS , +.Xr delta SCCS , +.Xr get SCCS , +.Xr help SCCS , +.Xr prt SCCS , +.Xr rmdel SCCS , +.Xr sccsdiff SCCS , +.Rs +.%A Eric Allman +.%T "An Introduction to the Source Code Control System" +.Re +.Sh HISTORY +The +.Nm sccs +command +appeared in +.Bx 4.3 . +.Sh BUGS +It should be able to take directory arguments on pseudo-commands +like the +.Li SCCS +commands do. diff --git a/usr.bin/sccs/sccs.c b/usr.bin/sccs/sccs.c new file mode 100644 index 0000000..2dfd76d --- /dev/null +++ b/usr.bin/sccs/sccs.c @@ -0,0 +1,1621 @@ +/* + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)sccs.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* +** SCCS.C -- human-oriented front end to the SCCS system. +** +** Without trying to add any functionality to speak of, this +** program tries to make SCCS a little more accessible to human +** types. The main thing it does is automatically put the +** string "SCCS/s." on the front of names. Also, it has a +** couple of things that are designed to shorten frequent +** combinations, e.g., "delget" which expands to a "delta" +** and a "get". +** +** This program can also function as a setuid front end. +** To do this, you should copy the source, renaming it to +** whatever you want, e.g., "syssccs". Change any defaults +** in the program (e.g., syssccs might default -d to +** "/usr/src/sys"). Then recompile and put the result +** as setuid to whomever you want. In this mode, sccs +** knows to not run setuid for certain programs in order +** to preserve security, and so forth. +** +** Usage: +** sccs [flags] command [args] +** +** Flags: +** -d represents a directory to search +** out of. It should be a full pathname +** for general usage. E.g., if is +** "/usr/src/sys", then a reference to the +** file "dev/bio.c" becomes a reference to +** "/usr/src/sys/dev/bio.c". +** -p prepends to the final component +** of the pathname. By default, this is +** "SCCS". For example, in the -d example +** above, the path then gets modified to +** "/usr/src/sys/dev/SCCS/s.bio.c". In +** more common usage (without the -d flag), +** "prog.c" would get modified to +** "SCCS/s.prog.c". In both cases, the +** "s." gets automatically prepended. +** -r run as the real user. +** +** Commands: +** admin, +** get, +** delta, +** rmdel, +** cdc, +** etc. Straight out of SCCS; only difference +** is that pathnames get modified as +** described above. +** enter Front end doing "sccs admin -i " +** create Macro for "enter" followed by "get". +** edit Macro for "get -e". +** unedit Removes a file being edited, knowing +** about p-files, etc. +** delget Macro for "delta" followed by "get". +** deledit Macro for "delta" followed by "get -e". +** branch Macro for "get -b -e", followed by "delta +** -s -n", followd by "get -e -t -g". +** diffs "diff" the specified version of files +** and the checked-out version. +** print Macro for "prs -e" followed by "get -p -m". +** tell List what files are being edited. +** info Print information about files being edited. +** clean Remove all files that can be +** regenerated from SCCS files. +** check Like info, but return exit status, for +** use in makefiles. +** fix Remove a top delta & reedit, but save +** the previous changes in that delta. +** +** Compilation Flags: +** UIDUSER -- determine who the user is by looking at the +** uid rather than the login name -- for machines +** where SCCS gets the user in this way. +** SCCSDIR -- if defined, forces the -d flag to take on +** this value. This is so that the setuid +** aspects of this program cannot be abused. +** This flag also disables the -p flag. +** SCCSPATH -- the default for the -p flag. +** MYNAME -- the title this program should print when it +** gives error messages. +** +** Compilation Instructions: +** cc -O -n -s sccs.c +** The flags listed above can be -D defined to simplify +** recompilation for variant versions. +** +** Author: +** Eric Allman, UCB/INGRES +** Copyright 1980 Regents of the University of California +*/ + + +/******************* Configuration Information ********************/ + +# ifndef SCCSPATH +# define SCCSPATH "SCCS" /* pathname in which to find s-files */ +# endif NOT SCCSPATH + +# ifndef MYNAME +# define MYNAME "sccs" /* name used for printing errors */ +# endif NOT MYNAME + +/**************** End of Configuration Information ****************/ + +typedef char bool; +# define TRUE 1 +# define FALSE 0 + +# define bitset(bit, word) ((bool) ((bit) & (word))) + +struct sccsprog +{ + char *sccsname; /* name of SCCS routine */ + short sccsoper; /* opcode, see below */ + short sccsflags; /* flags, see below */ + char *sccspath; /* pathname of binary implementing */ +}; + +/* values for sccsoper */ +# define PROG 0 /* call a program */ +# define CMACRO 1 /* command substitution macro */ +# define FIX 2 /* fix a delta */ +# define CLEAN 3 /* clean out recreatable files */ +# define UNEDIT 4 /* unedit a file */ +# define SHELL 5 /* call a shell file (like PROG) */ +# define DIFFS 6 /* diff between sccs & file out */ +# define DODIFF 7 /* internal call to diff program */ +# define ENTER 8 /* enter new files */ + +/* bits for sccsflags */ +# define NO_SDOT 0001 /* no s. on front of args */ +# define REALUSER 0002 /* protected (e.g., admin) */ + +/* modes for the "clean", "info", "check" ops */ +# define CLEANC 0 /* clean command */ +# define INFOC 1 /* info command */ +# define CHECKC 2 /* check command */ +# define TELLC 3 /* give list of files being edited */ + +/* +** Description of commands known to this program. +** First argument puts the command into a class. Second arg is +** info regarding treatment of this command. Third arg is a +** list of flags this command accepts from macros, etc. Fourth +** arg is the pathname of the implementing program, or the +** macro definition, or the arg to a sub-algorithm. +*/ + +struct sccsprog SccsProg[] = { + "admin", PROG, REALUSER, _PATH_SCCSADMIN, + "cdc", PROG, 0, _PATH_SCCSRMDEL, + "comb", PROG, 0, _PATH_SCCSCOMB, + "delta", PROG, 0, _PATH_SCCSDELTA, + "get", PROG, 0, _PATH_SCCSGET, + "help", PROG, NO_SDOT, _PATH_SCCSHELP, + "prs", PROG, 0, _PATH_SCCSPRS, + "prt", PROG, 0, _PATH_SCCSPRT, + "rmdel", PROG, REALUSER, _PATH_SCCSRMDEL, + "val", PROG, 0, _PATH_SCCSVAL, + "what", PROG, NO_SDOT, _PATH_SCCSWHAT, + "sccsdiff", SHELL, REALUSER, _PATH_SCCSDIFF, + "edit", CMACRO, NO_SDOT, "get -e", + "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", + "deledit", CMACRO, NO_SDOT, + "delta:mysrp -n/get:ixbskcl -e -t -g", + "fix", FIX, NO_SDOT, NULL, + "clean", CLEAN, REALUSER|NO_SDOT, + (char *) CLEANC, + "info", CLEAN, REALUSER|NO_SDOT, + (char *) INFOC, + "check", CLEAN, REALUSER|NO_SDOT, + (char *) CHECKC, + "tell", CLEAN, REALUSER|NO_SDOT, + (char *) TELLC, + "unedit", UNEDIT, NO_SDOT, NULL, + "diffs", DIFFS, NO_SDOT|REALUSER, + NULL, + "-diff", DODIFF, NO_SDOT|REALUSER, + _PATH_SCCSBDIFF, + "print", CMACRO, 0, "prs -e/get -p -m -s", + "branch", CMACRO, NO_SDOT, + "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g", + "enter", ENTER, NO_SDOT, NULL, + "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t", + NULL, -1, 0, NULL +}; + +/* one line from a p-file */ +struct pfile +{ + char *p_osid; /* old SID */ + char *p_nsid; /* new SID */ + char *p_user; /* user who did edit */ + char *p_date; /* date of get */ + char *p_time; /* time of get */ + char *p_aux; /* extra info at end */ +}; + +char *SccsPath = SCCSPATH; /* pathname of SCCS files */ +# ifdef SCCSDIR +char *SccsDir = SCCSDIR; /* directory to begin search from */ +# else +char *SccsDir = ""; +# endif +char MyName[] = MYNAME; /* name used in messages */ +int OutFile = -1; /* override output file for commands */ +bool RealUser; /* if set, running as real user */ +# ifdef DEBUG +bool Debug; /* turn on tracing */ +# endif +# ifndef V6 +extern char *getenv(); +# endif V6 + +char *gstrcat(), *strcat(); +char *gstrncat(), *strncat(); +char *gstrcpy(), *strcpy(); +#define FBUFSIZ BUFSIZ +#define PFILELG 120 + +main(argc, argv) + int argc; + char **argv; +{ + register char *p; + extern struct sccsprog *lookup(); + register int i; +# ifndef V6 +# ifndef SCCSDIR + register struct passwd *pw; + extern struct passwd *getpwnam(); + char buf[FBUFSIZ]; + + /* pull "SccsDir" out of the environment (possibly) */ + p = getenv("PROJECTDIR"); + if (p != NULL && p[0] != '\0') + { + if (p[0] == '/') + SccsDir = p; + else + { + pw = getpwnam(p); + if (pw == NULL) + { + usrerr("user %s does not exist", p); + exit(EX_USAGE); + } + gstrcpy(buf, pw->pw_dir, sizeof(buf)); + gstrcat(buf, "/src", sizeof(buf)); + if (access(buf, 0) < 0) + { + gstrcpy(buf, pw->pw_dir, sizeof(buf)); + gstrcat(buf, "/source", sizeof(buf)); + if (access(buf, 0) < 0) + { + usrerr("project %s has no source!", p); + exit(EX_USAGE); + } + } + SccsDir = buf; + } + } +# endif SCCSDIR +# endif V6 + + /* + ** Detect and decode flags intended for this program. + */ + + if (argc < 2) + { + fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); + exit(EX_USAGE); + } + argv[argc] = NULL; + + if (lookup(argv[0]) == NULL) + { + while ((p = *++argv) != NULL) + { + if (*p != '-') + break; + switch (*++p) + { + case 'r': /* run as real user */ + setuid(getuid()); + RealUser++; + break; + +# ifndef SCCSDIR + case 'p': /* path of sccs files */ + SccsPath = ++p; + if (SccsPath[0] == '\0' && argv[1] != NULL) + SccsPath = *++argv; + break; + + case 'd': /* directory to search from */ + SccsDir = ++p; + if (SccsDir[0] == '\0' && argv[1] != NULL) + SccsDir = *++argv; + break; +# endif + +# ifdef DEBUG + case 'T': /* trace */ + Debug++; + break; +# endif + + default: + usrerr("unknown option -%s", p); + break; + } + } + if (SccsPath[0] == '\0') + SccsPath = "."; + } + + i = command(argv, FALSE, ""); + exit(i); +} + +/* +** COMMAND -- look up and perform a command +** +** This routine is the guts of this program. Given an +** argument vector, it looks up the "command" (argv[0]) +** in the configuration table and does the necessary stuff. +** +** Parameters: +** argv -- an argument vector to process. +** forkflag -- if set, fork before executing the command. +** editflag -- if set, only include flags listed in the +** sccsklets field of the command descriptor. +** arg0 -- a space-seperated list of arguments to insert +** before argv. +** +** Returns: +** zero -- command executed ok. +** else -- error status. +** +** Side Effects: +** none. +*/ + +command(argv, forkflag, arg0) + char **argv; + bool forkflag; + char *arg0; +{ + register struct sccsprog *cmd; + register char *p; + char buf[FBUFSIZ]; + extern struct sccsprog *lookup(); + char *nav[1000]; + char **np; + register char **ap; + register int i; + register char *q; + extern bool unedit(); + int rval = 0; + extern char *index(); + extern char *makefile(); + char *editchs; + extern char *tail(); + +# ifdef DEBUG + if (Debug) + { + printf("command:\n\t\"%s\"\n", arg0); + for (np = argv; *np != NULL; np++) + printf("\t\"%s\"\n", *np); + } +# endif + + /* + ** Copy arguments. + ** Copy from arg0 & if necessary at most one arg + ** from argv[0]. + */ + + np = ap = &nav[1]; + editchs = NULL; + for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) + { + *np++ = q; + while (*p == ' ') + p++; + while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') + *q++ = *p++; + *q++ = '\0'; + if (*p == ':') + { + editchs = q; + while (*++p != '\0' && *p != '/' && *p != ' ') + *q++ = *p; + *q++ = '\0'; + } + } + *np = NULL; + if (*ap == NULL) + *np++ = *argv++; + + /* + ** Look up command. + ** At this point, *ap is the command name. + */ + + cmd = lookup(*ap); + if (cmd == NULL) + { + usrerr("Unknown command \"%s\"", *ap); + return (EX_USAGE); + } + + /* + ** Copy remaining arguments doing editing as appropriate. + */ + + for (; *argv != NULL; argv++) + { + p = *argv; + if (*p == '-') + { + if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) + *np++ = p; + } + else + { + if (!bitset(NO_SDOT, cmd->sccsflags)) + p = makefile(p); + if (p != NULL) + *np++ = p; + } + } + *np = NULL; + + /* + ** Interpret operation associated with this command. + */ + + switch (cmd->sccsoper) + { + case SHELL: /* call a shell file */ + *ap = cmd->sccspath; + *--ap = "sh"; + rval = callprog(_PATH_BSHELL, cmd->sccsflags, ap, forkflag); + break; + + case PROG: /* call an sccs prog */ + rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); + break; + + case CMACRO: /* command macro */ + /* step through & execute each part of the macro */ + for (p = cmd->sccspath; *p != '\0'; p++) + { + q = p; + while (*p != '\0' && *p != '/') + p++; + rval = command(&ap[1], *p != '\0', q); + if (rval != 0) + break; + } + break; + + case FIX: /* fix a delta */ + if (ap[1]==0 || strncmp(ap[1], "-r", 2)!=0) + { + usrerr("-r flag needed for fix command"); + rval = EX_USAGE; + break; + } + + /* get the version with all changes */ + rval = command(&ap[1], TRUE, "get -k"); + + /* now remove that version from the s-file */ + if (rval == 0) + rval = command(&ap[1], TRUE, "rmdel:r"); + + /* and edit the old version (but don't clobber new vers) */ + if (rval == 0) + rval = command(&ap[2], FALSE, "get -e -g"); + break; + + case CLEAN: + rval = clean((int) cmd->sccspath, ap); + break; + + case UNEDIT: + for (argv = np = &ap[1]; *argv != NULL; argv++) + { + if (unedit(*argv)) + *np++ = *argv; + } + *np = NULL; + + /* get all the files that we unedited successfully */ + if (np > &ap[1]) + rval = command(&ap[1], FALSE, "get"); + break; + + case DIFFS: /* diff between s-file & edit file */ + /* find the end of the flag arguments */ + for (np = &ap[1]; *np != NULL && **np == '-'; np++) + continue; + argv = np; + + /* for each file, do the diff */ + p = argv[1]; + while (*np != NULL) + { + /* messy, but we need a null terminated argv */ + *argv = *np++; + argv[1] = NULL; + i = dodiff(ap, tail(*argv)); + if (rval == 0) + rval = i; + argv[1] = p; + } + break; + + case DODIFF: /* internal diff call */ + setuid(getuid()); + for (np = ap; *np != NULL; np++) + { + if ((*np)[0] == '-' && (*np)[1] == 'C') + (*np)[1] = 'c'; + } + + /* insert "-" argument */ + np[1] = NULL; + np[0] = np[-1]; + np[-1] = "-"; + + /* execute the diff program of choice */ +# ifndef V6 + execvp("diff", ap); +# endif + execv(cmd->sccspath, argv); + syserr("cannot exec %s", cmd->sccspath); + exit(EX_OSERR); + + case ENTER: /* enter new sccs files */ + /* skip over flag arguments */ + for (np = &ap[1]; *np != NULL && **np == '-'; np++) + continue; + argv = np; + + /* do an admin for each file */ + p = argv[1]; + while (*np != NULL) + { + printf("\n%s:\n", *np); + strcpy(buf, "-i"); + gstrcat(buf, *np, sizeof(buf)); + ap[0] = buf; + argv[0] = tail(*np); + argv[1] = NULL; + rval = command(ap, TRUE, "admin"); + argv[1] = p; + if (rval == 0) + { + strcpy(buf, ","); + gstrcat(buf, tail(*np), sizeof(buf)); + if (link(*np, buf) >= 0) + unlink(*np); + } + np++; + } + break; + + default: + syserr("oper %d", cmd->sccsoper); + exit(EX_SOFTWARE); + } +# ifdef DEBUG + if (Debug) + printf("command: rval=%d\n", rval); +# endif + return (rval); +} + +/* +** LOOKUP -- look up an SCCS command name. +** +** Parameters: +** name -- the name of the command to look up. +** +** Returns: +** ptr to command descriptor for this command. +** NULL if no such entry. +** +** Side Effects: +** none. +*/ + +struct sccsprog * +lookup(name) + char *name; +{ + register struct sccsprog *cmd; + + for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) + { + if (strcmp(cmd->sccsname, name) == 0) + return (cmd); + } + return (NULL); +} + +/* +** CALLPROG -- call a program +** +** Used to call the SCCS programs. +** +** Parameters: +** progpath -- pathname of the program to call. +** flags -- status flags from the command descriptors. +** argv -- an argument vector to pass to the program. +** forkflag -- if true, fork before calling, else just +** exec. +** +** Returns: +** The exit status of the program. +** Nothing if forkflag == FALSE. +** +** Side Effects: +** Can exit if forkflag == FALSE. +*/ + +callprog(progpath, flags, argv, forkflag) + char *progpath; + short flags; + char **argv; + bool forkflag; +{ + register int i; + register int wpid; + auto int st; + register int sigcode; + register int coredumped; + register const char *sigmsg; + char sigmsgbuf[10+1]; /* "Signal 127" + terminating '\0' */ + +# ifdef DEBUG + if (Debug) + { + printf("callprog:\n"); + for (i = 0; argv[i] != NULL; i++) + printf("\t\"%s\"\n", argv[i]); + } +# endif + + if (*argv == NULL) + return (-1); + + /* + ** Fork if appropriate. + */ + + if (forkflag) + { +# ifdef DEBUG + if (Debug) + printf("Forking\n"); +# endif + i = fork(); + if (i < 0) + { + syserr("cannot fork"); + exit(EX_OSERR); + } + else if (i > 0) + { + while ((wpid = wait(&st)) != -1 && wpid != i) + ; + if ((sigcode = st & 0377) == 0) + st = (st >> 8) & 0377; + else + { + coredumped = sigcode & 0200; + sigcode &= 0177; + if (sigcode != SIGINT && sigcode != SIGPIPE) + { + if (sigcode < NSIG) + sigmsg = sys_siglist[sigcode]; + else + { + sprintf(sigmsgbuf, "Signal %d", + sigcode); + sigmsg = sigmsgbuf; + } + fprintf(stderr, "sccs: %s: %s%s", argv[0], + sigmsg, + coredumped ? " - core dumped": ""); + } + st = EX_SOFTWARE; + } + if (OutFile >= 0) + { + close(OutFile); + OutFile = -1; + } + return (st); + } + } + else if (OutFile >= 0) + { + syserr("callprog: setting stdout w/o forking"); + exit(EX_SOFTWARE); + } + + /* set protection as appropriate */ + if (bitset(REALUSER, flags)) + setuid(getuid()); + + /* change standard input & output if needed */ + if (OutFile >= 0) + { + close(1); + dup(OutFile); + close(OutFile); + } + + /* call real SCCS program */ + execv(progpath, argv); + syserr("cannot execute %s", progpath); + exit(EX_UNAVAILABLE); + /*NOTREACHED*/ +} + +/* +** MAKEFILE -- make filename of SCCS file +** +** If the name passed is already the name of an SCCS file, +** just return it. Otherwise, munge the name into the name +** of the actual SCCS file. +** +** There are cases when it is not clear what you want to +** do. For example, if SccsPath is an absolute pathname +** and the name given is also an absolute pathname, we go +** for SccsPath (& only use the last component of the name +** passed) -- this is important for security reasons (if +** sccs is being used as a setuid front end), but not +** particularly intuitive. +** +** Parameters: +** name -- the file name to be munged. +** +** Returns: +** The pathname of the sccs file. +** NULL on error. +** +** Side Effects: +** none. +*/ + +char * +makefile(name) + char *name; +{ + register char *p; + char buf[3*FBUFSIZ]; + extern char *malloc(); + extern char *rindex(); + extern bool safepath(); + extern bool isdir(); + register char *q; + + p = rindex(name, '/'); + if (p == NULL) + p = name; + else + p++; + + /* + ** Check to see that the path is "safe", i.e., that we + ** are not letting some nasty person use the setuid part + ** of this program to look at or munge some presumably + ** hidden files. + */ + + if (SccsDir[0] == '/' && !safepath(name)) + return (NULL); + + /* + ** Create the base pathname. + */ + + /* first the directory part */ + if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) + { + gstrcpy(buf, SccsDir, sizeof(buf)); + gstrcat(buf, "/", sizeof(buf)); + } + else + gstrcpy(buf, "", sizeof(buf)); + + /* then the head of the pathname */ + gstrncat(buf, name, p - name, sizeof(buf)); + q = &buf[strlen(buf)]; + + /* now copy the final part of the name, in case useful */ + gstrcpy(q, p, sizeof(buf)); + + /* so is it useful? */ + if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) + { + /* sorry, no; copy the SCCS pathname & the "s." */ + gstrcpy(q, SccsPath, sizeof(buf)); + gstrcat(buf, "/s.", sizeof(buf)); + + /* and now the end of the name */ + gstrcat(buf, p, sizeof(buf)); + } + + /* if i haven't changed it, why did I do all this? */ + if (strcmp(buf, name) == 0) + p = name; + else + { + /* but if I have, squirrel it away */ + p = malloc(strlen(buf) + 1); + if (p == NULL) + { + perror("Sccs: no mem"); + exit(EX_OSERR); + } + strcpy(p, buf); + } + + return (p); +} + +/* +** ISDIR -- return true if the argument is a directory. +** +** Parameters: +** name -- the pathname of the file to check. +** +** Returns: +** TRUE if 'name' is a directory, FALSE otherwise. +** +** Side Effects: +** none. +*/ + +bool +isdir(name) + char *name; +{ + struct stat stbuf; + + return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); +} + +/* +** SAFEPATH -- determine whether a pathname is "safe" +** +** "Safe" pathnames only allow you to get deeper into the +** directory structure, i.e., full pathnames and ".." are +** not allowed. +** +** Parameters: +** p -- the name to check. +** +** Returns: +** TRUE -- if the path is safe. +** FALSE -- if the path is not safe. +** +** Side Effects: +** Prints a message if the path is not safe. +*/ + +bool +safepath(p) + register char *p; +{ + extern char *index(); + + if (*p != '/') + { + while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) + { + p = index(p, '/'); + if (p == NULL) + return (TRUE); + p++; + } + } + + printf("You may not use full pathnames or \"..\"\n"); + return (FALSE); +} + +/* +** CLEAN -- clean out recreatable files +** +** Any file for which an "s." file exists but no "p." file +** exists in the current directory is purged. +** +** Parameters: +** mode -- tells whether this came from a "clean", "info", or +** "check" command. +** argv -- the rest of the argument vector. +** +** Returns: +** none. +** +** Side Effects: +** Removes files in the current directory. +** Prints information regarding files being edited. +** Exits if a "check" command. +*/ + +clean(mode, argv) + int mode; + char **argv; +{ + struct direct *dir; + char buf[FBUFSIZ]; + char *bufend; + register DIR *dirp; + register char *basefile; + bool gotedit; + bool gotpfent; + FILE *pfp; + bool nobranch = FALSE; + extern struct pfile *getpfent(); + register struct pfile *pf; + register char **ap; + extern char *username(); + char *usernm = NULL; + char *subdir = NULL; + char *cmdname; + + /* + ** Process the argv + */ + + cmdname = *argv; + for (ap = argv; *++ap != NULL; ) + { + if (**ap == '-') + { + /* we have a flag */ + switch ((*ap)[1]) + { + case 'b': + nobranch = TRUE; + break; + + case 'u': + if ((*ap)[2] != '\0') + usernm = &(*ap)[2]; + else if (ap[1] != NULL && ap[1][0] != '-') + usernm = *++ap; + else + usernm = username(); + break; + } + } + else + { + if (subdir != NULL) + usrerr("too many args"); + else + subdir = *ap; + } + } + + /* + ** Find and open the SCCS directory. + */ + + gstrcpy(buf, SccsDir, sizeof(buf)); + if (buf[0] != '\0') + gstrcat(buf, "/", sizeof(buf)); + if (subdir != NULL) + { + gstrcat(buf, subdir, sizeof(buf)); + gstrcat(buf, "/", sizeof(buf)); + } + gstrcat(buf, SccsPath, sizeof(buf)); + bufend = &buf[strlen(buf)]; + + dirp = opendir(buf); + if (dirp == NULL) + { + usrerr("cannot open %s", buf); + return (EX_NOINPUT); + } + + /* + ** Scan the SCCS directory looking for s. files. + ** gotedit tells whether we have tried to clean any + ** files that are being edited. + */ + + gotedit = FALSE; + while (dir = readdir(dirp)) { + if (strncmp(dir->d_name, "s.", 2) != 0) + continue; + + /* got an s. file -- see if the p. file exists */ + gstrcpy(bufend, "/p.", sizeof(buf)); + basefile = bufend + 3; + gstrcpy(basefile, &dir->d_name[2], sizeof(buf)); + + /* + ** open and scan the p-file. + ** 'gotpfent' tells if we have found a valid p-file + ** entry. + */ + + pfp = fopen(buf, "r"); + gotpfent = FALSE; + if (pfp != NULL) + { + /* the file exists -- report it's contents */ + while ((pf = getpfent(pfp)) != NULL) + { + if (nobranch && isbranch(pf->p_nsid)) + continue; + if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) + continue; + gotedit = TRUE; + gotpfent = TRUE; + if (mode == TELLC) + { + printf("%s\n", basefile); + break; + } + printf("%12s: being edited: ", basefile); + putpfent(pf, stdout); + } + fclose(pfp); + } + + /* the s. file exists and no p. file exists -- unlink the g-file */ + if (mode == CLEANC && !gotpfent) + { + char unlinkbuf[FBUFSIZ]; + gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf)); + unlink(unlinkbuf); + } + } + + /* cleanup & report results */ + closedir(dirp); + if (!gotedit && mode == INFOC) + { + printf("Nothing being edited"); + if (nobranch) + printf(" (on trunk)"); + if (usernm == NULL) + printf("\n"); + else + printf(" by %s\n", usernm); + } + if (mode == CHECKC) + exit(gotedit); + return (EX_OK); +} + +/* +** ISBRANCH -- is the SID a branch? +** +** Parameters: +** sid -- the sid to check. +** +** Returns: +** TRUE if the sid represents a branch. +** FALSE otherwise. +** +** Side Effects: +** none. +*/ + +isbranch(sid) + char *sid; +{ + register char *p; + int dots; + + dots = 0; + for (p = sid; *p != '\0'; p++) + { + if (*p == '.') + dots++; + if (dots > 1) + return (TRUE); + } + return (FALSE); +} + +/* +** UNEDIT -- unedit a file +** +** Checks to see that the current user is actually editting +** the file and arranges that s/he is not editting it. +** +** Parameters: +** fn -- the name of the file to be unedited. +** +** Returns: +** TRUE -- if the file was successfully unedited. +** FALSE -- if the file was not unedited for some +** reason. +** +** Side Effects: +** fn is removed +** entries are removed from pfile. +*/ + +bool +unedit(fn) + char *fn; +{ + register FILE *pfp; + char *cp, *pfn; + static char tfn[] = _PATH_TMP; + FILE *tfp; + register char *q; + bool delete = FALSE; + bool others = FALSE; + char *myname; + extern char *username(); + struct pfile *pent; + extern struct pfile *getpfent(); + char buf[PFILELG]; + extern char *makefile(), *rindex(), *tail(); + + /* make "s." filename & find the trailing component */ + pfn = makefile(fn); + if (pfn == NULL) + return (FALSE); + q = rindex(pfn, '/'); + if (q == NULL) + q = &pfn[-1]; + if (q[1] != 's' || q[2] != '.') + { + usrerr("bad file name \"%s\"", fn); + return (FALSE); + } + + /* turn "s." into "p." & try to open it */ + *++q = 'p'; + + pfp = fopen(pfn, "r"); + if (pfp == NULL) + { + printf("%12s: not being edited\n", fn); + return (FALSE); + } + + /* create temp file for editing p-file */ + mktemp(tfn); + tfp = fopen(tfn, "w"); + if (tfp == NULL) + { + usrerr("cannot create \"%s\"", tfn); + exit(EX_OSERR); + } + + /* figure out who I am */ + myname = username(); + + /* + ** Copy p-file to temp file, doing deletions as needed. + */ + + while ((pent = getpfent(pfp)) != NULL) + { + if (strcmp(pent->p_user, myname) == 0) + { + /* a match */ + delete++; + } + else + { + /* output it again */ + putpfent(pent, tfp); + others++; + } + } + + /* + * Before changing anything, make sure we can remove + * the file in question (assuming it exists). + */ + if (delete) { + extern int errno; + + cp = tail(fn); + errno = 0; + if (access(cp, 0) < 0 && errno != ENOENT) + goto bad; + if (errno == 0) + /* + * This is wrong, but the rest of the program + * has built in assumptions about "." as well, + * so why make unedit a special case? + */ + if (access(".", 2) < 0) { + bad: + printf("%12s: can't remove\n", cp); + fclose(tfp); + fclose(pfp); + unlink(tfn); + return (FALSE); + } + } + /* do final cleanup */ + if (others) + { + /* copy it back (perhaps it should be linked?) */ + if (freopen(tfn, "r", tfp) == NULL) + { + syserr("cannot reopen \"%s\"", tfn); + exit(EX_OSERR); + } + if (freopen(pfn, "w", pfp) == NULL) + { + usrerr("cannot create \"%s\"", pfn); + return (FALSE); + } + while (fgets(buf, sizeof buf, tfp) != NULL) + fputs(buf, pfp); + } + else + { + /* it's empty -- remove it */ + unlink(pfn); + } + fclose(tfp); + fclose(pfp); + unlink(tfn); + + /* actually remove the g-file */ + if (delete) + { + /* + * Since we've checked above, we can + * use the return from unlink to + * determine if the file existed or not. + */ + if (unlink(cp) >= 0) + printf("%12s: removed\n", cp); + return (TRUE); + } + else + { + printf("%12s: not being edited by you\n", fn); + return (FALSE); + } +} + +/* +** DODIFF -- diff an s-file against a g-file +** +** Parameters: +** getv -- argv for the 'get' command. +** gfile -- name of the g-file to diff against. +** +** Returns: +** Result of get. +** +** Side Effects: +** none. +*/ + +dodiff(getv, gfile) + char **getv; + char *gfile; +{ + int pipev[2]; + int rval; + register int i; + register int pid; + auto int st; + extern int errno; + sig_t osig; + + printf("\n------- %s -------\n", gfile); + fflush(stdout); + + /* create context for diff to run in */ + if (pipe(pipev) < 0) + { + syserr("dodiff: pipe failed"); + exit(EX_OSERR); + } + if ((pid = fork()) < 0) + { + syserr("dodiff: fork failed"); + exit(EX_OSERR); + } + else if (pid > 0) + { + /* in parent; run get */ + OutFile = pipev[1]; + close(pipev[0]); + rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); + osig = signal(SIGINT, SIG_IGN); + while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) + errno = 0; + signal(SIGINT, osig); + /* ignore result of diff */ + } + else + { + /* in child, run diff */ + if (close(pipev[1]) < 0 || close(0) < 0 || + dup(pipev[0]) != 0 || close(pipev[0]) < 0) + { + syserr("dodiff: magic failed"); + exit(EX_OSERR); + } + command(&getv[1], FALSE, "-diff:elsfhbC"); + } + return (rval); +} + +/* +** TAIL -- return tail of filename. +** +** Parameters: +** fn -- the filename. +** +** Returns: +** a pointer to the tail of the filename; e.g., given +** "cmd/ls.c", "ls.c" is returned. +** +** Side Effects: +** none. +*/ + +char * +tail(fn) + register char *fn; +{ + register char *p; + + for (p = fn; *p != 0; p++) + if (*p == '/' && p[1] != '\0' && p[1] != '/') + fn = &p[1]; + return (fn); +} + +/* +** GETPFENT -- get an entry from the p-file +** +** Parameters: +** pfp -- p-file file pointer +** +** Returns: +** pointer to p-file struct for next entry +** NULL on EOF or error +** +** Side Effects: +** Each call wipes out results of previous call. +*/ + +struct pfile * +getpfent(pfp) + FILE *pfp; +{ + static struct pfile ent; + static char buf[PFILELG]; + register char *p; + extern char *nextfield(); + + if (fgets(buf, sizeof buf, pfp) == NULL) + return (NULL); + + ent.p_osid = p = buf; + ent.p_nsid = p = nextfield(p); + ent.p_user = p = nextfield(p); + ent.p_date = p = nextfield(p); + ent.p_time = p = nextfield(p); + ent.p_aux = p = nextfield(p); + + return (&ent); +} + + +char * +nextfield(p) + register char *p; +{ + if (p == NULL || *p == '\0') + return (NULL); + while (*p != ' ' && *p != '\n' && *p != '\0') + p++; + if (*p == '\n' || *p == '\0') + { + *p = '\0'; + return (NULL); + } + *p++ = '\0'; + return (p); +} + /* +** PUTPFENT -- output a p-file entry to a file +** +** Parameters: +** pf -- the p-file entry +** f -- the file to put it on. +** +** Returns: +** none. +** +** Side Effects: +** pf is written onto file f. +*/ + +putpfent(pf, f) + register struct pfile *pf; + register FILE *f; +{ + fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid, + pf->p_user, pf->p_date, pf->p_time); + if (pf->p_aux != NULL) + fprintf(f, " %s", pf->p_aux); + else + fprintf(f, "\n"); +} + +/* +** USRERR -- issue user-level error +** +** Parameters: +** f -- format string. +** p1-p3 -- parameters to a printf. +** +** Returns: +** -1 +** +** Side Effects: +** none. +*/ + +/*VARARGS1*/ +usrerr(f, p1, p2, p3) + char *f; +{ + fprintf(stderr, "\n%s: ", MyName); + fprintf(stderr, f, p1, p2, p3); + fprintf(stderr, "\n"); + + return (-1); +} + +/* +** SYSERR -- print system-generated error. +** +** Parameters: +** f -- format string to a printf. +** p1, p2, p3 -- parameters to f. +** +** Returns: +** never. +** +** Side Effects: +** none. +*/ + +/*VARARGS1*/ +syserr(f, p1, p2, p3) + char *f; +{ + extern int errno; + + fprintf(stderr, "\n%s SYSERR: ", MyName); + fprintf(stderr, f, p1, p2, p3); + fprintf(stderr, "\n"); + if (errno == 0) + exit(EX_SOFTWARE); + else + { + perror(NULL); + exit(EX_OSERR); + } +} + /* +** USERNAME -- return name of the current user +** +** Parameters: +** none +** +** Returns: +** name of current user +** +** Side Effects: +** none +*/ + +char * +username() +{ +# ifdef UIDUSER + extern struct passwd *getpwuid(); + register struct passwd *pw; + + pw = getpwuid(getuid()); + if (pw == NULL) + { + syserr("who are you? (uid=%d)", getuid()); + exit(EX_OSERR); + } + return (pw->pw_name); +# else + extern char *getlogin(); + register char *p; + + p = getenv("USER"); + if (p == NULL || p[0] == '\0') + p = getlogin(); + return (p); +# endif UIDUSER +} + +/* +** Guarded string manipulation routines; the last argument +** is the length of the buffer into which the strcpy or strcat +** is to be done. +*/ +char *gstrcat(to, from, length) + char *to, *from; + int length; +{ + if (strlen(from) + strlen(to) >= length) { + gstrbotch(to, from); + } + return(strcat(to, from)); +} + +char *gstrncat(to, from, n, length) + char *to, *from; + int n; + int length; +{ + if (n + strlen(to) >= length) { + gstrbotch(to, from); + } + return(strncat(to, from, n)); +} + +char *gstrcpy(to, from, length) + char *to, *from; + int length; +{ + if (strlen(from) >= length) { + gstrbotch(from, (char *)0); + } + return(strcpy(to, from)); +} +gstrbotch(str1, str2) + char *str1, *str2; +{ + usrerr("Filename(s) too long: %s %s", str1, str2); +} -- cgit v1.1