Annotation of src/usr.bin/rcs/ci.c, Revision 1.7
1.7 ! niallo 1: /* $OpenBSD: ci.c,v 1.6 2005/10/08 14:09:18 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.4 niallo 49: static char * checkin_diff_file(RCSFILE *, RCSNUM *, const char *);
1.6 niallo 50: static char * checkin_getlogmsg(char *, char *, RCSNUM *);
1.4 niallo 51:
1.1 niallo 52: void
53: checkin_usage(void)
54: {
55: fprintf(stderr,
56: "usage: %s [-jlMNqu] [-d date | -r rev] [-m msg] [-k mode] "
57: "file ...\n", __progname);
58: }
59:
60: /*
61: * checkin_main()
62: *
63: * Handler for the `ci' program.
64: * Returns 0 on success, or >0 on error.
65: */
66: /*
67: Options:
68:
69: -r | -r[rev]: check in revision rev
70: -l[rev]: ", but do co -l
1.6 niallo 71: -u[rev]: ", but do co -u
1.1 niallo 72: -f[rev]: force a deposit (check in?)
73: -k[rev]: ?
74: -q[rev]: quiet mode
75: -i[rev]: initial check in, errors if RCS file already exists.
76: -j[rev]: just checkin and do not initialize, errors if RCS file already exists.
77: -I[rev]: user is prompted even if stdin is not a tty
1.6 niallo 78: -d[date]: uses date for checkin date and time.
1.1 niallo 79: -M[rev]: set modification time on any new working file to be that of the retrieved version.
80: -mmsg: msg is the log message, don't start editor. log messages with #are comments.
81: */
82: int
83: checkin_main(int argc, char **argv)
84: {
1.6 niallo 85: int i, ch, dflag, flags, lkmode, interactive;
1.1 niallo 86: mode_t fmode;
87: RCSFILE *file;
1.4 niallo 88: RCSNUM *frev;
1.1 niallo 89: char fpath[MAXPATHLEN];
1.4 niallo 90: char *rcs_msg, *rev, *filec, *deltatext;
91: BUF *bp;
1.1 niallo 92:
93: lkmode = -1;
94: flags = RCS_RDWR;
95: file = NULL;
1.4 niallo 96: rcs_msg = rev = NULL;
1.6 niallo 97: fmode = dflag = verbose = 0;
98: interactive = 1;
1.1 niallo 99:
1.3 joris 100: while ((ch = getopt(argc, argv, "j:l:M:N:qu:d:r::m:k:V")) != -1) {
1.1 niallo 101: switch (ch) {
102: case 'h':
103: (usage)();
104: exit(0);
105: case 'm':
106: rcs_msg = optarg;
1.6 niallo 107: interactive = 0;
1.3 joris 108: break;
109: case 'q':
110: verbose = 0;
1.1 niallo 111: break;
112: case 'V':
113: printf("%s\n", rcs_version);
114: exit(0);
115: default:
116: (usage)();
117: exit(1);
118: }
119: }
120:
121: argc -= optind;
122: argv += optind;
123: if (argc == 0) {
124: cvs_log(LP_ERR, "no input file");
125: (usage)();
126: exit(1);
127: }
128:
129: for (i = 0; i < argc; i++) {
130: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
131: continue;
132:
1.4 niallo 133: file = rcs_open(fpath, RCS_RDWR, fmode);
1.1 niallo 134: if (file == NULL) {
1.4 niallo 135: cvs_log(LP_ERR, "failed to open rcsfile '%s'", fpath);
1.1 niallo 136: exit(1);
137: }
1.4 niallo 138:
139: if (dflag) {
1.6 niallo 140: /* XXX */
1.1 niallo 141: }
1.4 niallo 142:
143: /*
144: * Load file contents
145: */
146: if ((bp = cvs_buf_load(argv[i], BUF_AUTOEXT)) == NULL) {
147: cvs_log(LP_ERR, "failed to load '%s'", argv[i]);
148: exit(1);
149: }
150:
1.6 niallo 151: if (rev == NULL)
152: frev = file->rf_head;
153: /*
154: * If no log message specified, get it interactively.
155: */
156: if (rcs_msg == NULL)
157: rcs_msg = checkin_getlogmsg(fpath, argv[i], frev);
158:
1.4 niallo 159: if (cvs_buf_putc(bp, '\0') < 0)
160: exit(1);
161:
162: filec = cvs_buf_release(bp);
163:
164: /*
165: * Remove the lock
166: */
167: if (rcs_lock_remove(file, frev) < 0) {
168: if (rcs_errno != RCS_ERR_NOENT)
169: cvs_log(LP_WARN, "failed to remove lock");
170: }
171:
172: /*
173: * Get RCS patch
174: */
175: if ((deltatext = checkin_diff_file(file, frev, argv[i])) == NULL) {
176: cvs_log(LP_ERR, "failed to get diff");
177: exit(1);
178: }
179:
180: /*
181: * Current head revision gets the RCS patch as rd_text
182: */
183: if (rcs_deltatext_set(file, file->rf_head, deltatext) == -1) {
1.7 ! niallo 184: cvs_log(LP_ERR,
! 185: "failed to set new rd_text for head rev");
1.4 niallo 186: exit (1);
187: }
188: /*
189: * Now add our new revision
190: */
191: if (rcs_rev_add(file, RCS_HEAD_REV, rcs_msg, -1) != 0) {
192: cvs_log(LP_ERR, "failed to add new revision");
193: exit(1);
194: }
195:
196: /*
197: * New head revision has to contain entire file;
198: */
199: if (rcs_deltatext_set(file, frev, filec) == -1) {
200: cvs_log(LP_ERR, "failed to set new head revision");
201: exit(1);
202: }
203:
204: free(deltatext);
205: free(filec);
206:
207: /* File will NOW be synced */
1.1 niallo 208: rcs_close(file);
1.4 niallo 209:
210: /* XXX:
211: * Delete the working file - we do not support -u/-l just yet
212: */
213: (void)unlink(argv[i]);
1.6 niallo 214: if (interactive) {
215: free(rcs_msg);
216: rcs_msg = NULL;
217: }
1.4 niallo 218: }
219:
220: return (0);
221: }
222:
223: static char *
224: checkin_diff_file(RCSFILE *rfp, RCSNUM *rev, const char *filename)
225: {
226: char path1[MAXPATHLEN], path2[MAXPATHLEN];
227: BUF *b1, *b2, *b3;
228: char rbuf[64], *deltatext;
229:
230: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
231:
232: if ((b1 = cvs_buf_load(filename, BUF_AUTOEXT)) == NULL) {
233: cvs_log(LP_ERR, "failed to load file: '%s'", filename);
234: return (NULL);
1.1 niallo 235: }
236:
1.4 niallo 237: if ((b2 = rcs_getrev(rfp, rev)) == NULL) {
238: cvs_log(LP_ERR, "failed to load revision");
239: cvs_buf_free(b1);
240: return (NULL);
241: }
242:
243: if ((b3 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL) {
244: cvs_log(LP_ERR, "failed to allocated buffer for diff");
245: cvs_buf_free(b1);
246: cvs_buf_free(b2);
247: return (NULL);
248: }
249:
250: strlcpy(path1, "/tmp/diff1.XXXXXXXXXX", sizeof(path1));
251: if (cvs_buf_write_stmp(b1, path1, 0600) == -1) {
252: cvs_log(LP_ERRNO, "could not write temporary file");
253: cvs_buf_free(b1);
254: cvs_buf_free(b2);
255: return (NULL);
256: }
257: cvs_buf_free(b1);
258:
259: strlcpy(path2, "/tmp/diff2.XXXXXXXXXX", sizeof(path2));
260: if (cvs_buf_write_stmp(b2, path2, 0600) == -1) {
261: cvs_buf_free(b2);
262: (void)unlink(path1);
263: return (NULL);
264: }
265: cvs_buf_free(b2);
266:
1.5 niallo 267: diff_format = D_RCSDIFF;
1.4 niallo 268: cvs_diffreg(path1, path2, b3);
269: (void)unlink(path1);
270: (void)unlink(path2);
271:
272: cvs_buf_putc(b3, '\0');
273: deltatext = (char *)cvs_buf_release(b3);
274:
275: return (deltatext);
1.6 niallo 276: }
277:
278: /*
279: * Get log message from user interactively.
280: */
281: static char *
282: checkin_getlogmsg(char *rcsfile, char *workingfile, RCSNUM *rev)
283: {
284: char *rcs_msg, buf[128], nrev[16], prev[16];
285: BUF *logbuf;
286: RCSNUM *tmprev;
287:
1.7 ! niallo 288: rcs_msg = NULL;
1.6 niallo 289: tmprev = rcsnum_alloc();
290: rcsnum_cpy(rev, tmprev, 16);
291: rcsnum_tostr(rev, prev, sizeof(prev));
292: rcsnum_tostr(rcsnum_inc(tmprev), nrev, sizeof(nrev));
293: rcsnum_free(tmprev);
294:
295: if ((logbuf = cvs_buf_alloc(64, BUF_AUTOEXT)) == NULL) {
1.7 ! niallo 296: cvs_log(LP_ERR, "failed to allocate log buffer");
1.6 niallo 297: return (NULL);
298: }
299: cvs_printf("%s <-- %s\n", rcsfile, workingfile);
1.7 ! niallo 300: cvs_printf("new revision: %s; previous revision: %s\n", nrev, prev);
1.6 niallo 301: cvs_printf("enter log message, terminated with single "
302: "'.' or end of file:\n");
303: cvs_printf(">> ");
304: for (;;) {
305: fgets(buf, (int)sizeof(buf), stdin);
306: if (feof(stdin) || ferror(stdin)
307: || buf[0] == '.')
308: break;
309: cvs_buf_append(logbuf, buf, strlen(buf));
310: cvs_printf(">> ");
311: }
312: rcs_msg = (char *)cvs_buf_release(logbuf);
313: return (rcs_msg);
1.1 niallo 314: }