ci.c revision 9
19Sjkh/* Copyright (C) 1982, 1988, 1989 Walter Tichy 29Sjkh Copyright 1990, 1991 by Paul Eggert 39Sjkh Distributed under license by the Free Software Foundation, Inc. 49Sjkh 59SjkhThis file is part of RCS. 69Sjkh 79SjkhRCS is free software; you can redistribute it and/or modify 89Sjkhit under the terms of the GNU General Public License as published by 99Sjkhthe Free Software Foundation; either version 2, or (at your option) 109Sjkhany later version. 119Sjkh 129SjkhRCS is distributed in the hope that it will be useful, 139Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of 149SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 159SjkhGNU General Public License for more details. 169Sjkh 179SjkhYou should have received a copy of the GNU General Public License 189Sjkhalong with RCS; see the file COPYING. If not, write to 199Sjkhthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 209Sjkh 219SjkhReport problems and direct all questions to: 229Sjkh 239Sjkh rcs-bugs@cs.purdue.edu 249Sjkh 259Sjkh*/ 269Sjkh 279Sjkh/* 289Sjkh * RCS checkin operation 299Sjkh */ 309Sjkh/******************************************************************* 319Sjkh * check revisions into RCS files 329Sjkh ******************************************************************* 339Sjkh */ 349Sjkh 359Sjkh 369Sjkh 379Sjkh/* $Log: ci.c,v $ 389Sjkh * Revision 5.21 1991/11/20 17:58:07 eggert 399Sjkh * Don't read the delta tree from a nonexistent RCS file. 409Sjkh * 419Sjkh * Revision 5.20 1991/10/07 17:32:46 eggert 429Sjkh * Fix log bugs. Remove lint. 439Sjkh * 449Sjkh * Revision 5.19 1991/09/26 23:10:30 eggert 459Sjkh * Plug file descriptor leak. 469Sjkh * 479Sjkh * Revision 5.18 1991/09/18 07:29:10 eggert 489Sjkh * Work around a common ftruncate() bug. 499Sjkh * 509Sjkh * Revision 5.17 1991/09/10 22:15:46 eggert 519Sjkh * Fix test for redirected stdin. 529Sjkh * 539Sjkh * Revision 5.16 1991/08/19 23:17:54 eggert 549Sjkh * When there are no changes, revert to previous revision instead of aborting. 559Sjkh * Add piece tables, -M, -r$. Tune. 569Sjkh * 579Sjkh * Revision 5.15 1991/04/21 11:58:14 eggert 589Sjkh * Ensure that working file is newer than RCS file after ci -[lu]. 599Sjkh * Add -x, RCSINIT, MS-DOS support. 609Sjkh * 619Sjkh * Revision 5.14 1991/02/28 19:18:47 eggert 629Sjkh * Don't let a setuid ci create a new RCS file; rcs -i -a must be run first. 639Sjkh * Fix ci -ko -l mode bug. Open work file at most once. 649Sjkh * 659Sjkh * Revision 5.13 1991/02/25 07:12:33 eggert 669Sjkh * getdate -> getcurdate (SVR4 name clash) 679Sjkh * 689Sjkh * Revision 5.12 1990/12/31 01:00:12 eggert 699Sjkh * Don't use uninitialized storage when handling -{N,n}. 709Sjkh * 719Sjkh * Revision 5.11 1990/12/04 05:18:36 eggert 729Sjkh * Use -I for prompts and -q for diagnostics. 739Sjkh * 749Sjkh * Revision 5.10 1990/11/05 20:30:10 eggert 759Sjkh * Don't remove working file when aborting due to no changes. 769Sjkh * 779Sjkh * Revision 5.9 1990/11/01 05:03:23 eggert 789Sjkh * Add -I and new -t behavior. Permit arbitrary data in logs. 799Sjkh * 809Sjkh * Revision 5.8 1990/10/04 06:30:09 eggert 819Sjkh * Accumulate exit status across files. 829Sjkh * 839Sjkh * Revision 5.7 1990/09/25 20:11:46 hammer 849Sjkh * fixed another small typo 859Sjkh * 869Sjkh * Revision 5.6 1990/09/24 21:48:50 hammer 879Sjkh * added cleanups from Paul Eggert. 889Sjkh * 899Sjkh * Revision 5.5 1990/09/21 06:16:38 hammer 909Sjkh * made it handle multiple -{N,n}'s. Also, made it treat re-directed stdin 919Sjkh * the same as the terminal 929Sjkh * 939Sjkh * Revision 5.4 1990/09/20 02:38:51 eggert 949Sjkh * ci -k now checks dates more thoroughly. 959Sjkh * 969Sjkh * Revision 5.3 1990/09/11 02:41:07 eggert 979Sjkh * Fix revision bug with `ci -k file1 file2'. 989Sjkh * 999Sjkh * Revision 5.2 1990/09/04 08:02:10 eggert 1009Sjkh * Permit adjacent revisions with identical time stamps (possible on fast hosts). 1019Sjkh * Improve incomplete line handling. Standardize yes-or-no procedure. 1029Sjkh * 1039Sjkh * Revision 5.1 1990/08/29 07:13:44 eggert 1049Sjkh * Expand locker value like co. Clean old log messages too. 1059Sjkh * 1069Sjkh * Revision 5.0 1990/08/22 08:10:00 eggert 1079Sjkh * Don't require a final newline. 1089Sjkh * Make lock and temp files faster and safer. 1099Sjkh * Remove compile-time limits; use malloc instead. 1109Sjkh * Permit dates past 1999/12/31. Switch to GMT. 1119Sjkh * Add setuid support. Don't pass +args to diff. Check diff's output. 1129Sjkh * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. 1139Sjkh * Check diff's output. 1149Sjkh * 1159Sjkh * Revision 4.9 89/05/01 15:10:54 narten 1169Sjkh * changed copyright header to reflect current distribution rules 1179Sjkh * 1189Sjkh * Revision 4.8 88/11/08 13:38:23 narten 1199Sjkh * changes from root@seismo.CSS.GOV (Super User) 1209Sjkh * -d with no arguments uses the mod time of the file it is checking in 1219Sjkh * 1229Sjkh * Revision 4.7 88/08/09 19:12:07 eggert 1239Sjkh * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one. 1249Sjkh * Use execv(), not system(); allow cc -R; remove lint. 1259Sjkh * isatty(fileno(stdin)) -> ttystdin() 1269Sjkh * 1279Sjkh * Revision 4.6 87/12/18 11:34:41 narten 1289Sjkh * lint cleanups (from Guy Harris) 1299Sjkh * 1309Sjkh * Revision 4.5 87/10/18 10:18:48 narten 1319Sjkh * Updating version numbers. Changes relative to revision 1.1 are actually 1329Sjkh * relative to 4.3 1339Sjkh * 1349Sjkh * Revision 1.3 87/09/24 13:57:19 narten 1359Sjkh * Sources now pass through lint (if you ignore printf/sprintf/fprintf 1369Sjkh * warnings) 1379Sjkh * 1389Sjkh * Revision 1.2 87/03/27 14:21:33 jenkins 1399Sjkh * Port to suns 1409Sjkh * 1419Sjkh * Revision 4.3 83/12/15 12:28:54 wft 1429Sjkh * ci -u and ci -l now set mode of working file properly. 1439Sjkh * 1449Sjkh * Revision 4.2 83/12/05 13:40:54 wft 1459Sjkh * Merged with 3.9.1.1: added calls to clearerr(stdin). 1469Sjkh * made rewriteflag external. 1479Sjkh * 1489Sjkh * Revision 4.1 83/05/10 17:03:06 wft 1499Sjkh * Added option -d and -w, and updated assingment of date, etc. to new delta. 1509Sjkh * Added handling of default branches. 1519Sjkh * Option -k generates std. log message; fixed undef. pointer in reading of log. 1529Sjkh * Replaced getlock() with findlock(), link--unlink with rename(), 1539Sjkh * getpwuid() with getcaller(). 1549Sjkh * Moved all revision number generation to new routine addelta(). 1559Sjkh * Removed calls to stat(); now done by pairfilenames(). 1569Sjkh * Changed most calls to catchints() with restoreints(). 1579Sjkh * Directed all interactive messages to stderr. 1589Sjkh * 1599Sjkh * Revision 3.9.1.1 83/10/19 04:21:03 lepreau 1609Sjkh * Added clearerr(stdin) to getlogmsg() for re-reading stdin. 1619Sjkh * 1629Sjkh * Revision 3.9 83/02/15 15:25:44 wft 1639Sjkh * 4.2 prerelease 1649Sjkh * 1659Sjkh * Revision 3.9 83/02/15 15:25:44 wft 1669Sjkh * Added call to fastcopy() to copy remainder of RCS file. 1679Sjkh * 1689Sjkh * Revision 3.8 83/01/14 15:34:05 wft 1699Sjkh * Added ignoring of interrupts while new RCS file is renamed; 1709Sjkh * Avoids deletion of RCS files by interrupts. 1719Sjkh * 1729Sjkh * Revision 3.7 82/12/10 16:09:20 wft 1739Sjkh * Corrected checking of return code from diff. 1749Sjkh * 1759Sjkh * Revision 3.6 82/12/08 21:34:49 wft 1769Sjkh * Using DATEFORM to prepare date of checked-in revision; 1779Sjkh * Fixed return from addbranch(). 1789Sjkh * 1799Sjkh * Revision 3.5 82/12/04 18:32:42 wft 1809Sjkh * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated 1819Sjkh * field lockedby in removelock(), moved getlogmsg() before calling diff. 1829Sjkh * 1839Sjkh * Revision 3.4 82/12/02 13:27:13 wft 1849Sjkh * added option -k. 1859Sjkh * 1869Sjkh * Revision 3.3 82/11/28 20:53:31 wft 1879Sjkh * Added mustcheckin() to check for redundant checkins. 1889Sjkh * Added xpandfile() to do keyword expansion for -u and -l; 1899Sjkh * -m appends linefeed to log message if necessary. 1909Sjkh * getlogmsg() suppresses prompt if stdin is not a terminal. 1919Sjkh * Replaced keeplock with lockflag, fclose() with ffclose(), 1929Sjkh * %02d with %.2d, getlogin() with getpwuid(). 1939Sjkh * 1949Sjkh * Revision 3.2 82/10/18 20:57:23 wft 1959Sjkh * An RCS file inherits its mode during the first ci from the working file, 1969Sjkh * otherwise it stays the same, except that write permission is removed. 1979Sjkh * Fixed ci -l, added ci -u (both do an implicit co after the ci). 1989Sjkh * Fixed call to getlogin(), added call to getfullRCSname(), added check 1999Sjkh * for write error. 2009Sjkh * Changed conflicting identifiers. 2019Sjkh * 2029Sjkh * Revision 3.1 82/10/13 16:04:59 wft 2039Sjkh * fixed type of variables receiving from getc() (char -> int). 2049Sjkh * added include file dbm.h for getting BYTESIZ. This is used 2059Sjkh * to check the return code from diff portably. 2069Sjkh */ 2079Sjkh 2089Sjkh#include "rcsbase.h" 2099Sjkh 2109Sjkhstruct Symrev { 2119Sjkh char const *ssymbol; 2129Sjkh int override; 2139Sjkh struct Symrev * nextsym; 2149Sjkh}; 2159Sjkh 2169Sjkhstatic char const *getcurdate P((void)); 2179Sjkhstatic int addbranch P((struct hshentry*,struct buf*)); 2189Sjkhstatic int addelta P((void)); 2199Sjkhstatic int addsyms P((char const*)); 2209Sjkhstatic int fixwork P((mode_t,char const*)); 2219Sjkhstatic int removelock P((struct hshentry*)); 2229Sjkhstatic int xpandfile P((RILE*,char const*,struct hshentry const*,char const**)); 2239Sjkhstatic struct cbuf getlogmsg P((void)); 2249Sjkhstatic void cleanup P((void)); 2259Sjkhstatic void incnum P((char const*,struct buf*)); 2269Sjkhstatic void addassoclst P((int, char *)); 2279Sjkh 2289Sjkhstatic FILE *exfile; 2299Sjkhstatic RILE *workptr; /* working file pointer */ 2309Sjkhstatic struct buf newdelnum; /* new revision number */ 2319Sjkhstatic struct cbuf msg; 2329Sjkhstatic int exitstatus; 2339Sjkhstatic int forceciflag; /* forces check in */ 2349Sjkhstatic int keepflag, keepworkingfile, rcsinitflag; 2359Sjkhstatic struct hshentries *gendeltas; /* deltas to be generated */ 2369Sjkhstatic struct hshentry *targetdelta; /* old delta to be generated */ 2379Sjkhstatic struct hshentry newdelta; /* new delta to be inserted */ 2389Sjkhstatic struct stat workstat; 2399Sjkhstatic struct Symrev *assoclst, *lastassoc; 2409Sjkh 2419SjkhmainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") 2429Sjkh{ 2439Sjkh static char const cmdusage[] = 2449Sjkh "\nci usage: ci -{fklqru}[rev] -mmsg -{nN}name -sstate -t[textfile] -Vn file ..."; 2459Sjkh static char const default_state[] = DEFAULTSTATE; 2469Sjkh 2479Sjkh char altdate[datesize]; 2489Sjkh char olddate[datesize]; 2499Sjkh char newdatebuf[datesize], targetdatebuf[datesize]; 2509Sjkh char *a, **newargv, *textfile; 2519Sjkh char const *author, *krev, *rev, *state; 2529Sjkh char const *diffilename, *expfilename; 2539Sjkh char const *workdiffname, *newworkfilename; 2549Sjkh char const *mtime; 2559Sjkh int lockflag, lockthis, mtimeflag, removedlock; 2569Sjkh int r; 2579Sjkh int changedRCS, changework, newhead; 2589Sjkh int usestatdate; /* Use mod time of file for -d. */ 2599Sjkh mode_t newworkmode; /* mode for working file */ 2609Sjkh struct hshentry *workdelta; 2619Sjkh 2629Sjkh setrid(); 2639Sjkh 2649Sjkh author = rev = state = textfile = nil; 2659Sjkh lockflag = false; 2669Sjkh mtimeflag = false; 2679Sjkh altdate[0]= '\0'; /* empty alternate date for -d */ 2689Sjkh usestatdate=false; 2699Sjkh suffixes = X_DEFAULT; 2709Sjkh 2719Sjkh argc = getRCSINIT(argc, argv, &newargv); 2729Sjkh argv = newargv; 2739Sjkh while (a = *++argv, 0<--argc && *a++=='-') { 2749Sjkh switch (*a++) { 2759Sjkh 2769Sjkh case 'r': 2779Sjkh keepworkingfile = lockflag = false; 2789Sjkh revno: 2799Sjkh if (*a) { 2809Sjkh if (rev) warn("redefinition of revision number"); 2819Sjkh rev = a; 2829Sjkh } 2839Sjkh break; 2849Sjkh 2859Sjkh case 'l': 2869Sjkh keepworkingfile=lockflag=true; 2879Sjkh goto revno; 2889Sjkh 2899Sjkh case 'u': 2909Sjkh keepworkingfile=true; lockflag=false; 2919Sjkh goto revno; 2929Sjkh 2939Sjkh case 'I': 2949Sjkh interactiveflag = true; 2959Sjkh goto revno; 2969Sjkh 2979Sjkh case 'q': 2989Sjkh quietflag=true; 2999Sjkh goto revno; 3009Sjkh 3019Sjkh case 'f': 3029Sjkh forceciflag=true; 3039Sjkh goto revno; 3049Sjkh 3059Sjkh case 'k': 3069Sjkh keepflag=true; 3079Sjkh goto revno; 3089Sjkh 3099Sjkh case 'm': 3109Sjkh if (msg.size) redefined('m'); 3119Sjkh msg = cleanlogmsg(a, strlen(a)); 3129Sjkh if (!msg.size) 3139Sjkh warn("missing message for -m option"); 3149Sjkh break; 3159Sjkh 3169Sjkh case 'n': 3179Sjkh if (!*a) { 3189Sjkh error("missing symbolic name after -n"); 3199Sjkh break; 3209Sjkh } 3219Sjkh checksid(a); 3229Sjkh addassoclst(false, a); 3239Sjkh break; 3249Sjkh 3259Sjkh case 'N': 3269Sjkh if (!*a) { 3279Sjkh error("missing symbolic name after -N"); 3289Sjkh break; 3299Sjkh } 3309Sjkh checksid(a); 3319Sjkh addassoclst(true, a); 3329Sjkh break; 3339Sjkh 3349Sjkh case 's': 3359Sjkh if (*a) { 3369Sjkh if (state) redefined('s'); 3379Sjkh checksid(a); 3389Sjkh state = a; 3399Sjkh } else 3409Sjkh warn("missing state for -s option"); 3419Sjkh break; 3429Sjkh 3439Sjkh case 't': 3449Sjkh if (*a) { 3459Sjkh if (textfile) redefined('t'); 3469Sjkh textfile = a; 3479Sjkh } 3489Sjkh break; 3499Sjkh 3509Sjkh case 'd': 3519Sjkh if (altdate[0] || usestatdate) 3529Sjkh redefined('d'); 3539Sjkh altdate[0] = 0; 3549Sjkh if (!(usestatdate = !*a)) 3559Sjkh str2date(a, altdate); 3569Sjkh break; 3579Sjkh 3589Sjkh case 'M': 3599Sjkh mtimeflag = true; 3609Sjkh goto revno; 3619Sjkh 3629Sjkh case 'w': 3639Sjkh if (*a) { 3649Sjkh if (author) redefined('w'); 3659Sjkh checksid(a); 3669Sjkh author = a; 3679Sjkh } else 3689Sjkh warn("missing author for -w option"); 3699Sjkh break; 3709Sjkh 3719Sjkh case 'x': 3729Sjkh suffixes = a; 3739Sjkh break; 3749Sjkh 3759Sjkh case 'V': 3769Sjkh setRCSversion(*argv); 3779Sjkh break; 3789Sjkh 3799Sjkh 3809Sjkh 3819Sjkh default: 3829Sjkh faterror("unknown option: %s%s", *argv, cmdusage); 3839Sjkh }; 3849Sjkh } /* end processing of options */ 3859Sjkh 3869Sjkh if (argc<1) faterror("no input file%s", cmdusage); 3879Sjkh 3889Sjkh /* now handle all filenames */ 3899Sjkh do { 3909Sjkh targetdelta=nil; 3919Sjkh ffree(); 3929Sjkh 3939Sjkh switch (pairfilenames(argc, argv, rcswriteopen, false, false)) { 3949Sjkh 3959Sjkh case -1: /* New RCS file */ 3969Sjkh# if has_setuid && has_getuid 3979Sjkh if (euid() != ruid()) { 3989Sjkh error("setuid initial checkin prohibited; use `rcs -i -a' first"); 3999Sjkh continue; 4009Sjkh } 4019Sjkh# endif 4029Sjkh rcsinitflag = true; 4039Sjkh break; 4049Sjkh 4059Sjkh case 0: /* Error */ 4069Sjkh continue; 4079Sjkh 4089Sjkh case 1: /* Normal checkin with prev . RCS file */ 4099Sjkh rcsinitflag = !Head; 4109Sjkh } 4119Sjkh 4129Sjkh /* now RCSfilename contains the name of the RCS file, and 4139Sjkh * workfilename contains the name of the working file. 4149Sjkh * If the RCS file exists, finptr contains the file descriptor for the 4159Sjkh * RCS file. The admin node is initialized. 4169Sjkh * RCSstat is set. 4179Sjkh */ 4189Sjkh 4199Sjkh diagnose("%s <-- %s\n", RCSfilename,workfilename); 4209Sjkh 4219Sjkh if (!(workptr = Iopen(workfilename, FOPEN_R_WORK, &workstat))) { 4229Sjkh eerror(workfilename); 4239Sjkh continue; 4249Sjkh } 4259Sjkh if (finptr && !checkaccesslist()) continue; /* give up */ 4269Sjkh 4279Sjkh krev = rev; 4289Sjkh if (keepflag) { 4299Sjkh /* get keyword values from working file */ 4309Sjkh if (!getoldkeys(workptr)) continue; 4319Sjkh if (!rev && !*(krev = prevrev.string)) { 4329Sjkh error("can't find a revision number in %s",workfilename); 4339Sjkh continue; 4349Sjkh } 4359Sjkh if (!*prevdate.string && *altdate=='\0' && usestatdate==false) 4369Sjkh warn("can't find a date in %s", workfilename); 4379Sjkh if (!*prevauthor.string && !author) 4389Sjkh warn("can't find an author in %s", workfilename); 4399Sjkh if (!*prevstate.string && !state) 4409Sjkh warn("can't find a state in %s", workfilename); 4419Sjkh } /* end processing keepflag */ 4429Sjkh 4439Sjkh /* Read the delta tree. */ 4449Sjkh if (finptr) 4459Sjkh gettree(); 4469Sjkh 4479Sjkh /* expand symbolic revision number */ 4489Sjkh if (!fexpandsym(krev, &newdelnum, workptr)) 4499Sjkh continue; 4509Sjkh 4519Sjkh /* splice new delta into tree */ 4529Sjkh if ((removedlock = addelta()) < 0) 4539Sjkh continue; 4549Sjkh 4559Sjkh newdelta.num = newdelnum.string; 4569Sjkh newdelta.branches=nil; 4579Sjkh newdelta.lockedby=nil; /*might be changed by addlock() */ 4589Sjkh newdelta.selector = true; 4599Sjkh /* set author */ 4609Sjkh if (author!=nil) 4619Sjkh newdelta.author=author; /* set author given by -w */ 4629Sjkh else if (keepflag && *prevauthor.string) 4639Sjkh newdelta.author=prevauthor.string; /* preserve old author if possible*/ 4649Sjkh else newdelta.author=getcaller();/* otherwise use caller's id */ 4659Sjkh newdelta.state = default_state; 4669Sjkh if (state!=nil) 4679Sjkh newdelta.state=state; /* set state given by -s */ 4689Sjkh else if (keepflag && *prevstate.string) 4699Sjkh newdelta.state=prevstate.string; /* preserve old state if possible */ 4709Sjkh if (usestatdate) { 4719Sjkh time2date(workstat.st_mtime, altdate); 4729Sjkh } 4739Sjkh if (*altdate!='\0') 4749Sjkh newdelta.date=altdate; /* set date given by -d */ 4759Sjkh else if (keepflag && *prevdate.string) { 4769Sjkh /* Preserve old date if possible. */ 4779Sjkh str2date(prevdate.string, olddate); 4789Sjkh newdelta.date = olddate; 4799Sjkh } else 4809Sjkh newdelta.date = getcurdate(); /* use current date */ 4819Sjkh /* now check validity of date -- needed because of -d and -k */ 4829Sjkh if (targetdelta!=nil && 4839Sjkh cmpnum(newdelta.date,targetdelta->date) < 0) { 4849Sjkh error("Date %s precedes %s in existing revision %s.", 4859Sjkh date2str(newdelta.date, newdatebuf), 4869Sjkh date2str(targetdelta->date, targetdatebuf), 4879Sjkh targetdelta->num 4889Sjkh ); 4899Sjkh continue; 4909Sjkh } 4919Sjkh 4929Sjkh 4939Sjkh if (lockflag && addlock(&newdelta) < 0) continue; 4949Sjkh if (!addsyms(newdelta.num)) 4959Sjkh continue; 4969Sjkh 4979Sjkh 4989Sjkh putadmin(frewrite); 4999Sjkh puttree(Head,frewrite); 5009Sjkh putdesc(false,textfile); 5019Sjkh 5029Sjkh changework = Expand != OLD_EXPAND; 5039Sjkh lockthis = lockflag; 5049Sjkh workdelta = &newdelta; 5059Sjkh 5069Sjkh /* build rest of file */ 5079Sjkh if (rcsinitflag) { 5089Sjkh diagnose("initial revision: %s\n", newdelnum.string); 5099Sjkh /* get logmessage */ 5109Sjkh newdelta.log=getlogmsg(); 5119Sjkh if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue; 5129Sjkh RCSstat.st_mode = workstat.st_mode; 5139Sjkh changedRCS = true; 5149Sjkh } else { 5159Sjkh diffilename = maketemp(0); 5169Sjkh workdiffname = workfilename; 5179Sjkh if (workdiffname[0] == '+') { 5189Sjkh /* Some diffs have options with leading '+'. */ 5199Sjkh char *dp = ftnalloc(char, strlen(workfilename)+3); 5209Sjkh workdiffname = dp; 5219Sjkh *dp++ = '.'; 5229Sjkh *dp++ = SLASH; 5239Sjkh VOID strcpy(dp, workfilename); 5249Sjkh } 5259Sjkh newhead = Head == &newdelta; 5269Sjkh if (!newhead) 5279Sjkh foutptr = frewrite; 5289Sjkh expfilename = buildrevision( 5299Sjkh gendeltas, targetdelta, (FILE*)0, false 5309Sjkh ); 5319Sjkh if ( 5329Sjkh !forceciflag && 5339Sjkh (changework = rcsfcmp( 5349Sjkh workptr, &workstat, expfilename, targetdelta 5359Sjkh )) <= 0 5369Sjkh ) { 5379Sjkh diagnose("file is unchanged; reverting to previous revision %s\n", 5389Sjkh targetdelta->num 5399Sjkh ); 5409Sjkh if (removedlock < lockflag) { 5419Sjkh diagnose("previous revision was not locked; ignoring -l option\n"); 5429Sjkh lockthis = 0; 5439Sjkh } 5449Sjkh if (!(changedRCS = 5459Sjkh lockflag < removedlock 5469Sjkh || assoclst 5479Sjkh || newdelta.state != default_state 5489Sjkh && strcmp(newdelta.state, targetdelta->state) != 0 5499Sjkh )) 5509Sjkh workdelta = targetdelta; 5519Sjkh else { 5529Sjkh /* 5539Sjkh * We have started to build the wrong new RCS file. 5549Sjkh * Start over from the beginning. 5559Sjkh */ 5569Sjkh long hwm = ftell(frewrite); 5579Sjkh int bad_truncate; 5589Sjkh if (fseek(frewrite, 0L, SEEK_SET) != 0) 5599Sjkh Oerror(); 5609Sjkh# if !has_ftruncate 5619Sjkh bad_truncate = 1; 5629Sjkh# else 5639Sjkh /* 5649Sjkh * Work around a common ftruncate() bug. 5659Sjkh * We can't rely on has_truncate, because we might 5669Sjkh * be using a filesystem exported to us via NFS. 5679Sjkh */ 5689Sjkh bad_truncate = ftruncate(fileno(frewrite),(off_t)0); 5699Sjkh if (bad_truncate && errno != EACCES) 5709Sjkh Oerror(); 5719Sjkh# endif 5729Sjkh Irewind(finptr); 5739Sjkh Lexinit(); 5749Sjkh getadmin(); 5759Sjkh gettree(); 5769Sjkh if (!(workdelta = genrevs( 5779Sjkh targetdelta->num, (char*)0, (char*)0, (char*)0, 5789Sjkh &gendeltas 5799Sjkh ))) 5809Sjkh continue; 5819Sjkh workdelta->log = targetdelta->log; 5829Sjkh if (newdelta.state != default_state) 5839Sjkh workdelta->state = newdelta.state; 5849Sjkh if (removedlock && removelock(workdelta)<0) 5859Sjkh continue; 5869Sjkh if (!addsyms(workdelta->num)) 5879Sjkh continue; 5889Sjkh if (!dorewrite(true, true)) 5899Sjkh continue; 5909Sjkh fastcopy(finptr, frewrite); 5919Sjkh if (bad_truncate) 5929Sjkh while (ftell(frewrite) < hwm) 5939Sjkh /* White out any earlier mistake with '\n's. */ 5949Sjkh /* This is unlikely. */ 5959Sjkh afputc('\n', frewrite); 5969Sjkh } 5979Sjkh } else { 5989Sjkh diagnose("new revision: %s; previous revision: %s\n", 5999Sjkh newdelnum.string, targetdelta->num 6009Sjkh ); 6019Sjkh newdelta.log = getlogmsg(); 6029Sjkh switch (run((char*)0, diffilename, 6039Sjkh DIFF DIFF_FLAGS, 6049Sjkh newhead ? workdiffname : expfilename, 6059Sjkh newhead ? expfilename : workdiffname, 6069Sjkh (char*)0 6079Sjkh )) { 6089Sjkh case DIFF_FAILURE: case DIFF_SUCCESS: break; 6099Sjkh default: faterror("diff failed"); 6109Sjkh } 6119Sjkh if (newhead) { 6129Sjkh Irewind(workptr); 6139Sjkh if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue; 6149Sjkh if (!putdtext(targetdelta->num,targetdelta->log,diffilename,frewrite,true)) continue; 6159Sjkh } else 6169Sjkh if (!putdtext(newdelnum.string,newdelta.log,diffilename,frewrite,true)) continue; 6179Sjkh changedRCS = true; 6189Sjkh } 6199Sjkh } 6209Sjkh if (!donerewrite(changedRCS)) 6219Sjkh continue; 6229Sjkh 6239Sjkh if (!keepworkingfile) { 6249Sjkh Izclose(&workptr); 6259Sjkh r = un_link(workfilename); /* Get rid of old file */ 6269Sjkh } else { 6279Sjkh newworkmode = WORKMODE(RCSstat.st_mode, 6289Sjkh ! (Expand==VAL_EXPAND || lockthis < StrictLocks) 6299Sjkh ); 6309Sjkh mtime = mtimeflag ? workdelta->date : (char const*)0; 6319Sjkh 6329Sjkh /* Expand if it might change or if we can't fix mode, time. */ 6339Sjkh if (changework || (r=fixwork(newworkmode,mtime)) != 0) { 6349Sjkh Irewind(workptr); 6359Sjkh /* Expand keywords in file. */ 6369Sjkh locker_expansion = lockthis; 6379Sjkh switch (xpandfile( 6389Sjkh workptr, workfilename, 6399Sjkh workdelta, &newworkfilename 6409Sjkh )) { 6419Sjkh default: 6429Sjkh continue; 6439Sjkh 6449Sjkh case 0: 6459Sjkh /* 6469Sjkh * No expansion occurred; try to reuse working file 6479Sjkh * unless we already tried and failed. 6489Sjkh */ 6499Sjkh if (changework) 6509Sjkh if ((r=fixwork(newworkmode,mtime)) == 0) 6519Sjkh break; 6529Sjkh /* fall into */ 6539Sjkh case 1: 6549Sjkh if (!(r = setfiledate(newworkfilename,mtime))) { 6559Sjkh Izclose(&workptr); 6569Sjkh ignoreints(); 6579Sjkh r = chnamemod(&exfile, newworkfilename, workfilename, newworkmode); 6589Sjkh keepdirtemp(newworkfilename); 6599Sjkh restoreints(); 6609Sjkh } 6619Sjkh } 6629Sjkh } 6639Sjkh } 6649Sjkh if (r != 0) { 6659Sjkh eerror(workfilename); 6669Sjkh continue; 6679Sjkh } 6689Sjkh diagnose("done\n"); 6699Sjkh 6709Sjkh } while (cleanup(), 6719Sjkh ++argv, --argc >=1); 6729Sjkh 6739Sjkh tempunlink(); 6749Sjkh exitmain(exitstatus); 6759Sjkh} /* end of main (ci) */ 6769Sjkh 6779Sjkh static void 6789Sjkhcleanup() 6799Sjkh{ 6809Sjkh if (nerror) exitstatus = EXIT_FAILURE; 6819Sjkh Izclose(&finptr); 6829Sjkh Izclose(&workptr); 6839Sjkh Ozclose(&exfile); 6849Sjkh Ozclose(&fcopy); 6859Sjkh Ozclose(&frewrite); 6869Sjkh dirtempunlink(); 6879Sjkh} 6889Sjkh 6899Sjkh#if lint 6909Sjkh# define exiterr ciExit 6919Sjkh#endif 6929Sjkh exiting void 6939Sjkhexiterr() 6949Sjkh{ 6959Sjkh dirtempunlink(); 6969Sjkh tempunlink(); 6979Sjkh _exit(EXIT_FAILURE); 6989Sjkh} 6999Sjkh 7009Sjkh/*****************************************************************/ 7019Sjkh/* the rest are auxiliary routines */ 7029Sjkh 7039Sjkh 7049Sjkh static int 7059Sjkhaddelta() 7069Sjkh/* Function: Appends a delta to the delta tree, whose number is 7079Sjkh * given by newdelnum. Updates Head, newdelnum, newdelnumlength, 7089Sjkh * and the links in newdelta. 7099Sjkh * Return -1 on error, 1 if a lock is removed, 0 otherwise. 7109Sjkh */ 7119Sjkh{ 7129Sjkh register char *tp; 7139Sjkh register unsigned i; 7149Sjkh int removedlock; 7159Sjkh unsigned newdnumlength; /* actual length of new rev. num. */ 7169Sjkh 7179Sjkh newdnumlength = countnumflds(newdelnum.string); 7189Sjkh 7199Sjkh if (rcsinitflag) { 7209Sjkh /* this covers non-existing RCS file and a file initialized with rcs -i */ 7219Sjkh if ((newdnumlength==0)&&(Dbranch!=nil)) { 7229Sjkh bufscpy(&newdelnum, Dbranch); 7239Sjkh newdnumlength = countnumflds(Dbranch); 7249Sjkh } 7259Sjkh if (newdnumlength==0) bufscpy(&newdelnum, "1.1"); 7269Sjkh else if (newdnumlength==1) bufscat(&newdelnum, ".1"); 7279Sjkh else if (newdnumlength>2) { 7289Sjkh error("Branch point doesn't exist for %s.",newdelnum.string); 7299Sjkh return -1; 7309Sjkh } /* newdnumlength == 2 is OK; */ 7319Sjkh Head = &newdelta; 7329Sjkh newdelta.next=nil; 7339Sjkh return 0; 7349Sjkh } 7359Sjkh if (newdnumlength==0) { 7369Sjkh /* derive new revision number from locks */ 7379Sjkh switch (findlock(true, &targetdelta)) { 7389Sjkh 7399Sjkh default: 7409Sjkh /* found two or more old locks */ 7419Sjkh return -1; 7429Sjkh 7439Sjkh case 1: 7449Sjkh /* found an old lock */ 7459Sjkh /* check whether locked revision exists */ 7469Sjkh if (!genrevs(targetdelta->num,(char*)0,(char*)0,(char*)0,&gendeltas)) 7479Sjkh return -1; 7489Sjkh if (targetdelta==Head) { 7499Sjkh /* make new head */ 7509Sjkh newdelta.next=Head; 7519Sjkh Head= &newdelta; 7529Sjkh } else if (!targetdelta->next && countnumflds(targetdelta->num)>2) { 7539Sjkh /* new tip revision on side branch */ 7549Sjkh targetdelta->next= &newdelta; 7559Sjkh newdelta.next = nil; 7569Sjkh } else { 7579Sjkh /* middle revision; start a new branch */ 7589Sjkh bufscpy(&newdelnum, ""); 7599Sjkh return addbranch(targetdelta,&newdelnum); 7609Sjkh } 7619Sjkh incnum(targetdelta->num, &newdelnum); 7629Sjkh return 1; /* successful use of existing lock */ 7639Sjkh 7649Sjkh case 0: 7659Sjkh /* no existing lock; try Dbranch */ 7669Sjkh /* update newdelnum */ 7679Sjkh if (StrictLocks || !myself(RCSstat.st_uid)) { 7689Sjkh error("no lock set by %s",getcaller()); 7699Sjkh return -1; 7709Sjkh } 7719Sjkh if (Dbranch) { 7729Sjkh bufscpy(&newdelnum, Dbranch); 7739Sjkh } else { 7749Sjkh incnum(Head->num, &newdelnum); 7759Sjkh } 7769Sjkh newdnumlength = countnumflds(newdelnum.string); 7779Sjkh /* now fall into next statement */ 7789Sjkh } 7799Sjkh } 7809Sjkh if (newdnumlength<=2) { 7819Sjkh /* add new head per given number */ 7829Sjkh if(newdnumlength==1) { 7839Sjkh /* make a two-field number out of it*/ 7849Sjkh if (cmpnumfld(newdelnum.string,Head->num,1)==0) 7859Sjkh incnum(Head->num, &newdelnum); 7869Sjkh else 7879Sjkh bufscat(&newdelnum, ".1"); 7889Sjkh } 7899Sjkh if (cmpnum(newdelnum.string,Head->num) <= 0) { 7909Sjkh error("deltanumber %s too low; must be higher than %s", 7919Sjkh newdelnum.string, Head->num); 7929Sjkh return -1; 7939Sjkh } 7949Sjkh targetdelta = Head; 7959Sjkh if (0 <= (removedlock = removelock(Head))) { 7969Sjkh if (!genrevs(Head->num,(char*)0,(char*)0,(char*)0,&gendeltas)) 7979Sjkh return -1; 7989Sjkh newdelta.next = Head; 7999Sjkh Head = &newdelta; 8009Sjkh } 8019Sjkh return removedlock; 8029Sjkh } else { 8039Sjkh /* put new revision on side branch */ 8049Sjkh /*first, get branch point */ 8059Sjkh tp = newdelnum.string; 8069Sjkh for (i = newdnumlength - (newdnumlength&1 ^ 1); (--i); ) 8079Sjkh while (*tp++ != '.') 8089Sjkh ; 8099Sjkh *--tp = 0; /* Kill final dot to get old delta temporarily. */ 8109Sjkh if (!(targetdelta=genrevs(newdelnum.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas))) 8119Sjkh return -1; 8129Sjkh if (cmpnum(targetdelta->num, newdelnum.string) != 0) { 8139Sjkh error("can't find branchpoint %s", newdelnum.string); 8149Sjkh return -1; 8159Sjkh } 8169Sjkh *tp = '.'; /* Restore final dot. */ 8179Sjkh return addbranch(targetdelta,&newdelnum); 8189Sjkh } 8199Sjkh} 8209Sjkh 8219Sjkh 8229Sjkh 8239Sjkh static int 8249Sjkhaddbranch(branchpoint,num) 8259Sjkh struct hshentry *branchpoint; 8269Sjkh struct buf *num; 8279Sjkh/* adds a new branch and branch delta at branchpoint. 8289Sjkh * If num is the null string, appends the new branch, incrementing 8299Sjkh * the highest branch number (initially 1), and setting the level number to 1. 8309Sjkh * the new delta and branchhead are in globals newdelta and newbranch, resp. 8319Sjkh * the new number is placed into num. 8329Sjkh * Return -1 on error, 1 if a lock is removed, 0 otherwise. 8339Sjkh */ 8349Sjkh{ 8359Sjkh struct branchhead *bhead, **btrail; 8369Sjkh struct buf branchnum; 8379Sjkh int removedlock, result; 8389Sjkh unsigned field, numlength; 8399Sjkh static struct branchhead newbranch; /* new branch to be inserted */ 8409Sjkh 8419Sjkh numlength = countnumflds(num->string); 8429Sjkh 8439Sjkh if (branchpoint->branches==nil) { 8449Sjkh /* start first branch */ 8459Sjkh branchpoint->branches = &newbranch; 8469Sjkh if (numlength==0) { 8479Sjkh bufscpy(num, branchpoint->num); 8489Sjkh bufscat(num, ".1.1"); 8499Sjkh } else if (numlength&1) 8509Sjkh bufscat(num, ".1"); 8519Sjkh newbranch.nextbranch=nil; 8529Sjkh 8539Sjkh } else if (numlength==0) { 8549Sjkh /* append new branch to the end */ 8559Sjkh bhead=branchpoint->branches; 8569Sjkh while (bhead->nextbranch) bhead=bhead->nextbranch; 8579Sjkh bhead->nextbranch = &newbranch; 8589Sjkh bufautobegin(&branchnum); 8599Sjkh getbranchno(bhead->hsh->num, &branchnum); 8609Sjkh incnum(branchnum.string, num); 8619Sjkh bufautoend(&branchnum); 8629Sjkh bufscat(num, ".1"); 8639Sjkh newbranch.nextbranch=nil; 8649Sjkh } else { 8659Sjkh /* place the branch properly */ 8669Sjkh field = numlength - (numlength&1 ^ 1); 8679Sjkh /* field of branch number */ 8689Sjkh btrail = &branchpoint->branches; 8699Sjkh while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) { 8709Sjkh btrail = &(*btrail)->nextbranch; 8719Sjkh if (!*btrail) { 8729Sjkh result = -1; 8739Sjkh break; 8749Sjkh } 8759Sjkh } 8769Sjkh if (result < 0) { 8779Sjkh /* insert/append new branchhead */ 8789Sjkh newbranch.nextbranch = *btrail; 8799Sjkh *btrail = &newbranch; 8809Sjkh if (numlength&1) bufscat(num, ".1"); 8819Sjkh } else { 8829Sjkh /* branch exists; append to end */ 8839Sjkh bufautobegin(&branchnum); 8849Sjkh getbranchno(num->string, &branchnum); 8859Sjkh targetdelta=genrevs(branchnum.string,(char*)nil, 8869Sjkh (char*)nil,(char*)nil,&gendeltas); 8879Sjkh bufautoend(&branchnum); 8889Sjkh if (!targetdelta) 8899Sjkh return -1; 8909Sjkh if (cmpnum(num->string,targetdelta->num) <= 0) { 8919Sjkh error("deltanumber %s too low; must be higher than %s", 8929Sjkh num->string,targetdelta->num); 8939Sjkh return -1; 8949Sjkh } 8959Sjkh if (0 <= (removedlock = removelock(targetdelta))) { 8969Sjkh if (numlength&1) 8979Sjkh incnum(targetdelta->num,num); 8989Sjkh targetdelta->next = &newdelta; 8999Sjkh newdelta.next = 0; 9009Sjkh } 9019Sjkh return removedlock; 9029Sjkh /* Don't do anything to newbranch. */ 9039Sjkh } 9049Sjkh } 9059Sjkh newbranch.hsh = &newdelta; 9069Sjkh newdelta.next=nil; 9079Sjkh return 0; 9089Sjkh} 9099Sjkh 9109Sjkh static int 9119Sjkhaddsyms(num) 9129Sjkh char const *num; 9139Sjkh{ 9149Sjkh register struct Symrev *p; 9159Sjkh 9169Sjkh for (p = assoclst; p; p = p->nextsym) 9179Sjkh if (!addsymbol(num, p->ssymbol, p->override)) 9189Sjkh return false; 9199Sjkh return true; 9209Sjkh} 9219Sjkh 9229Sjkh 9239Sjkh static void 9249Sjkhincnum(onum,nnum) 9259Sjkh char const *onum; 9269Sjkh struct buf *nnum; 9279Sjkh/* Increment the last field of revision number onum by one and 9289Sjkh * place the result into nnum. 9299Sjkh */ 9309Sjkh{ 9319Sjkh register char *tp, *np; 9329Sjkh register size_t l; 9339Sjkh 9349Sjkh l = strlen(onum); 9359Sjkh bufalloc(nnum, l+2); 9369Sjkh np = tp = nnum->string; 9379Sjkh VOID strcpy(np, onum); 9389Sjkh for (tp = np + l; np != tp; ) 9399Sjkh if (isdigit(*--tp)) { 9409Sjkh if (*tp != '9') { 9419Sjkh ++*tp; 9429Sjkh return; 9439Sjkh } 9449Sjkh *tp = '0'; 9459Sjkh } else { 9469Sjkh tp++; 9479Sjkh break; 9489Sjkh } 9499Sjkh /* We changed 999 to 000; now change it to 1000. */ 9509Sjkh *tp = '1'; 9519Sjkh tp = np + l; 9529Sjkh *tp++ = '0'; 9539Sjkh *tp = 0; 9549Sjkh} 9559Sjkh 9569Sjkh 9579Sjkh 9589Sjkh static int 9599Sjkhremovelock(delta) 9609Sjkhstruct hshentry * delta; 9619Sjkh/* function: Finds the lock held by caller on delta, 9629Sjkh * removes it, and returns nonzero if successful. 9639Sjkh * Print an error message and return -1 if there is no such lock. 9649Sjkh * An exception is if !StrictLocks, and caller is the owner of 9659Sjkh * the RCS file. If caller does not have a lock in this case, 9669Sjkh * return 0; return 1 if a lock is actually removed. 9679Sjkh */ 9689Sjkh{ 9699Sjkh register struct lock *next, **trail; 9709Sjkh char const *num; 9719Sjkh 9729Sjkh num=delta->num; 9739Sjkh for (trail = &Locks; (next = *trail); trail = &next->nextlock) 9749Sjkh if (next->delta == delta) 9759Sjkh if (strcmp(getcaller(), next->login) == 0) { 9769Sjkh /* We found a lock on delta by caller; delete it. */ 9779Sjkh *trail = next->nextlock; 9789Sjkh delta->lockedby = 0; 9799Sjkh return 1; 9809Sjkh } else { 9819Sjkh error("revision %s locked by %s",num,next->login); 9829Sjkh return -1; 9839Sjkh } 9849Sjkh if (!StrictLocks && myself(RCSstat.st_uid)) 9859Sjkh return 0; 9869Sjkh error("no lock set by %s for revision %s", getcaller(), num); 9879Sjkh return -1; 9889Sjkh} 9899Sjkh 9909Sjkh 9919Sjkh 9929Sjkh static char const * 9939Sjkhgetcurdate() 9949Sjkh/* Return a pointer to the current date. */ 9959Sjkh{ 9969Sjkh static char buffer[datesize]; /* date buffer */ 9979Sjkh time_t t; 9989Sjkh 9999Sjkh if (!buffer[0]) { 10009Sjkh t = time((time_t *)0); 10019Sjkh if (t == -1) 10029Sjkh faterror("time not available"); 10039Sjkh time2date(t, buffer); 10049Sjkh } 10059Sjkh return buffer; 10069Sjkh} 10079Sjkh 10089Sjkh static int 10099Sjkh#if has_prototypes 10109Sjkhfixwork(mode_t newworkmode, char const *mtime) 10119Sjkh /* The `#if has_prototypes' is needed because mode_t might promote to int. */ 10129Sjkh#else 10139Sjkh fixwork(newworkmode, mtime) 10149Sjkh mode_t newworkmode; 10159Sjkh char const *mtime; 10169Sjkh#endif 10179Sjkh{ 10189Sjkh int r; 10199Sjkh return 10209Sjkh 1 < workstat.st_nlink 10219Sjkh || newworkmode&S_IWUSR && !myself(workstat.st_uid) 10229Sjkh ? -1 10239Sjkh : 10249Sjkh workstat.st_mode != newworkmode 10259Sjkh && 10269Sjkh (r = 10279Sjkh# if has_fchmod 10289Sjkh fchmod(Ifileno(workptr), newworkmode) 10299Sjkh# else 10309Sjkh chmod(workfilename, newworkmode) 10319Sjkh# endif 10329Sjkh ) != 0 10339Sjkh ? r 10349Sjkh : 10359Sjkh setfiledate(workfilename, mtime); 10369Sjkh} 10379Sjkh 10389Sjkh static int 10399Sjkhxpandfile(unexfile, dir, delta, exfilename) 10409Sjkh RILE *unexfile; 10419Sjkh char const *dir; 10429Sjkh struct hshentry const *delta; 10439Sjkh char const **exfilename; 10449Sjkh/* 10459Sjkh * Read unexfile and copy it to a 10469Sjkh * file in dir, performing keyword substitution with data from delta. 10479Sjkh * Return -1 if unsuccessful, 1 if expansion occurred, 0 otherwise. 10489Sjkh * If successful, stores the stream descriptor into *EXFILEP 10499Sjkh * and its name into *EXFILENAME. 10509Sjkh */ 10519Sjkh{ 10529Sjkh char const *targetfname; 10539Sjkh int e, r; 10549Sjkh 10559Sjkh targetfname = makedirtemp(dir, 1); 10569Sjkh if (!(exfile = fopen(targetfname, FOPEN_W_WORK))) { 10579Sjkh eerror(targetfname); 10589Sjkh error("can't expand working file"); 10599Sjkh return -1; 10609Sjkh } 10619Sjkh r = 0; 10629Sjkh if (Expand == OLD_EXPAND) 10639Sjkh fastcopy(unexfile,exfile); 10649Sjkh else { 10659Sjkh for (;;) { 10669Sjkh e = expandline(unexfile,exfile,delta,false,(FILE*)nil); 10679Sjkh if (e < 0) 10689Sjkh break; 10699Sjkh r |= e; 10709Sjkh if (e <= 1) 10719Sjkh break; 10729Sjkh } 10739Sjkh } 10749Sjkh *exfilename = targetfname; 10759Sjkh aflush(exfile); 10769Sjkh return r & 1; 10779Sjkh} 10789Sjkh 10799Sjkh 10809Sjkh 10819Sjkh 10829Sjkh/* --------------------- G E T L O G M S G --------------------------------*/ 10839Sjkh 10849Sjkh 10859Sjkh static struct cbuf 10869Sjkhgetlogmsg() 10879Sjkh/* Obtain and yield a log message. 10889Sjkh * If a log message is given with -m, yield that message. 10899Sjkh * If this is the initial revision, yield a standard log message. 10909Sjkh * Otherwise, reads a character string from the terminal. 10919Sjkh * Stops after reading EOF or a single '.' on a 10929Sjkh * line. getlogmsg prompts the first time it is called for the 10939Sjkh * log message; during all later calls it asks whether the previous 10949Sjkh * log message can be reused. 10959Sjkh */ 10969Sjkh{ 10979Sjkh static char const 10989Sjkh emptych[] = EMPTYLOG, 10999Sjkh initialch[] = "Initial revision"; 11009Sjkh static struct cbuf const 11019Sjkh emptylog = { emptych, sizeof(emptych)-sizeof(char) }, 11029Sjkh initiallog = { initialch, sizeof(initialch)-sizeof(char) }; 11039Sjkh static struct buf logbuf; 11049Sjkh static struct cbuf logmsg; 11059Sjkh 11069Sjkh register char *tp; 11079Sjkh register size_t i; 11089Sjkh char const *caller; 11099Sjkh 11109Sjkh if (msg.size) return msg; 11119Sjkh 11129Sjkh if (keepflag) { 11139Sjkh /* generate std. log message */ 11149Sjkh caller = getcaller(); 11159Sjkh i = sizeof(ciklog)+strlen(caller)+3; 11169Sjkh bufalloc(&logbuf, i+datesize); 11179Sjkh tp = logbuf.string; 11189Sjkh VOID sprintf(tp, "%s%s at ", ciklog, caller); 11199Sjkh VOID date2str(getcurdate(), tp+i); 11209Sjkh logmsg.string = tp; 11219Sjkh logmsg.size = strlen(tp); 11229Sjkh return logmsg; 11239Sjkh } 11249Sjkh 11259Sjkh if (!targetdelta && ( 11269Sjkh cmpnum(newdelnum.string,"1.1")==0 || 11279Sjkh cmpnum(newdelnum.string,"1.0")==0 11289Sjkh )) 11299Sjkh return initiallog; 11309Sjkh 11319Sjkh if (logmsg.size) { 11329Sjkh /*previous log available*/ 11339Sjkh if (yesorno(true, "reuse log message of previous file? [yn](y): ")) 11349Sjkh return logmsg; 11359Sjkh } 11369Sjkh 11379Sjkh /* now read string from stdin */ 11389Sjkh logmsg = getsstdin("m", "log message", "", &logbuf); 11399Sjkh 11409Sjkh /* now check whether the log message is not empty */ 11419Sjkh if (logmsg.size) 11429Sjkh return logmsg; 11439Sjkh return emptylog; 11449Sjkh} 11459Sjkh 11469Sjkh/* Make a linked list of Symbolic names */ 11479Sjkh 11489Sjkh static void 11499Sjkhaddassoclst(flag, sp) 11509Sjkhint flag; 11519Sjkhchar * sp; 11529Sjkh{ 11539Sjkh struct Symrev *pt; 11549Sjkh 11559Sjkh pt = talloc(struct Symrev); 11569Sjkh pt->ssymbol = sp; 11579Sjkh pt->override = flag; 11589Sjkh pt->nextsym = nil; 11599Sjkh if (lastassoc) 11609Sjkh lastassoc->nextsym = pt; 11619Sjkh else 11629Sjkh assoclst = pt; 11639Sjkh lastassoc = pt; 11649Sjkh return; 11659Sjkh} 1166