1 /* $OpenBSD: commit.c,v 1.13 2004/12/21 18:32:10 jfb 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/queue.h> 29 #include <sys/stat.h> 30 31 #include <errno.h> 32 #include <stdio.h> 33 #include <fcntl.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <string.h> 37 #include <sysexits.h> 38 39 #include "cvs.h" 40 #include "log.h" 41 #include "buf.h" 42 #include "proto.h" 43 44 45 46 47 int cvs_commit_prepare (CVSFILE *, void *); 48 int cvs_commit_file (CVSFILE *, void *); 49 50 51 /* 52 * cvs_commit() 53 * 54 * Handler for the `cvs commit' command. 55 */ 56 int 57 cvs_commit(int argc, char **argv) 58 { 59 int i, ch, flags; 60 char *msg, *mfile; 61 struct cvs_flist cl; 62 struct cvsroot *root; 63 64 flags = CF_RECURSE|CF_IGNORE|CF_SORT; 65 mfile = NULL; 66 msg = NULL; 67 TAILQ_INIT(&cl); 68 69 while ((ch = getopt(argc, argv, "F:flm:Rr:")) != -1) { 70 switch (ch) { 71 case 'F': 72 mfile = optarg; 73 break; 74 case 'f': 75 /* XXX half-implemented */ 76 flags &= ~CF_RECURSE; 77 break; 78 case 'l': 79 flags &= ~CF_RECURSE; 80 break; 81 case 'm': 82 msg = optarg; 83 break; 84 case 'R': 85 flags |= CF_RECURSE; 86 break; 87 default: 88 return (EX_USAGE); 89 } 90 } 91 92 if ((msg != NULL) && (mfile != NULL)) { 93 cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive"); 94 return (EX_USAGE); 95 } 96 97 if ((mfile != NULL) && (msg = cvs_logmsg_open(mfile)) == NULL) 98 return (EX_DATAERR); 99 100 argc -= optind; 101 argv += optind; 102 103 if (argc == 0) { 104 cvs_files = cvs_file_get(".", flags); 105 } else { 106 cvs_files = cvs_file_getspec(argv, argc, flags); 107 } 108 if (cvs_files == NULL) 109 return (EX_DATAERR); 110 111 cvs_file_examine(cvs_files, cvs_commit_prepare, &cl); 112 if (TAILQ_EMPTY(&cl)) 113 return (0); 114 115 if (msg == NULL) { 116 msg = cvs_logmsg_get(CVS_FILE_NAME(cvs_files), NULL, &cl, NULL); 117 if (msg == NULL) 118 return (1); 119 } 120 121 root = CVS_DIR_ROOT(cvs_files); 122 if (root == NULL) { 123 cvs_log(LP_ERR, 124 "No CVSROOT specified! Please use the `-d' option"); 125 cvs_log(LP_ERR, 126 "or set the CVSROOT environment variable."); 127 return (EX_USAGE); 128 } 129 if ((root->cr_method != CVS_METHOD_LOCAL) && 130 ((cvs_connect(root) < 0) || (cvs_logmsg_send(root, msg) < 0))) 131 return (EX_PROTOCOL); 132 133 cvs_file_examine(cvs_files, cvs_commit_file, &cl); 134 135 if (root->cr_method != CVS_METHOD_LOCAL) { 136 if (cvs_senddir(root, cvs_files) < 0) 137 return (EX_PROTOCOL); 138 for (i = 0; i < argc; i++) 139 if (cvs_sendarg(root, argv[i], 0) < 0) 140 return (EX_PROTOCOL); 141 if (cvs_sendreq(root, CVS_REQ_CI, NULL) < 0) 142 return (EX_PROTOCOL); 143 } 144 145 return (0); 146 } 147 148 149 /* 150 * cvs_commit_prepare() 151 * 152 * Examine the file <cf> to see if it will be part of the commit, in which 153 * case it gets added to the list passed as second argument. 154 */ 155 int 156 cvs_commit_prepare(CVSFILE *cf, void *arg) 157 { 158 CVSFILE *copy; 159 struct cvs_flist *clp = (struct cvs_flist *)arg; 160 161 if ((cf->cf_type == DT_REG) && (cf->cf_cvstat == CVS_FST_MODIFIED)) { 162 copy = cvs_file_copy(cf); 163 if (copy == NULL) 164 return (-1); 165 166 TAILQ_INSERT_TAIL(clp, copy, cf_list); 167 } 168 169 return (0); 170 } 171 172 173 /* 174 * cvs_commit_file() 175 * 176 * Commit a single file. 177 */ 178 int 179 cvs_commit_file(CVSFILE *cf, void *arg) 180 { 181 int ret; 182 char *repo, rcspath[MAXPATHLEN], fpath[MAXPATHLEN]; 183 RCSFILE *rf; 184 struct cvsroot *root; 185 struct cvs_ent *entp; 186 187 ret = 0; 188 rf = NULL; 189 repo = NULL; 190 root = CVS_DIR_ROOT(cf); 191 192 if (cf->cf_type == DT_DIR) { 193 if (root->cr_method != CVS_METHOD_LOCAL) { 194 if (cf->cf_cvstat != CVS_FST_UNKNOWN) 195 ret = cvs_senddir(root, cf); 196 } 197 198 return (ret); 199 } 200 201 cvs_file_getpath(cf, fpath, sizeof(fpath)); 202 203 if (cf->cf_parent != NULL) 204 repo = cf->cf_parent->cf_ddat->cd_repo; 205 206 entp = cvs_ent_getent(fpath); 207 if (entp == NULL) 208 return (-1); 209 210 if ((cf->cf_cvstat == CVS_FST_ADDED) || 211 (cf->cf_cvstat == CVS_FST_MODIFIED)) { 212 if ((root->cr_method != CVS_METHOD_LOCAL) && 213 (cvs_sendentry(root, entp) < 0)) { 214 cvs_ent_free(entp); 215 return (-1); 216 } 217 218 cvs_sendreq(root, CVS_REQ_MODIFIED, CVS_FILE_NAME(cf)); 219 cvs_sendfile(root, fpath); 220 } 221 222 snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s", 223 root->cr_dir, repo, fpath, RCS_FILE_EXT); 224 225 cvs_ent_free(entp); 226 227 return (0); 228 } 229