Annotation of src/usr.bin/rcs/ci.c, Revision 1.6
1.6 ! niallo 1: /* $OpenBSD: ci.c,v 1.5 2005/10/08 11:50:59 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:
140: if (dflag) {
1.6 ! niallo 141: /* XXX */
1.1 niallo 142: }
1.4 niallo 143:
144:
145: /*
146: * Load file contents
147: */
148: if ((bp = cvs_buf_load(argv[i], BUF_AUTOEXT)) == NULL) {
149: cvs_log(LP_ERR, "failed to load '%s'", argv[i]);
150: exit(1);
151: }
152:
1.6 ! niallo 153: if (rev == NULL)
! 154: frev = file->rf_head;
! 155: /*
! 156: * If no log message specified, get it interactively.
! 157: */
! 158: if (rcs_msg == NULL)
! 159: rcs_msg = checkin_getlogmsg(fpath, argv[i], frev);
! 160:
1.4 niallo 161: if (cvs_buf_putc(bp, '\0') < 0)
162: exit(1);
163:
164: filec = cvs_buf_release(bp);
165:
166: /*
167: * Remove the lock
168: */
169: if (rcs_lock_remove(file, frev) < 0) {
170: if (rcs_errno != RCS_ERR_NOENT)
171: cvs_log(LP_WARN, "failed to remove lock");
172: }
173:
174: /*
175: * Get RCS patch
176: */
177: if ((deltatext = checkin_diff_file(file, frev, argv[i])) == NULL) {
178: cvs_log(LP_ERR, "failed to get diff");
179: exit(1);
180: }
181:
182: /*
183: * Current head revision gets the RCS patch as rd_text
184: */
185: if (rcs_deltatext_set(file, file->rf_head, deltatext) == -1) {
186: cvs_log(LP_ERR, "failed to set new rd_text for head rev");
187: exit (1);
188: }
189: /*
190: * Now add our new revision
191: */
192: if (rcs_rev_add(file, RCS_HEAD_REV, rcs_msg, -1) != 0) {
193: cvs_log(LP_ERR, "failed to add new revision");
194: exit(1);
195: }
196:
197: /*
198: * New head revision has to contain entire file;
199: */
200: if (rcs_deltatext_set(file, frev, filec) == -1) {
201: cvs_log(LP_ERR, "failed to set new head revision");
202: exit(1);
203: }
204:
205: free(deltatext);
206: free(filec);
207:
208: /* File will NOW be synced */
1.1 niallo 209: rcs_close(file);
1.4 niallo 210:
211: /* XXX:
212: * Delete the working file - we do not support -u/-l just yet
213: */
214: (void)unlink(argv[i]);
1.6 ! niallo 215: if (interactive) {
! 216: free(rcs_msg);
! 217: rcs_msg = NULL;
! 218: }
1.4 niallo 219: }
220:
221: return (0);
222: }
223:
224: static char *
225: checkin_diff_file(RCSFILE *rfp, RCSNUM *rev, const char *filename)
226: {
227: char path1[MAXPATHLEN], path2[MAXPATHLEN];
228: BUF *b1, *b2, *b3;
229: char rbuf[64], *deltatext;
230:
231: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
232:
233: if ((b1 = cvs_buf_load(filename, BUF_AUTOEXT)) == NULL) {
234: cvs_log(LP_ERR, "failed to load file: '%s'", filename);
235: return (NULL);
1.1 niallo 236: }
237:
1.4 niallo 238: if ((b2 = rcs_getrev(rfp, rev)) == NULL) {
239: cvs_log(LP_ERR, "failed to load revision");
240: cvs_buf_free(b1);
241: return (NULL);
242: }
243:
244: if ((b3 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL) {
245: cvs_log(LP_ERR, "failed to allocated buffer for diff");
246: cvs_buf_free(b1);
247: cvs_buf_free(b2);
248: return (NULL);
249: }
250:
251: strlcpy(path1, "/tmp/diff1.XXXXXXXXXX", sizeof(path1));
252: if (cvs_buf_write_stmp(b1, path1, 0600) == -1) {
253: cvs_log(LP_ERRNO, "could not write temporary file");
254: cvs_buf_free(b1);
255: cvs_buf_free(b2);
256: return (NULL);
257: }
258: cvs_buf_free(b1);
259:
260: strlcpy(path2, "/tmp/diff2.XXXXXXXXXX", sizeof(path2));
261: if (cvs_buf_write_stmp(b2, path2, 0600) == -1) {
262: cvs_buf_free(b2);
263: (void)unlink(path1);
264: return (NULL);
265: }
266: cvs_buf_free(b2);
267:
1.5 niallo 268: diff_format = D_RCSDIFF;
1.4 niallo 269: cvs_diffreg(path1, path2, b3);
270: (void)unlink(path1);
271: (void)unlink(path2);
272:
273: cvs_buf_putc(b3, '\0');
274: deltatext = (char *)cvs_buf_release(b3);
275:
276: return (deltatext);
1.6 ! niallo 277: }
! 278:
! 279: /*
! 280: * Get log message from user interactively.
! 281: */
! 282: static char *
! 283: checkin_getlogmsg(char *rcsfile, char *workingfile, RCSNUM *rev)
! 284: {
! 285: char *rcs_msg, buf[128], nrev[16], prev[16];
! 286: BUF *logbuf;
! 287: RCSNUM *tmprev;
! 288:
! 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) {
! 296: cvs_log(LP_ERR,
! 297: "failed to allocate log buffer");
! 298: return (NULL);
! 299: }
! 300: cvs_printf("%s <-- %s\n", rcsfile, workingfile);
! 301: cvs_printf("new revision: %s; previous revision: %s\n",
! 302: nrev, prev);
! 303: cvs_printf("enter log message, terminated with single "
! 304: "'.' or end of file:\n");
! 305: cvs_printf(">> ");
! 306: for (;;) {
! 307: fgets(buf, (int)sizeof(buf), stdin);
! 308: if (feof(stdin) || ferror(stdin)
! 309: || buf[0] == '.')
! 310: break;
! 311: cvs_buf_append(logbuf, buf, strlen(buf));
! 312: cvs_printf(">> ");
! 313: }
! 314: rcs_msg = (char *)cvs_buf_release(logbuf);
! 315: return (rcs_msg);
1.1 niallo 316: }