Annotation of src/usr.bin/rcs/ci.c, Revision 1.10
1.10 ! niallo 1: /* $OpenBSD: ci.c,v 1.9 2005/10/08 15:26:47 niallo Exp $ */
1.1 niallo 2: /*
3: * Copyright (c) 2005 Niall O'Higgins <niallo@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following cinditions
8: * are met:
9: *
10: * 1. Redistributions of source cide must retain the above cipyright
11: * notice, this list of cinditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27: #include <sys/param.h>
28: #include <sys/types.h>
29: #include <sys/stat.h>
30: #include <sys/wait.h>
31:
32: #include <err.h>
33: #include <pwd.h>
34: #include <errno.h>
35: #include <stdio.h>
36: #include <ctype.h>
37: #include <stdlib.h>
38: #include <unistd.h>
39: #include <signal.h>
40: #include <string.h>
41:
42: #include "log.h"
43: #include "rcs.h"
1.4 niallo 44: #include "diff.h"
1.1 niallo 45: #include "rcsprog.h"
46:
47: extern char *__progname;
48:
1.9 niallo 49: #define LOCK_LOCK 1
50: #define LOCK_UNLOCK 2
51:
1.4 niallo 52: static char * checkin_diff_file(RCSFILE *, RCSNUM *, const char *);
1.6 niallo 53: static char * checkin_getlogmsg(char *, char *, RCSNUM *);
1.4 niallo 54:
1.1 niallo 55: void
56: checkin_usage(void)
57: {
58: fprintf(stderr,
1.10 ! niallo 59: "usage: %s [-jlMNqruV] [-d date | -r rev] [-m msg] [-k mode] "
1.1 niallo 60: "file ...\n", __progname);
61: }
62:
63: /*
64: * checkin_main()
65: *
66: * Handler for the `ci' program.
67: * Returns 0 on success, or >0 on error.
68: */
69: /*
70: Options:
71:
72: -r | -r[rev]: check in revision rev
73: -l[rev]: ", but do co -l
1.6 niallo 74: -u[rev]: ", but do co -u
1.1 niallo 75: -f[rev]: force a deposit (check in?)
76: -k[rev]: ?
77: -q[rev]: quiet mode
78: -i[rev]: initial check in, errors if RCS file already exists.
79: -j[rev]: just checkin and do not initialize, errors if RCS file already exists.
80: -I[rev]: user is prompted even if stdin is not a tty
1.6 niallo 81: -d[date]: uses date for checkin date and time.
1.1 niallo 82: -M[rev]: set modification time on any new working file to be that of the retrieved version.
83: -mmsg: msg is the log message, don't start editor. log messages with #are comments.
84: */
85: int
86: checkin_main(int argc, char **argv)
87: {
1.6 niallo 88: int i, ch, dflag, flags, lkmode, interactive;
1.1 niallo 89: mode_t fmode;
90: RCSFILE *file;
1.4 niallo 91: RCSNUM *frev;
1.1 niallo 92: char fpath[MAXPATHLEN];
1.9 niallo 93: char *rcs_msg, *rev, *filec, *deltatext, *username;
1.4 niallo 94: BUF *bp;
1.1 niallo 95:
96: flags = RCS_RDWR;
97: file = NULL;
1.4 niallo 98: rcs_msg = rev = NULL;
1.9 niallo 99: fmode = lkmode = dflag = verbose = 0;
1.6 niallo 100: interactive = 1;
1.1 niallo 101:
1.9 niallo 102: if ((username = getlogin()) == NULL) {
103: cvs_log(LP_ERR, "failed to get username");
104: exit(1);
105: }
106:
107: while ((ch = getopt(argc, argv, "j:lM:N:qud:r::m:k:V")) != -1) {
1.1 niallo 108: switch (ch) {
109: case 'h':
110: (usage)();
111: exit(0);
112: case 'm':
113: rcs_msg = optarg;
1.6 niallo 114: interactive = 0;
1.3 joris 115: break;
116: case 'q':
117: verbose = 0;
1.1 niallo 118: break;
119: case 'V':
120: printf("%s\n", rcs_version);
121: exit(0);
1.9 niallo 122: case 'l':
123: lkmode = LOCK_LOCK;
124: break;
125: case 'u':
126: lkmode = LOCK_UNLOCK;
127: break;
1.1 niallo 128: default:
129: (usage)();
130: exit(1);
131: }
132: }
133:
134: argc -= optind;
135: argv += optind;
136: if (argc == 0) {
137: cvs_log(LP_ERR, "no input file");
138: (usage)();
139: exit(1);
140: }
141:
142: for (i = 0; i < argc; i++) {
143: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
144: continue;
145:
1.4 niallo 146: file = rcs_open(fpath, RCS_RDWR, fmode);
1.1 niallo 147: if (file == NULL) {
1.4 niallo 148: cvs_log(LP_ERR, "failed to open rcsfile '%s'", fpath);
1.1 niallo 149: exit(1);
150: }
1.4 niallo 151:
152: if (dflag) {
1.6 niallo 153: /* XXX */
1.1 niallo 154: }
1.4 niallo 155:
156: /*
157: * Load file contents
158: */
159: if ((bp = cvs_buf_load(argv[i], BUF_AUTOEXT)) == NULL) {
160: cvs_log(LP_ERR, "failed to load '%s'", argv[i]);
161: exit(1);
162: }
163:
1.6 niallo 164: if (rev == NULL)
165: frev = file->rf_head;
166: /*
167: * If no log message specified, get it interactively.
168: */
169: if (rcs_msg == NULL)
170: rcs_msg = checkin_getlogmsg(fpath, argv[i], frev);
171:
1.4 niallo 172: if (cvs_buf_putc(bp, '\0') < 0)
173: exit(1);
174:
175: filec = cvs_buf_release(bp);
176:
177: /*
178: * Remove the lock
179: */
180: if (rcs_lock_remove(file, frev) < 0) {
181: if (rcs_errno != RCS_ERR_NOENT)
182: cvs_log(LP_WARN, "failed to remove lock");
183: }
184:
185: /*
186: * Get RCS patch
187: */
188: if ((deltatext = checkin_diff_file(file, frev, argv[i])) == NULL) {
189: cvs_log(LP_ERR, "failed to get diff");
190: exit(1);
191: }
192:
193: /*
194: * Current head revision gets the RCS patch as rd_text
195: */
196: if (rcs_deltatext_set(file, file->rf_head, deltatext) == -1) {
1.7 niallo 197: cvs_log(LP_ERR,
198: "failed to set new rd_text for head rev");
1.4 niallo 199: exit (1);
200: }
201: /*
202: * Now add our new revision
203: */
204: if (rcs_rev_add(file, RCS_HEAD_REV, rcs_msg, -1) != 0) {
205: cvs_log(LP_ERR, "failed to add new revision");
206: exit(1);
207: }
208:
209: /*
210: * New head revision has to contain entire file;
211: */
212: if (rcs_deltatext_set(file, frev, filec) == -1) {
213: cvs_log(LP_ERR, "failed to set new head revision");
214: exit(1);
215: }
216:
217: free(deltatext);
218: free(filec);
1.9 niallo 219: (void)unlink(argv[i]);
1.4 niallo 220:
1.9 niallo 221: /*
222: * Do checkout if -u or -l are specified.
223: */
224: if (lkmode != 0) {
225: mode_t mode = 0;
226: if ((bp = rcs_getrev(file, frev)) == NULL) {
227: cvs_log(LP_ERR, "cannot get revision");
228: goto err;
229: }
230: if (lkmode == LOCK_LOCK) {
231: mode = 0644;
232: if (rcs_lock_add(file, username, frev) < 0) {
233: if (rcs_errno != RCS_ERR_DUPENT)
234: cvs_log(LP_ERR,
235: "failed to lock revision");
236: else
237: cvs_log(LP_ERR,
238: "you already have a lock");
239: }
240: } else if (lkmode == LOCK_UNLOCK) {
241: mode = 0444;
242: }
243: if (cvs_buf_write(bp, argv[i], mode) < 0) {
244: cvs_log(LP_ERR,
245: "failed to write revision to file");
246: }
247: cvs_buf_free(bp);
248: }
249: err:
1.4 niallo 250: /* File will NOW be synced */
1.1 niallo 251: rcs_close(file);
1.4 niallo 252:
1.6 niallo 253: if (interactive) {
254: free(rcs_msg);
255: rcs_msg = NULL;
256: }
1.4 niallo 257: }
258:
259: return (0);
260: }
261:
262: static char *
263: checkin_diff_file(RCSFILE *rfp, RCSNUM *rev, const char *filename)
264: {
265: char path1[MAXPATHLEN], path2[MAXPATHLEN];
266: BUF *b1, *b2, *b3;
267: char rbuf[64], *deltatext;
268:
269: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
270:
271: if ((b1 = cvs_buf_load(filename, BUF_AUTOEXT)) == NULL) {
272: cvs_log(LP_ERR, "failed to load file: '%s'", filename);
273: return (NULL);
1.1 niallo 274: }
275:
1.4 niallo 276: if ((b2 = rcs_getrev(rfp, rev)) == NULL) {
277: cvs_log(LP_ERR, "failed to load revision");
278: cvs_buf_free(b1);
279: return (NULL);
280: }
281:
282: if ((b3 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL) {
283: cvs_log(LP_ERR, "failed to allocated buffer for diff");
284: cvs_buf_free(b1);
285: cvs_buf_free(b2);
286: return (NULL);
287: }
288:
289: strlcpy(path1, "/tmp/diff1.XXXXXXXXXX", sizeof(path1));
290: if (cvs_buf_write_stmp(b1, path1, 0600) == -1) {
291: cvs_log(LP_ERRNO, "could not write temporary file");
292: cvs_buf_free(b1);
293: cvs_buf_free(b2);
294: return (NULL);
295: }
296: cvs_buf_free(b1);
297:
298: strlcpy(path2, "/tmp/diff2.XXXXXXXXXX", sizeof(path2));
299: if (cvs_buf_write_stmp(b2, path2, 0600) == -1) {
300: cvs_buf_free(b2);
301: (void)unlink(path1);
302: return (NULL);
303: }
304: cvs_buf_free(b2);
305:
1.5 niallo 306: diff_format = D_RCSDIFF;
1.4 niallo 307: cvs_diffreg(path1, path2, b3);
308: (void)unlink(path1);
309: (void)unlink(path2);
310:
311: cvs_buf_putc(b3, '\0');
312: deltatext = (char *)cvs_buf_release(b3);
313:
314: return (deltatext);
1.6 niallo 315: }
316:
317: /*
318: * Get log message from user interactively.
319: */
320: static char *
321: checkin_getlogmsg(char *rcsfile, char *workingfile, RCSNUM *rev)
322: {
323: char *rcs_msg, buf[128], nrev[16], prev[16];
324: BUF *logbuf;
325: RCSNUM *tmprev;
326:
1.7 niallo 327: rcs_msg = NULL;
1.6 niallo 328: tmprev = rcsnum_alloc();
329: rcsnum_cpy(rev, tmprev, 16);
1.9 niallo 330: rcsnum_tostr(tmprev, prev, sizeof(prev));
1.6 niallo 331: rcsnum_tostr(rcsnum_inc(tmprev), nrev, sizeof(nrev));
332: rcsnum_free(tmprev);
333:
334: if ((logbuf = cvs_buf_alloc(64, BUF_AUTOEXT)) == NULL) {
1.7 niallo 335: cvs_log(LP_ERR, "failed to allocate log buffer");
1.6 niallo 336: return (NULL);
337: }
338: cvs_printf("%s <-- %s\n", rcsfile, workingfile);
1.7 niallo 339: cvs_printf("new revision: %s; previous revision: %s\n", nrev, prev);
1.6 niallo 340: cvs_printf("enter log message, terminated with single "
341: "'.' or end of file:\n");
342: cvs_printf(">> ");
343: for (;;) {
344: fgets(buf, (int)sizeof(buf), stdin);
1.9 niallo 345: if (feof(stdin) || ferror(stdin) || buf[0] == '.')
1.6 niallo 346: break;
347: cvs_buf_append(logbuf, buf, strlen(buf));
348: cvs_printf(">> ");
349: }
1.8 niallo 350: cvs_buf_putc(logbuf, '\0');
1.6 niallo 351: rcs_msg = (char *)cvs_buf_release(logbuf);
352: return (rcs_msg);
1.1 niallo 353: }