Annotation of src/usr.bin/rcs/ci.c, Revision 1.13
1.13 ! joris 1: /* $OpenBSD: ci.c,v 1.12 2005/10/09 14:48:13 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.12 niallo 53: static char * checkin_getlogmsg(char *, char *, RCSNUM *, RCSNUM *);
1.4 niallo 54:
1.1 niallo 55: void
56: checkin_usage(void)
57: {
58: fprintf(stderr,
1.12 niallo 59: "usage: %s [-jlMNquV] [-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: int
70: checkin_main(int argc, char **argv)
71: {
1.12 niallo 72: int i, ch, dflag, flags, lkmode, interactive, rflag;
1.1 niallo 73: mode_t fmode;
74: RCSFILE *file;
1.12 niallo 75: RCSNUM *frev, *newrev;
1.1 niallo 76: char fpath[MAXPATHLEN];
1.12 niallo 77: char *rcs_msg, *filec, *deltatext, *username;
1.4 niallo 78: BUF *bp;
1.1 niallo 79:
80: flags = RCS_RDWR;
81: file = NULL;
1.12 niallo 82: rcs_msg = NULL;
83: newrev = NULL;
84: fmode = lkmode = dflag = verbose = rflag = 0;
1.6 niallo 85: interactive = 1;
1.1 niallo 86:
1.9 niallo 87: if ((username = getlogin()) == NULL) {
88: cvs_log(LP_ERR, "failed to get username");
89: exit(1);
90: }
91:
92: while ((ch = getopt(argc, argv, "j:lM:N:qud:r::m:k:V")) != -1) {
1.1 niallo 93: switch (ch) {
94: case 'h':
95: (usage)();
96: exit(0);
97: case 'm':
98: rcs_msg = optarg;
1.6 niallo 99: interactive = 0;
1.3 joris 100: break;
101: case 'q':
102: verbose = 0;
1.1 niallo 103: break;
104: case 'V':
105: printf("%s\n", rcs_version);
106: exit(0);
1.9 niallo 107: case 'l':
108: lkmode = LOCK_LOCK;
109: break;
110: case 'u':
111: lkmode = LOCK_UNLOCK;
112: break;
1.12 niallo 113: case 'r':
114: rflag = 1;
115: if (optarg != NULL) {
116: if ((newrev = rcsnum_parse(optarg)) == NULL) {
117: cvs_log(LP_ERR, "bad revision number");
118: exit(1);
119: }
120: }
121: break;
1.1 niallo 122: default:
123: (usage)();
124: exit(1);
125: }
126: }
127:
128: argc -= optind;
129: argv += optind;
130: if (argc == 0) {
131: cvs_log(LP_ERR, "no input file");
132: (usage)();
133: exit(1);
134: }
135:
136: for (i = 0; i < argc; i++) {
137: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
138: continue;
139:
1.4 niallo 140: file = rcs_open(fpath, RCS_RDWR, fmode);
1.1 niallo 141: if (file == NULL) {
1.4 niallo 142: cvs_log(LP_ERR, "failed to open rcsfile '%s'", fpath);
1.1 niallo 143: exit(1);
144: }
1.4 niallo 145:
146: if (dflag) {
1.6 niallo 147: /* XXX */
1.1 niallo 148: }
1.4 niallo 149:
150: /*
151: * Load file contents
152: */
153: if ((bp = cvs_buf_load(argv[i], BUF_AUTOEXT)) == NULL) {
154: cvs_log(LP_ERR, "failed to load '%s'", argv[i]);
155: exit(1);
156: }
157:
1.12 niallo 158: if (cvs_buf_putc(bp, '\0') < 0)
159: exit(1);
160:
161: filec = cvs_buf_release(bp);
162:
163: /*
164: * If rev is not specified on the command line,
165: * assume HEAD.
166: */
167: frev = file->rf_head;
1.13 ! joris 168:
1.6 niallo 169: /*
170: * If no log message specified, get it interactively.
171: */
1.13 ! joris 172: if (rcs_msg != NULL)
1.12 niallo 173: rcs_msg = checkin_getlogmsg(fpath, argv[i], frev, newrev);
1.4 niallo 174:
175: /*
176: * Remove the lock
177: */
178: if (rcs_lock_remove(file, frev) < 0) {
179: if (rcs_errno != RCS_ERR_NOENT)
180: cvs_log(LP_WARN, "failed to remove lock");
181: }
182:
183: /*
184: * Get RCS patch
185: */
186: if ((deltatext = checkin_diff_file(file, frev, argv[i])) == NULL) {
187: cvs_log(LP_ERR, "failed to get diff");
188: exit(1);
189: }
190:
191: /*
192: * Current head revision gets the RCS patch as rd_text
193: */
1.12 niallo 194: if (rcs_deltatext_set(file, frev, deltatext) == -1) {
1.7 niallo 195: cvs_log(LP_ERR,
196: "failed to set new rd_text for head rev");
1.4 niallo 197: exit (1);
198: }
199: /*
200: * Now add our new revision
201: */
1.12 niallo 202: if (rcs_rev_add(file, (newrev == NULL ? RCS_HEAD_REV : newrev),
203: rcs_msg, -1) != 0) {
1.4 niallo 204: cvs_log(LP_ERR, "failed to add new revision");
205: exit(1);
206: }
207:
208: /*
1.12 niallo 209: * If we are checking in to a non-default (ie user-specified)
210: * revision, set head to this revision.
211: */
212: if (newrev != NULL)
213: rcs_head_set(file, newrev);
214:
215: /*
1.4 niallo 216: * New head revision has to contain entire file;
217: */
218: if (rcs_deltatext_set(file, frev, filec) == -1) {
219: cvs_log(LP_ERR, "failed to set new head revision");
220: exit(1);
221: }
222:
223: free(deltatext);
224: free(filec);
1.9 niallo 225: (void)unlink(argv[i]);
1.4 niallo 226:
1.9 niallo 227: /*
228: * Do checkout if -u or -l are specified.
229: */
1.12 niallo 230: if (lkmode != 0 && !rflag) {
1.9 niallo 231: mode_t mode = 0;
1.12 niallo 232: if ((bp = rcs_getrev(file, newrev)) == NULL) {
1.9 niallo 233: cvs_log(LP_ERR, "cannot get revision");
234: goto err;
235: }
236: if (lkmode == LOCK_LOCK) {
237: mode = 0644;
1.12 niallo 238: if (rcs_lock_add(file, username, newrev) < 0) {
1.9 niallo 239: if (rcs_errno != RCS_ERR_DUPENT)
240: cvs_log(LP_ERR,
241: "failed to lock revision");
242: else
243: cvs_log(LP_ERR,
244: "you already have a lock");
245: }
246: } else if (lkmode == LOCK_UNLOCK) {
247: mode = 0444;
248: }
249: if (cvs_buf_write(bp, argv[i], mode) < 0) {
250: cvs_log(LP_ERR,
251: "failed to write revision to file");
252: }
253: cvs_buf_free(bp);
254: }
255: err:
1.4 niallo 256: /* File will NOW be synced */
1.1 niallo 257: rcs_close(file);
1.4 niallo 258:
1.6 niallo 259: if (interactive) {
260: free(rcs_msg);
261: rcs_msg = NULL;
262: }
1.4 niallo 263: }
264:
265: return (0);
266: }
267:
268: static char *
269: checkin_diff_file(RCSFILE *rfp, RCSNUM *rev, const char *filename)
270: {
271: char path1[MAXPATHLEN], path2[MAXPATHLEN];
272: BUF *b1, *b2, *b3;
273: char rbuf[64], *deltatext;
274:
275: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
276:
277: if ((b1 = cvs_buf_load(filename, BUF_AUTOEXT)) == NULL) {
278: cvs_log(LP_ERR, "failed to load file: '%s'", filename);
279: return (NULL);
1.1 niallo 280: }
281:
1.4 niallo 282: if ((b2 = rcs_getrev(rfp, rev)) == NULL) {
283: cvs_log(LP_ERR, "failed to load revision");
284: cvs_buf_free(b1);
285: return (NULL);
286: }
287:
288: if ((b3 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL) {
289: cvs_log(LP_ERR, "failed to allocated buffer for diff");
290: cvs_buf_free(b1);
291: cvs_buf_free(b2);
292: return (NULL);
293: }
294:
295: strlcpy(path1, "/tmp/diff1.XXXXXXXXXX", sizeof(path1));
296: if (cvs_buf_write_stmp(b1, path1, 0600) == -1) {
297: cvs_log(LP_ERRNO, "could not write temporary file");
298: cvs_buf_free(b1);
299: cvs_buf_free(b2);
300: return (NULL);
301: }
302: cvs_buf_free(b1);
303:
304: strlcpy(path2, "/tmp/diff2.XXXXXXXXXX", sizeof(path2));
305: if (cvs_buf_write_stmp(b2, path2, 0600) == -1) {
306: cvs_buf_free(b2);
307: (void)unlink(path1);
308: return (NULL);
309: }
310: cvs_buf_free(b2);
311:
1.5 niallo 312: diff_format = D_RCSDIFF;
1.4 niallo 313: cvs_diffreg(path1, path2, b3);
314: (void)unlink(path1);
315: (void)unlink(path2);
316:
317: cvs_buf_putc(b3, '\0');
318: deltatext = (char *)cvs_buf_release(b3);
319:
320: return (deltatext);
1.6 niallo 321: }
322:
323: /*
324: * Get log message from user interactively.
325: */
326: static char *
1.12 niallo 327: checkin_getlogmsg(char *rcsfile, char *workingfile, RCSNUM *rev, RCSNUM *rev2)
1.6 niallo 328: {
329: char *rcs_msg, buf[128], nrev[16], prev[16];
330: BUF *logbuf;
331: RCSNUM *tmprev;
332:
1.7 niallo 333: rcs_msg = NULL;
1.6 niallo 334: tmprev = rcsnum_alloc();
335: rcsnum_cpy(rev, tmprev, 16);
1.9 niallo 336: rcsnum_tostr(tmprev, prev, sizeof(prev));
1.12 niallo 337: if (rev2 == NULL)
338: rcsnum_tostr(rcsnum_inc(tmprev), nrev, sizeof(nrev));
339: else
340: rcsnum_tostr(rev2, nrev, sizeof(nrev));
1.6 niallo 341: rcsnum_free(tmprev);
342:
343: if ((logbuf = cvs_buf_alloc(64, BUF_AUTOEXT)) == NULL) {
1.7 niallo 344: cvs_log(LP_ERR, "failed to allocate log buffer");
1.6 niallo 345: return (NULL);
346: }
347: cvs_printf("%s <-- %s\n", rcsfile, workingfile);
1.7 niallo 348: cvs_printf("new revision: %s; previous revision: %s\n", nrev, prev);
1.6 niallo 349: cvs_printf("enter log message, terminated with single "
350: "'.' or end of file:\n");
351: cvs_printf(">> ");
352: for (;;) {
353: fgets(buf, (int)sizeof(buf), stdin);
1.9 niallo 354: if (feof(stdin) || ferror(stdin) || buf[0] == '.')
1.6 niallo 355: break;
356: cvs_buf_append(logbuf, buf, strlen(buf));
357: cvs_printf(">> ");
358: }
1.8 niallo 359: cvs_buf_putc(logbuf, '\0');
1.6 niallo 360: rcs_msg = (char *)cvs_buf_release(logbuf);
361: return (rcs_msg);
1.1 niallo 362: }