1 /* $OpenBSD: commit.c,v 1.4 2004/11/09 21:11:37 krapht Exp $ */ 2 /* 3 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@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 conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions 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/types.h> 28 #include <sys/stat.h> 29 30 #include <errno.h> 31 #include <stdio.h> 32 #include <fcntl.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <sysexits.h> 37 38 #include "cvs.h" 39 #include "log.h" 40 #include "proto.h" 41 42 43 #define CVS_COMMIT_BIGMSG 8000 44 #define CVS_COMMIT_FTMPL "/tmp/cvsXXXXXXXXXX" 45 #define CVS_COMMIT_LOGPREFIX "CVS:" 46 #define CVS_COMMIT_LOGLINE \ 47 "----------------------------------------------------------------------" 48 49 50 51 static char* cvs_commit_openmsg (const char *); 52 static char* cvs_commit_getmsg (const char *); 53 54 55 /* 56 * cvs_commit() 57 * 58 * Handler for the `cvs commit' command. 59 */ 60 61 int 62 cvs_commit(int argc, char **argv) 63 { 64 int ch, recurse; 65 char *msg, *mfile; 66 67 recurse = 1; 68 mfile = NULL; 69 msg = NULL; 70 71 #if 0 72 cvs_commit_getmsg("."); 73 #endif 74 75 while ((ch = getopt(argc, argv, "F:flm:R")) != -1) { 76 switch (ch) { 77 case 'F': 78 mfile = optarg; 79 break; 80 case 'f': 81 recurse = 0; 82 break; 83 case 'l': 84 recurse = 0; 85 break; 86 case 'm': 87 msg = optarg; 88 break; 89 case 'R': 90 recurse = 1; 91 break; 92 default: 93 return (EX_USAGE); 94 } 95 } 96 97 if ((msg != NULL) && (mfile != NULL)) { 98 cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive"); 99 return (EX_USAGE); 100 } 101 102 if ((mfile != NULL) && (msg = cvs_commit_openmsg(mfile)) == NULL) 103 return (EX_DATAERR); 104 105 argc -= optind; 106 argv += optind; 107 108 return (0); 109 } 110 111 112 /* 113 * cvs_commit_openmsg() 114 * 115 * Open the file specified by <path> and allocate a buffer large enough to 116 * hold all of the file's contents. The returned value must later be freed 117 * using the free() function. 118 * Returns a pointer to the allocated buffer on success, or NULL on failure. 119 */ 120 121 static char* 122 cvs_commit_openmsg(const char *path) 123 { 124 int fd, ch; 125 size_t sz; 126 char buf[32], *msg; 127 struct stat st; 128 129 if (stat(path, &st) == -1) { 130 cvs_log(LP_ERRNO, "failed to stat `%s'", path); 131 return (NULL); 132 } 133 134 if (!S_ISREG(st.st_mode)) { 135 cvs_log(LP_ERR, "message file must be a regular file"); 136 return (NULL); 137 } 138 139 if (st.st_size > CVS_COMMIT_BIGMSG) { 140 do { 141 fprintf(stderr, 142 "The specified message file seems big. " 143 "Proceed anyways? (y/n) "); 144 if (fgets(buf, sizeof(buf), stdin) == NULL) { 145 cvs_log(LP_ERRNO, 146 "failed to read from standard input"); 147 return (NULL); 148 } 149 150 sz = strlen(buf); 151 if ((sz == 0) || (sz > 2) || 152 ((buf[sz] != 'y') && (buf[sz] != 'n'))) { 153 fprintf(stderr, "invalid input\n"); 154 continue; 155 } 156 else if (buf[sz] == 'y') 157 break; 158 else if (buf[sz] == 'n') { 159 cvs_log(LP_ERR, "aborted by user"); 160 return (NULL); 161 } 162 163 } while (1); 164 } 165 166 sz = st.st_size + 1; 167 168 msg = (char *)malloc(sz); 169 if (msg == NULL) { 170 cvs_log(LP_ERRNO, "failed to allocate message buffer"); 171 return (NULL); 172 } 173 174 fd = open(path, O_RDONLY, 0); 175 if (fd == -1) { 176 cvs_log(LP_ERRNO, "failed to open message file `%s'", path); 177 return (NULL); 178 } 179 180 if (read(fd, msg, sz - 1) == -1) { 181 cvs_log(LP_ERRNO, "failed to read CVS commit message"); 182 return (NULL); 183 } 184 msg[sz - 1] = '\0'; 185 186 return (msg); 187 } 188 189 190 /* 191 * cvs_commit_getmsg() 192 * 193 * Get a commit log message by forking the user's editor. 194 * Returns the message in a dynamically allocated string on success, NULL on 195 * failure. 196 */ 197 198 static char* 199 cvs_commit_getmsg(const char *dir) 200 { 201 int ret, fd, argc, fds[3]; 202 char *argv[4], path[MAXPATHLEN], *msg; 203 FILE *fp; 204 205 fds[0] = -1; 206 fds[1] = -1; 207 fds[2] = -1; 208 strlcpy(path, CVS_COMMIT_FTMPL, sizeof(path)); 209 argc = 0; 210 argv[argc++] = cvs_editor; 211 argv[argc++] = path; 212 argv[argc] = NULL; 213 214 if ((fd = mkstemp(path)) == -1) { 215 cvs_log(LP_ERRNO, "failed to create temporary file"); 216 return (NULL); 217 } 218 219 fp = fdopen(fd, "w"); 220 if (fp == NULL) { 221 cvs_log(LP_ERRNO, "failed to fdopen"); 222 exit(1); 223 } else { 224 fprintf(fp, 225 "\n%s %s\n%s Enter Log. Lines beginning with `%s' are " 226 "removed automatically\n%s\n%s Commiting in %s\n" 227 "%s\n%s Modified Files:\n", 228 CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGLINE, 229 CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX, 230 CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX, 231 dir, CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX); 232 233 /* XXX list files here */ 234 235 fprintf(fp, "%s %s\n", CVS_COMMIT_LOGPREFIX, 236 CVS_COMMIT_LOGLINE); 237 } 238 (void)fflush(fp); 239 (void)fclose(fp); 240 241 do { 242 ret = cvs_exec(argc, argv, fds); 243 if (ret == -1) { 244 fprintf(stderr, 245 "Log message unchanged or not specified\n" 246 "a)bort, c)ontinue, e)dit, !)reuse this message " 247 "unchanged for remaining dirs\nAction: () "); 248 249 ret = getchar(); 250 if (ret == 'a') { 251 cvs_log(LP_ERR, "aborted by user"); 252 break; 253 } else if (ret == 'c') { 254 } else if (ret == 'e') { 255 } else if (ret == '!') { 256 } 257 258 } 259 } while (0); 260 261 (void)close(fd); 262 263 return (msg); 264 } 265 266 267 /* 268 * cvs_commit_gettmpl() 269 * 270 * Get the template to display when invoking the editor to get a commit 271 * message. 272 */ 273 274 cvs_commit_gettmpl(void) 275 { 276 277 } 278