1*fa28c6faSchristos /* $NetBSD: co.c,v 1.2 2016/01/14 04:22:39 christos Exp $ */
27bdc2678Schristos
37bdc2678Schristos /* Check out working files from revisions of RCS files. */
47bdc2678Schristos
57bdc2678Schristos /* Copyright 1982, 1988, 1989 Walter Tichy
67bdc2678Schristos Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
77bdc2678Schristos Distributed under license by the Free Software Foundation, Inc.
87bdc2678Schristos
97bdc2678Schristos This file is part of RCS.
107bdc2678Schristos
117bdc2678Schristos RCS is free software; you can redistribute it and/or modify
127bdc2678Schristos it under the terms of the GNU General Public License as published by
137bdc2678Schristos the Free Software Foundation; either version 2, or (at your option)
147bdc2678Schristos any later version.
157bdc2678Schristos
167bdc2678Schristos RCS is distributed in the hope that it will be useful,
177bdc2678Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
187bdc2678Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
197bdc2678Schristos GNU General Public License for more details.
207bdc2678Schristos
217bdc2678Schristos You should have received a copy of the GNU General Public License
227bdc2678Schristos along with RCS; see the file COPYING.
237bdc2678Schristos If not, write to the Free Software Foundation,
247bdc2678Schristos 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
257bdc2678Schristos
267bdc2678Schristos Report problems and direct all questions to:
277bdc2678Schristos
287bdc2678Schristos rcs-bugs@cs.purdue.edu
297bdc2678Schristos
307bdc2678Schristos */
317bdc2678Schristos
327bdc2678Schristos /*
337bdc2678Schristos * Log: co.c,v
347bdc2678Schristos * Revision 5.18 1995/06/16 06:19:24 eggert
357bdc2678Schristos * Update FSF address.
367bdc2678Schristos *
377bdc2678Schristos * Revision 5.17 1995/06/01 16:23:43 eggert
387bdc2678Schristos * (main, preparejoin): Pass argument instead of using `join' static variable.
397bdc2678Schristos * (main): Add -kb.
407bdc2678Schristos *
417bdc2678Schristos * Revision 5.16 1994/03/17 14:05:48 eggert
427bdc2678Schristos * Move buffer-flushes out of critical sections, since they aren't critical.
437bdc2678Schristos * Use ORCSerror to clean up after a fatal error. Remove lint.
447bdc2678Schristos * Specify subprocess input via file descriptor, not file name.
457bdc2678Schristos *
467bdc2678Schristos * Revision 5.15 1993/11/09 17:40:15 eggert
477bdc2678Schristos * -V now prints version on stdout and exits. Don't print usage twice.
487bdc2678Schristos *
497bdc2678Schristos * Revision 5.14 1993/11/03 17:42:27 eggert
507bdc2678Schristos * Add -z. Generate a value for the Name keyword.
517bdc2678Schristos * Don't arbitrarily limit the number of joins.
527bdc2678Schristos * Improve quality of diagnostics.
537bdc2678Schristos *
547bdc2678Schristos * Revision 5.13 1992/07/28 16:12:44 eggert
557bdc2678Schristos * Add -V. Check that working and RCS files are distinct.
567bdc2678Schristos *
577bdc2678Schristos * Revision 5.12 1992/02/17 23:02:08 eggert
587bdc2678Schristos * Add -T.
597bdc2678Schristos *
607bdc2678Schristos * Revision 5.11 1992/01/24 18:44:19 eggert
617bdc2678Schristos * Add support for bad_creat0. lint -> RCS_lint
627bdc2678Schristos *
637bdc2678Schristos * Revision 5.10 1992/01/06 02:42:34 eggert
647bdc2678Schristos * Update usage string.
657bdc2678Schristos *
667bdc2678Schristos * Revision 5.9 1991/10/07 17:32:46 eggert
677bdc2678Schristos * -k affects just working file, not RCS file.
687bdc2678Schristos *
697bdc2678Schristos * Revision 5.8 1991/08/19 03:13:55 eggert
707bdc2678Schristos * Warn before removing somebody else's file.
717bdc2678Schristos * Add -M. Fix co -j bugs. Tune.
727bdc2678Schristos *
737bdc2678Schristos * Revision 5.7 1991/04/21 11:58:15 eggert
747bdc2678Schristos * Ensure that working file is newer than RCS file after co -[lu].
757bdc2678Schristos * Add -x, RCSINIT, MS-DOS support.
767bdc2678Schristos *
777bdc2678Schristos * Revision 5.6 1990/12/04 05:18:38 eggert
787bdc2678Schristos * Don't checkaccesslist() unless necessary.
797bdc2678Schristos * Use -I for prompts and -q for diagnostics.
807bdc2678Schristos *
817bdc2678Schristos * Revision 5.5 1990/11/01 05:03:26 eggert
827bdc2678Schristos * Fix -j. Add -I.
837bdc2678Schristos *
847bdc2678Schristos * Revision 5.4 1990/10/04 06:30:11 eggert
857bdc2678Schristos * Accumulate exit status across files.
867bdc2678Schristos *
877bdc2678Schristos * Revision 5.3 1990/09/11 02:41:09 eggert
887bdc2678Schristos * co -kv yields a readonly working file.
897bdc2678Schristos *
907bdc2678Schristos * Revision 5.2 1990/09/04 08:02:13 eggert
917bdc2678Schristos * Standardize yes-or-no procedure.
927bdc2678Schristos *
937bdc2678Schristos * Revision 5.0 1990/08/22 08:10:02 eggert
947bdc2678Schristos * Permit multiple locks by same user. Add setuid support.
957bdc2678Schristos * Remove compile-time limits; use malloc instead.
967bdc2678Schristos * Permit dates past 1999/12/31. Switch to GMT.
977bdc2678Schristos * Make lock and temp files faster and safer.
987bdc2678Schristos * Ansify and Posixate. Add -k, -V. Remove snooping. Tune.
997bdc2678Schristos *
1007bdc2678Schristos * Revision 4.7 89/05/01 15:11:41 narten
1017bdc2678Schristos * changed copyright header to reflect current distribution rules
1027bdc2678Schristos *
1037bdc2678Schristos * Revision 4.6 88/08/09 19:12:15 eggert
1047bdc2678Schristos * Fix "co -d" core dump; rawdate wasn't always initialized.
1057bdc2678Schristos * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
1067bdc2678Schristos *
1077bdc2678Schristos * Revision 4.5 87/12/18 11:35:40 narten
1087bdc2678Schristos * lint cleanups (from Guy Harris)
1097bdc2678Schristos *
1107bdc2678Schristos * Revision 4.4 87/10/18 10:20:53 narten
1117bdc2678Schristos * Updating version numbers changes relative to 1.1, are actually
1127bdc2678Schristos * relative to 4.2
1137bdc2678Schristos *
1147bdc2678Schristos * Revision 1.3 87/09/24 13:58:30 narten
1157bdc2678Schristos * Sources now pass through lint (if you ignore printf/sprintf/fprintf
1167bdc2678Schristos * warnings)
1177bdc2678Schristos *
1187bdc2678Schristos * Revision 1.2 87/03/27 14:21:38 jenkins
1197bdc2678Schristos * Port to suns
1207bdc2678Schristos *
1217bdc2678Schristos * Revision 4.2 83/12/05 13:39:48 wft
1227bdc2678Schristos * made rewriteflag external.
1237bdc2678Schristos *
1247bdc2678Schristos * Revision 4.1 83/05/10 16:52:55 wft
1257bdc2678Schristos * Added option -u and -f.
1267bdc2678Schristos * Added handling of default branch.
1277bdc2678Schristos * Replaced getpwuid() with getcaller().
1287bdc2678Schristos * Removed calls to stat(); now done by pairfilenames().
1297bdc2678Schristos * Changed and renamed rmoldfile() to rmworkfile().
1307bdc2678Schristos * Replaced catchints() calls with restoreints(), unlink()--link() with rename();
1317bdc2678Schristos *
1327bdc2678Schristos * Revision 3.7 83/02/15 15:27:07 wft
1337bdc2678Schristos * Added call to fastcopy() to copy remainder of RCS file.
1347bdc2678Schristos *
1357bdc2678Schristos * Revision 3.6 83/01/15 14:37:50 wft
1367bdc2678Schristos * Added ignoring of interrupts while RCS file is renamed; this avoids
1377bdc2678Schristos * deletion of RCS files during the unlink/link window.
1387bdc2678Schristos *
1397bdc2678Schristos * Revision 3.5 82/12/08 21:40:11 wft
1407bdc2678Schristos * changed processing of -d to use DATEFORM; removed actual from
1417bdc2678Schristos * call to preparejoin; re-fixed printing of done at the end.
1427bdc2678Schristos *
1437bdc2678Schristos * Revision 3.4 82/12/04 18:40:00 wft
1447bdc2678Schristos * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
1457bdc2678Schristos * Fixed printing of "done".
1467bdc2678Schristos *
1477bdc2678Schristos * Revision 3.3 82/11/28 22:23:11 wft
1487bdc2678Schristos * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
1497bdc2678Schristos * %02d with %.2d, mode generation for working file with WORKMODE.
1507bdc2678Schristos * Fixed nil printing. Fixed -j combined with -l and -p, and exit
1517bdc2678Schristos * for non-existing revisions in preparejoin().
1527bdc2678Schristos *
1537bdc2678Schristos * Revision 3.2 82/10/18 20:47:21 wft
1547bdc2678Schristos * Mode of working file is now maintained even for co -l, but write permission
1557bdc2678Schristos * is removed.
1567bdc2678Schristos * The working file inherits its mode from the RCS file, plus write permission
1577bdc2678Schristos * for the owner. The write permission is not given if locking is strict and
1587bdc2678Schristos * co does not lock.
1597bdc2678Schristos * An existing working file without write permission is deleted automatically.
1607bdc2678Schristos * Otherwise, co asks (empty answer: abort co).
1617bdc2678Schristos * Call to getfullRCSname() added, check for write error added, call
1627bdc2678Schristos * for getlogin() fixed.
1637bdc2678Schristos *
1647bdc2678Schristos * Revision 3.1 82/10/13 16:01:30 wft
1657bdc2678Schristos * fixed type of variables receiving from getc() (char -> int).
1667bdc2678Schristos * removed unused variables.
1677bdc2678Schristos */
1687bdc2678Schristos
1697bdc2678Schristos
1707bdc2678Schristos
1717bdc2678Schristos
1727bdc2678Schristos #include "rcsbase.h"
1737bdc2678Schristos
1747bdc2678Schristos static char *addjoin P((char*));
1757bdc2678Schristos static char const *getancestor P((char const*,char const*));
1767bdc2678Schristos static int buildjoin P((char const*));
1777bdc2678Schristos static int preparejoin P((char*));
1787bdc2678Schristos static int rmlock P((struct hshentry const*));
1797bdc2678Schristos static int rmworkfile P((void));
1807bdc2678Schristos static void cleanup P((void));
1817bdc2678Schristos
1827bdc2678Schristos static char const quietarg[] = "-q";
1837bdc2678Schristos
1847bdc2678Schristos static char const *expandarg, *suffixarg, *versionarg, *zonearg;
1857bdc2678Schristos static char const **joinlist; /* revisions to be joined */
1867bdc2678Schristos static int joinlength;
1877bdc2678Schristos static FILE *neworkptr;
1887bdc2678Schristos static int exitstatus;
1897bdc2678Schristos static int forceflag;
1907bdc2678Schristos static int lastjoin; /* index of last element in joinlist */
1917bdc2678Schristos static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */
1927bdc2678Schristos static int mtimeflag;
1937bdc2678Schristos static struct hshentries *gendeltas; /* deltas to be generated */
1947bdc2678Schristos static struct hshentry *targetdelta; /* final delta to be generated */
1957bdc2678Schristos static struct stat workstat;
1967bdc2678Schristos
1977bdc2678Schristos mainProg(coId, "co", "Id: co.c,v 5.18 1995/06/16 06:19:24 eggert Exp ")
1987bdc2678Schristos {
1997bdc2678Schristos static char const cmdusage[] =
2007bdc2678Schristos "\nco usage: co -{fIlMpqru}[rev] -ddate -jjoins -ksubst -sstate -T -w[who] -Vn -xsuff -zzone file ...";
2017bdc2678Schristos
2027bdc2678Schristos char *a, *joinflag, **newargv;
2037bdc2678Schristos char const *author, *date, *rev, *state;
2047bdc2678Schristos char const *joinname, *newdate, *neworkname;
2057bdc2678Schristos int changelock; /* 1 if a lock has been changed, -1 if error */
2067bdc2678Schristos int expmode, r, tostdout, workstatstat;
2077bdc2678Schristos int Ttimeflag;
2087bdc2678Schristos struct buf numericrev; /* expanded revision number */
2097bdc2678Schristos char finaldate[datesize];
2107bdc2678Schristos # if OPEN_O_BINARY
2117bdc2678Schristos int stdout_mode = 0;
2127bdc2678Schristos # endif
2137bdc2678Schristos
2147bdc2678Schristos setrid();
2157bdc2678Schristos author = date = rev = state = 0;
2167bdc2678Schristos joinflag = 0;
2177bdc2678Schristos bufautobegin(&numericrev);
2187bdc2678Schristos expmode = -1;
2197bdc2678Schristos suffixes = X_DEFAULT;
2207bdc2678Schristos tostdout = false;
2217bdc2678Schristos Ttimeflag = false;
2227bdc2678Schristos
2237bdc2678Schristos argc = getRCSINIT(argc, argv, &newargv);
2247bdc2678Schristos argv = newargv;
2257bdc2678Schristos while (a = *++argv, 0<--argc && *a++=='-') {
2267bdc2678Schristos switch (*a++) {
2277bdc2678Schristos
2287bdc2678Schristos case 'r':
2297bdc2678Schristos revno:
2307bdc2678Schristos if (*a) {
2317bdc2678Schristos if (rev) warn("redefinition of revision number");
2327bdc2678Schristos rev = a;
2337bdc2678Schristos }
2347bdc2678Schristos break;
2357bdc2678Schristos
2367bdc2678Schristos case 'f':
2377bdc2678Schristos forceflag=true;
2387bdc2678Schristos goto revno;
2397bdc2678Schristos
2407bdc2678Schristos case 'l':
2417bdc2678Schristos if (lockflag < 0) {
2427bdc2678Schristos warn("-u overridden by -l.");
2437bdc2678Schristos }
2447bdc2678Schristos lockflag = 1;
2457bdc2678Schristos goto revno;
2467bdc2678Schristos
2477bdc2678Schristos case 'u':
2487bdc2678Schristos if (0 < lockflag) {
2497bdc2678Schristos warn("-l overridden by -u.");
2507bdc2678Schristos }
2517bdc2678Schristos lockflag = -1;
2527bdc2678Schristos goto revno;
2537bdc2678Schristos
2547bdc2678Schristos case 'p':
2557bdc2678Schristos tostdout = true;
2567bdc2678Schristos goto revno;
2577bdc2678Schristos
2587bdc2678Schristos case 'I':
2597bdc2678Schristos interactiveflag = true;
2607bdc2678Schristos goto revno;
2617bdc2678Schristos
2627bdc2678Schristos case 'q':
2637bdc2678Schristos quietflag=true;
2647bdc2678Schristos goto revno;
2657bdc2678Schristos
2667bdc2678Schristos case 'd':
2677bdc2678Schristos if (date)
2687bdc2678Schristos redefined('d');
2697bdc2678Schristos str2date(a, finaldate);
2707bdc2678Schristos date=finaldate;
2717bdc2678Schristos break;
2727bdc2678Schristos
2737bdc2678Schristos case 'j':
2747bdc2678Schristos if (*a) {
2757bdc2678Schristos if (joinflag) redefined('j');
2767bdc2678Schristos joinflag = a;
2777bdc2678Schristos }
2787bdc2678Schristos break;
2797bdc2678Schristos
2807bdc2678Schristos case 'M':
2817bdc2678Schristos mtimeflag = true;
2827bdc2678Schristos goto revno;
2837bdc2678Schristos
2847bdc2678Schristos case 's':
2857bdc2678Schristos if (*a) {
2867bdc2678Schristos if (state) redefined('s');
2877bdc2678Schristos state = a;
2887bdc2678Schristos }
2897bdc2678Schristos break;
2907bdc2678Schristos
2917bdc2678Schristos case 'T':
2927bdc2678Schristos if (*a)
2937bdc2678Schristos goto unknown;
2947bdc2678Schristos Ttimeflag = true;
2957bdc2678Schristos break;
2967bdc2678Schristos
2977bdc2678Schristos case 'w':
2987bdc2678Schristos if (author) redefined('w');
2997bdc2678Schristos if (*a)
3007bdc2678Schristos author = a;
3017bdc2678Schristos else
3027bdc2678Schristos author = getcaller();
3037bdc2678Schristos break;
3047bdc2678Schristos
3057bdc2678Schristos case 'x':
3067bdc2678Schristos suffixarg = *argv;
3077bdc2678Schristos suffixes = a;
3087bdc2678Schristos break;
3097bdc2678Schristos
3107bdc2678Schristos case 'V':
3117bdc2678Schristos versionarg = *argv;
3127bdc2678Schristos setRCSversion(versionarg);
3137bdc2678Schristos break;
3147bdc2678Schristos
3157bdc2678Schristos case 'z':
3167bdc2678Schristos zonearg = *argv;
3177bdc2678Schristos zone_set(a);
3187bdc2678Schristos break;
3197bdc2678Schristos
3207bdc2678Schristos case 'k': /* set keyword expand mode */
3217bdc2678Schristos expandarg = *argv;
3227bdc2678Schristos if (0 <= expmode) redefined('k');
3237bdc2678Schristos if (0 <= (expmode = str2expmode(a)))
3247bdc2678Schristos break;
3257bdc2678Schristos /* fall into */
3267bdc2678Schristos default:
3277bdc2678Schristos unknown:
3287bdc2678Schristos error("unknown option: %s%s", *argv, cmdusage);
3297bdc2678Schristos
3307bdc2678Schristos };
3317bdc2678Schristos } /* end of option processing */
3327bdc2678Schristos
3337bdc2678Schristos /* Now handle all pathnames. */
3347bdc2678Schristos if (nerror) cleanup();
3357bdc2678Schristos else if (argc < 1) faterror("no input file%s", cmdusage);
3367bdc2678Schristos else for (; 0 < argc; cleanup(), ++argv, --argc) {
3377bdc2678Schristos ffree();
3387bdc2678Schristos
3397bdc2678Schristos if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0)
3407bdc2678Schristos continue;
3417bdc2678Schristos
3427bdc2678Schristos /*
3437bdc2678Schristos * RCSname contains the name of the RCS file, and finptr
3447bdc2678Schristos * points at it. workname contains the name of the working file.
3457bdc2678Schristos * Also, RCSstat has been set.
3467bdc2678Schristos */
3477bdc2678Schristos diagnose("%s --> %s\n", RCSname, tostdout?"standard output":workname);
3487bdc2678Schristos
3497bdc2678Schristos workstatstat = -1;
3507bdc2678Schristos if (tostdout) {
3517bdc2678Schristos # if OPEN_O_BINARY
3527bdc2678Schristos int newmode = Expand==BINARY_EXPAND ? OPEN_O_BINARY : 0;
3537bdc2678Schristos if (stdout_mode != newmode) {
3547bdc2678Schristos stdout_mode = newmode;
3557bdc2678Schristos oflush();
3567bdc2678Schristos VOID setmode(STDOUT_FILENO, newmode);
3577bdc2678Schristos }
3587bdc2678Schristos # endif
3597bdc2678Schristos neworkname = 0;
3607bdc2678Schristos neworkptr = workstdout = stdout;
3617bdc2678Schristos } else {
3627bdc2678Schristos workstatstat = stat(workname, &workstat);
3637bdc2678Schristos if (workstatstat == 0 && same_file(RCSstat, workstat, 0)) {
3647bdc2678Schristos rcserror("RCS file is the same as working file %s.",
3657bdc2678Schristos workname
3667bdc2678Schristos );
3677bdc2678Schristos continue;
3687bdc2678Schristos }
3697bdc2678Schristos neworkname = makedirtemp(1);
3707bdc2678Schristos if (!(neworkptr = fopenSafer(neworkname, FOPEN_W_WORK))) {
3717bdc2678Schristos if (errno == EACCES)
3727bdc2678Schristos workerror("permission denied on parent directory");
3737bdc2678Schristos else
3747bdc2678Schristos eerror(neworkname);
3757bdc2678Schristos continue;
3767bdc2678Schristos }
3777bdc2678Schristos }
3787bdc2678Schristos
3797bdc2678Schristos gettree(); /* reads in the delta tree */
3807bdc2678Schristos
3817bdc2678Schristos if (!Head) {
3827bdc2678Schristos /* no revisions; create empty file */
3837bdc2678Schristos diagnose("no revisions present; generating empty revision 0.0\n");
3847bdc2678Schristos if (lockflag)
3857bdc2678Schristos warn(
3867bdc2678Schristos "no revisions, so nothing can be %slocked",
3877bdc2678Schristos lockflag < 0 ? "un" : ""
3887bdc2678Schristos );
3897bdc2678Schristos Ozclose(&fcopy);
3907bdc2678Schristos if (workstatstat == 0)
3917bdc2678Schristos if (!rmworkfile()) continue;
3927bdc2678Schristos changelock = 0;
3937bdc2678Schristos newdate = 0;
3947bdc2678Schristos } else {
3957bdc2678Schristos int locks = lockflag ? findlock(false, &targetdelta) : 0;
3967bdc2678Schristos if (rev) {
3977bdc2678Schristos /* expand symbolic revision number */
3987bdc2678Schristos if (!expandsym(rev, &numericrev))
3997bdc2678Schristos continue;
4007bdc2678Schristos } else {
4017bdc2678Schristos switch (locks) {
4027bdc2678Schristos default:
4037bdc2678Schristos continue;
4047bdc2678Schristos case 0:
4057bdc2678Schristos bufscpy(&numericrev, Dbranch?Dbranch:"");
4067bdc2678Schristos break;
4077bdc2678Schristos case 1:
4087bdc2678Schristos bufscpy(&numericrev, targetdelta->num);
4097bdc2678Schristos break;
4107bdc2678Schristos }
4117bdc2678Schristos }
4127bdc2678Schristos /* get numbers of deltas to be generated */
4137bdc2678Schristos if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas)))
4147bdc2678Schristos continue;
4157bdc2678Schristos /* check reservations */
4167bdc2678Schristos changelock =
4177bdc2678Schristos lockflag < 0 ?
4187bdc2678Schristos rmlock(targetdelta)
4197bdc2678Schristos : lockflag == 0 ?
4207bdc2678Schristos 0
4217bdc2678Schristos :
4227bdc2678Schristos addlock(targetdelta, true);
4237bdc2678Schristos
4247bdc2678Schristos if (
4257bdc2678Schristos changelock < 0
4267bdc2678Schristos || (changelock && !checkaccesslist())
4277bdc2678Schristos || dorewrite(lockflag, changelock) != 0
4287bdc2678Schristos )
4297bdc2678Schristos continue;
4307bdc2678Schristos
4317bdc2678Schristos if (0 <= expmode)
4327bdc2678Schristos Expand = expmode;
4337bdc2678Schristos if (0 < lockflag && Expand == VAL_EXPAND) {
4347bdc2678Schristos rcserror("cannot combine -kv and -l");
4357bdc2678Schristos continue;
4367bdc2678Schristos }
4377bdc2678Schristos
4387bdc2678Schristos if (joinflag && !preparejoin(joinflag))
4397bdc2678Schristos continue;
4407bdc2678Schristos
4417bdc2678Schristos diagnose("revision %s%s\n",targetdelta->num,
4427bdc2678Schristos 0<lockflag ? " (locked)" :
4437bdc2678Schristos lockflag<0 ? " (unlocked)" : "");
4447bdc2678Schristos
4457bdc2678Schristos /* Prepare to remove old working file if necessary. */
4467bdc2678Schristos if (workstatstat == 0)
4477bdc2678Schristos if (!rmworkfile()) continue;
4487bdc2678Schristos
4497bdc2678Schristos /* skip description */
4507bdc2678Schristos getdesc(false); /* don't echo*/
4517bdc2678Schristos
4527bdc2678Schristos locker_expansion = 0 < lockflag;
4537bdc2678Schristos targetdelta->name = namedrev(rev, targetdelta);
4547bdc2678Schristos joinname = buildrevision(
4557bdc2678Schristos gendeltas, targetdelta,
4567bdc2678Schristos joinflag&&tostdout ? (FILE*)0 : neworkptr,
4577bdc2678Schristos Expand < MIN_UNEXPAND
4587bdc2678Schristos );
4597bdc2678Schristos # if !large_memory
4607bdc2678Schristos if (fcopy == neworkptr)
4617bdc2678Schristos fcopy = 0; /* Don't close it twice. */
4627bdc2678Schristos # endif
4637bdc2678Schristos if_advise_access(changelock && gendeltas->first!=targetdelta,
4647bdc2678Schristos finptr, MADV_SEQUENTIAL
4657bdc2678Schristos );
4667bdc2678Schristos
4677bdc2678Schristos if (donerewrite(changelock,
4687bdc2678Schristos Ttimeflag ? RCSstat.st_mtime : (time_t)-1
4697bdc2678Schristos ) != 0)
4707bdc2678Schristos continue;
4717bdc2678Schristos
4727bdc2678Schristos if (changelock) {
4737bdc2678Schristos locks += lockflag;
4747bdc2678Schristos if (1 < locks)
4757bdc2678Schristos rcswarn("You now have %d locks.", locks);
4767bdc2678Schristos }
4777bdc2678Schristos
4787bdc2678Schristos newdate = targetdelta->date;
4797bdc2678Schristos if (joinflag) {
4807bdc2678Schristos newdate = 0;
4817bdc2678Schristos if (!joinname) {
4827bdc2678Schristos aflush(neworkptr);
4837bdc2678Schristos joinname = neworkname;
4847bdc2678Schristos }
4857bdc2678Schristos if (Expand == BINARY_EXPAND)
4867bdc2678Schristos workerror("merging binary files");
4877bdc2678Schristos if (!buildjoin(joinname))
4887bdc2678Schristos continue;
4897bdc2678Schristos }
4907bdc2678Schristos }
4917bdc2678Schristos if (!tostdout) {
4927bdc2678Schristos mode_t m = WORKMODE(RCSstat.st_mode,
4937bdc2678Schristos ! (Expand==VAL_EXPAND || (lockflag<=0 && StrictLocks))
4947bdc2678Schristos );
4957bdc2678Schristos time_t t = mtimeflag&&newdate ? date2time(newdate) : (time_t)-1;
4967bdc2678Schristos aflush(neworkptr);
4977bdc2678Schristos ignoreints();
4987bdc2678Schristos r = chnamemod(&neworkptr, neworkname, workname, 1, m, t);
4997bdc2678Schristos keepdirtemp(neworkname);
5007bdc2678Schristos restoreints();
5017bdc2678Schristos if (r != 0) {
5027bdc2678Schristos eerror(workname);
5037bdc2678Schristos error("see %s", neworkname);
5047bdc2678Schristos continue;
5057bdc2678Schristos }
5067bdc2678Schristos diagnose("done\n");
5077bdc2678Schristos }
5087bdc2678Schristos }
5097bdc2678Schristos
5107bdc2678Schristos tempunlink();
5117bdc2678Schristos Ofclose(workstdout);
5127bdc2678Schristos exitmain(exitstatus);
5137bdc2678Schristos
5147bdc2678Schristos } /* end of main (co) */
5157bdc2678Schristos
5167bdc2678Schristos static void
cleanup()5177bdc2678Schristos cleanup()
5187bdc2678Schristos {
5197bdc2678Schristos if (nerror) exitstatus = EXIT_FAILURE;
5207bdc2678Schristos Izclose(&finptr);
5217bdc2678Schristos ORCSclose();
5227bdc2678Schristos # if !large_memory
5237bdc2678Schristos if (fcopy!=workstdout) Ozclose(&fcopy);
5247bdc2678Schristos # endif
5257bdc2678Schristos if (neworkptr!=workstdout) Ozclose(&neworkptr);
5267bdc2678Schristos dirtempunlink();
5277bdc2678Schristos }
5287bdc2678Schristos
5297bdc2678Schristos #if RCS_lint
5307bdc2678Schristos # define exiterr coExit
5317bdc2678Schristos #endif
5327bdc2678Schristos void
exiterr()5337bdc2678Schristos exiterr()
5347bdc2678Schristos {
5357bdc2678Schristos ORCSerror();
5367bdc2678Schristos dirtempunlink();
5377bdc2678Schristos tempunlink();
5387bdc2678Schristos _exit(EXIT_FAILURE);
5397bdc2678Schristos }
5407bdc2678Schristos
5417bdc2678Schristos
5427bdc2678Schristos /*****************************************************************
5437bdc2678Schristos * The following routines are auxiliary routines
5447bdc2678Schristos *****************************************************************/
5457bdc2678Schristos
5467bdc2678Schristos static int
rmworkfile()5477bdc2678Schristos rmworkfile()
5487bdc2678Schristos /*
5497bdc2678Schristos * Prepare to remove workname, if it exists, and if
5507bdc2678Schristos * it is read-only.
5517bdc2678Schristos * Otherwise (file writable):
5527bdc2678Schristos * if !quietmode asks the user whether to really delete it (default: fail);
5537bdc2678Schristos * otherwise failure.
5547bdc2678Schristos * Returns true if permission is gotten.
5557bdc2678Schristos */
5567bdc2678Schristos {
5577bdc2678Schristos if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) {
5587bdc2678Schristos /* File is writable */
5597bdc2678Schristos if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ",
5607bdc2678Schristos workname,
5617bdc2678Schristos myself(workstat.st_uid) ? "" : ", and you do not own it"
5627bdc2678Schristos )) {
5637bdc2678Schristos error(!quietflag && ttystdin()
5647bdc2678Schristos ? "checkout aborted"
5657bdc2678Schristos : "writable %s exists; checkout aborted", workname);
5667bdc2678Schristos return false;
5677bdc2678Schristos }
5687bdc2678Schristos }
5697bdc2678Schristos /* Actual unlink is done later by caller. */
5707bdc2678Schristos return true;
5717bdc2678Schristos }
5727bdc2678Schristos
5737bdc2678Schristos
5747bdc2678Schristos static int
rmlock(delta)5757bdc2678Schristos rmlock(delta)
5767bdc2678Schristos struct hshentry const *delta;
5777bdc2678Schristos /* Function: removes the lock held by caller on delta.
5787bdc2678Schristos * Returns -1 if someone else holds the lock,
5797bdc2678Schristos * 0 if there is no lock on delta,
5807bdc2678Schristos * and 1 if a lock was found and removed.
5817bdc2678Schristos */
5827bdc2678Schristos { register struct rcslock * next, * trail;
5837bdc2678Schristos char const *num;
5847bdc2678Schristos struct rcslock dummy;
5857bdc2678Schristos int whomatch, nummatch;
5867bdc2678Schristos
5877bdc2678Schristos num=delta->num;
5887bdc2678Schristos dummy.nextlock=next=Locks;
5897bdc2678Schristos trail = &dummy;
5907bdc2678Schristos while (next) {
5917bdc2678Schristos whomatch = strcmp(getcaller(), next->login);
5927bdc2678Schristos nummatch=strcmp(num,next->delta->num);
5937bdc2678Schristos if ((whomatch==0) && (nummatch==0)) break;
5947bdc2678Schristos /*found a lock on delta by caller*/
5957bdc2678Schristos if ((whomatch!=0)&&(nummatch==0)) {
5967bdc2678Schristos rcserror("revision %s locked by %s; use co -r or rcs -u",
5977bdc2678Schristos num, next->login
5987bdc2678Schristos );
5997bdc2678Schristos return -1;
6007bdc2678Schristos }
6017bdc2678Schristos trail=next;
6027bdc2678Schristos next=next->nextlock;
6037bdc2678Schristos }
6047bdc2678Schristos if (next) {
6057bdc2678Schristos /*found one; delete it */
6067bdc2678Schristos trail->nextlock=next->nextlock;
6077bdc2678Schristos Locks=dummy.nextlock;
6087bdc2678Schristos next->delta->lockedby = 0;
6097bdc2678Schristos return 1; /*success*/
6107bdc2678Schristos } else return 0; /*no lock on delta*/
6117bdc2678Schristos }
6127bdc2678Schristos
6137bdc2678Schristos
6147bdc2678Schristos
6157bdc2678Schristos
6167bdc2678Schristos /*****************************************************************
6177bdc2678Schristos * The rest of the routines are for handling joins
6187bdc2678Schristos *****************************************************************/
6197bdc2678Schristos
6207bdc2678Schristos
6217bdc2678Schristos static char *
addjoin(joinrev)6227bdc2678Schristos addjoin(joinrev)
6237bdc2678Schristos char *joinrev;
6247bdc2678Schristos /* Add joinrev's number to joinlist, yielding address of char past joinrev,
6257bdc2678Schristos * or 0 if no such revision exists.
6267bdc2678Schristos */
6277bdc2678Schristos {
6287bdc2678Schristos register char *j;
6297bdc2678Schristos register struct hshentry *d;
6307bdc2678Schristos char terminator;
6317bdc2678Schristos struct buf numrev;
6327bdc2678Schristos struct hshentries *joindeltas;
6337bdc2678Schristos
6347bdc2678Schristos j = joinrev;
6357bdc2678Schristos for (;;) {
6367bdc2678Schristos switch (*j++) {
6377bdc2678Schristos default:
6387bdc2678Schristos continue;
6397bdc2678Schristos case 0:
6407bdc2678Schristos case ' ': case '\t': case '\n':
6417bdc2678Schristos case ':': case ',': case ';':
6427bdc2678Schristos break;
6437bdc2678Schristos }
6447bdc2678Schristos break;
6457bdc2678Schristos }
6467bdc2678Schristos terminator = *--j;
6477bdc2678Schristos *j = 0;
6487bdc2678Schristos bufautobegin(&numrev);
6497bdc2678Schristos d = 0;
6507bdc2678Schristos if (expandsym(joinrev, &numrev))
6517bdc2678Schristos d = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&joindeltas);
6527bdc2678Schristos bufautoend(&numrev);
6537bdc2678Schristos *j = terminator;
6547bdc2678Schristos if (d) {
6557bdc2678Schristos joinlist[++lastjoin] = d->num;
6567bdc2678Schristos return j;
6577bdc2678Schristos }
6587bdc2678Schristos return 0;
6597bdc2678Schristos }
6607bdc2678Schristos
6617bdc2678Schristos static int
preparejoin(j)6627bdc2678Schristos preparejoin(j)
6637bdc2678Schristos register char *j;
6647bdc2678Schristos /* Parse join list J and place pointers to the
6657bdc2678Schristos * revision numbers into joinlist.
6667bdc2678Schristos */
6677bdc2678Schristos {
6687bdc2678Schristos lastjoin= -1;
6697bdc2678Schristos for (;;) {
6707bdc2678Schristos while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
6717bdc2678Schristos if (*j=='\0') break;
6727bdc2678Schristos if (lastjoin>=joinlength-2) {
6737bdc2678Schristos joinlist =
6747bdc2678Schristos (joinlength *= 2) == 0
6757bdc2678Schristos ? tnalloc(char const *, joinlength = 16)
6767bdc2678Schristos : trealloc(char const *, joinlist, joinlength);
6777bdc2678Schristos }
6787bdc2678Schristos if (!(j = addjoin(j))) return false;
6797bdc2678Schristos while ((*j==' ') || (*j=='\t')) j++;
6807bdc2678Schristos if (*j == ':') {
6817bdc2678Schristos j++;
6827bdc2678Schristos while((*j==' ') || (*j=='\t')) j++;
6837bdc2678Schristos if (*j!='\0') {
6847bdc2678Schristos if (!(j = addjoin(j))) return false;
6857bdc2678Schristos } else {
6867bdc2678Schristos rcsfaterror("join pair incomplete");
6877bdc2678Schristos }
6887bdc2678Schristos } else {
6897bdc2678Schristos if (lastjoin==0) { /* first pair */
6907bdc2678Schristos /* common ancestor missing */
6917bdc2678Schristos joinlist[1]=joinlist[0];
6927bdc2678Schristos lastjoin=1;
6937bdc2678Schristos /*derive common ancestor*/
6947bdc2678Schristos if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1])))
6957bdc2678Schristos return false;
6967bdc2678Schristos } else {
6977bdc2678Schristos rcsfaterror("join pair incomplete");
6987bdc2678Schristos }
6997bdc2678Schristos }
7007bdc2678Schristos }
7017bdc2678Schristos if (lastjoin < 1)
7027bdc2678Schristos rcsfaterror("empty join");
7037bdc2678Schristos return true;
7047bdc2678Schristos }
7057bdc2678Schristos
7067bdc2678Schristos
7077bdc2678Schristos
7087bdc2678Schristos static char const *
getancestor(r1,r2)7097bdc2678Schristos getancestor(r1, r2)
7107bdc2678Schristos char const *r1, *r2;
7117bdc2678Schristos /* Yield the common ancestor of r1 and r2 if successful, 0 otherwise.
7127bdc2678Schristos * Work reliably only if r1 and r2 are not branch numbers.
7137bdc2678Schristos */
7147bdc2678Schristos {
7157bdc2678Schristos static struct buf t1, t2;
7167bdc2678Schristos
7177bdc2678Schristos int l1, l2, l3;
7187bdc2678Schristos char const *r;
7197bdc2678Schristos
7207bdc2678Schristos l1 = countnumflds(r1);
7217bdc2678Schristos l2 = countnumflds(r2);
7227bdc2678Schristos if ((2<l1 || 2<l2) && cmpnum(r1,r2)!=0) {
7237bdc2678Schristos /* not on main trunk or identical */
7247bdc2678Schristos l3 = 0;
7257bdc2678Schristos while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0)
7267bdc2678Schristos l3 += 2;
7277bdc2678Schristos /* This will terminate since r1 and r2 are not the same; see above. */
7287bdc2678Schristos if (l3==0) {
7297bdc2678Schristos /* no common prefix; common ancestor on main trunk */
7307bdc2678Schristos VOID partialno(&t1, r1, l1>2 ? 2 : l1);
7317bdc2678Schristos VOID partialno(&t2, r2, l2>2 ? 2 : l2);
7327bdc2678Schristos r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string;
7337bdc2678Schristos if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0)
7347bdc2678Schristos return r;
7357bdc2678Schristos } else if (cmpnumfld(r1, r2, l3+1)!=0)
7367bdc2678Schristos return partialno(&t1,r1,l3);
7377bdc2678Schristos }
7387bdc2678Schristos rcserror("common ancestor of %s and %s undefined", r1, r2);
7397bdc2678Schristos return 0;
7407bdc2678Schristos }
7417bdc2678Schristos
7427bdc2678Schristos
7437bdc2678Schristos
7447bdc2678Schristos static int
buildjoin(initialfile)7457bdc2678Schristos buildjoin(initialfile)
7467bdc2678Schristos char const *initialfile;
7477bdc2678Schristos /* Function: merge pairs of elements in joinlist into initialfile
7487bdc2678Schristos * If workstdout is set, copy result to stdout.
7497bdc2678Schristos * All unlinking of initialfile, rev2, and rev3 should be done by tempunlink().
7507bdc2678Schristos */
7517bdc2678Schristos {
7527bdc2678Schristos struct buf commarg;
7537bdc2678Schristos struct buf subs;
7547bdc2678Schristos char const *rev2, *rev3;
7557bdc2678Schristos int i;
7567bdc2678Schristos char const *cov[10], *mergev[11];
7577bdc2678Schristos char const **p;
7587bdc2678Schristos
7597bdc2678Schristos bufautobegin(&commarg);
7607bdc2678Schristos bufautobegin(&subs);
7617bdc2678Schristos rev2 = maketemp(0);
7627bdc2678Schristos rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */
7637bdc2678Schristos
7647bdc2678Schristos cov[1] = CO;
7657bdc2678Schristos /* cov[2] setup below */
7667bdc2678Schristos p = &cov[3];
7677bdc2678Schristos if (expandarg) *p++ = expandarg;
7687bdc2678Schristos if (suffixarg) *p++ = suffixarg;
7697bdc2678Schristos if (versionarg) *p++ = versionarg;
7707bdc2678Schristos if (zonearg) *p++ = zonearg;
7717bdc2678Schristos *p++ = quietarg;
7727bdc2678Schristos *p++ = RCSname;
7737bdc2678Schristos *p = 0;
7747bdc2678Schristos
7757bdc2678Schristos mergev[1] = MERGE;
7767bdc2678Schristos mergev[2] = mergev[4] = "-L";
7777bdc2678Schristos /* rest of mergev setup below */
7787bdc2678Schristos
7797bdc2678Schristos i=0;
7807bdc2678Schristos while (i<lastjoin) {
7817bdc2678Schristos /*prepare marker for merge*/
7827bdc2678Schristos if (i==0)
7837bdc2678Schristos bufscpy(&subs, targetdelta->num);
7847bdc2678Schristos else {
7857bdc2678Schristos bufscat(&subs, ",");
7867bdc2678Schristos bufscat(&subs, joinlist[i-2]);
7877bdc2678Schristos bufscat(&subs, ":");
7887bdc2678Schristos bufscat(&subs, joinlist[i-1]);
7897bdc2678Schristos }
7907bdc2678Schristos diagnose("revision %s\n",joinlist[i]);
7917bdc2678Schristos bufscpy(&commarg, "-p");
7927bdc2678Schristos bufscat(&commarg, joinlist[i]);
7937bdc2678Schristos cov[2] = commarg.string;
7947bdc2678Schristos if (runv(-1, rev2, cov))
7957bdc2678Schristos goto badmerge;
7967bdc2678Schristos diagnose("revision %s\n",joinlist[i+1]);
7977bdc2678Schristos bufscpy(&commarg, "-p");
7987bdc2678Schristos bufscat(&commarg, joinlist[i+1]);
7997bdc2678Schristos cov[2] = commarg.string;
8007bdc2678Schristos if (runv(-1, rev3, cov))
8017bdc2678Schristos goto badmerge;
8027bdc2678Schristos diagnose("merging...\n");
8037bdc2678Schristos mergev[3] = subs.string;
8047bdc2678Schristos mergev[5] = joinlist[i+1];
8057bdc2678Schristos p = &mergev[6];
8067bdc2678Schristos if (quietflag) *p++ = quietarg;
8077bdc2678Schristos if (lastjoin<=i+2 && workstdout) *p++ = "-p";
8087bdc2678Schristos *p++ = initialfile;
8097bdc2678Schristos *p++ = rev2;
8107bdc2678Schristos *p++ = rev3;
8117bdc2678Schristos *p = 0;
8127bdc2678Schristos switch (runv(-1, (char*)0, mergev)) {
8137bdc2678Schristos case DIFF_FAILURE: case DIFF_SUCCESS:
8147bdc2678Schristos break;
8157bdc2678Schristos default:
8167bdc2678Schristos goto badmerge;
8177bdc2678Schristos }
8187bdc2678Schristos i=i+2;
8197bdc2678Schristos }
8207bdc2678Schristos bufautoend(&commarg);
8217bdc2678Schristos bufautoend(&subs);
8227bdc2678Schristos return true;
8237bdc2678Schristos
8247bdc2678Schristos badmerge:
8257bdc2678Schristos nerror++;
8267bdc2678Schristos bufautoend(&commarg);
8277bdc2678Schristos bufautoend(&subs);
8287bdc2678Schristos return false;
8297bdc2678Schristos }
830