1 /* $OpenBSD: commit.c,v 1.31 2005/04/24 02:06:27 joris 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 38 #include "cvs.h" 39 #include "log.h" 40 #include "buf.h" 41 #include "proto.h" 42 43 44 int cvs_commit_prepare(CVSFILE *, void *); 45 int cvs_commit_file(CVSFILE *, void *); 46 int cvs_commit_options(char *, int, char **, int *); 47 int cvs_commit_helper(void); 48 49 struct cvs_cmd_info cvs_commit = { 50 cvs_commit_options, 51 NULL, 52 cvs_commit_file, 53 NULL, 54 cvs_commit_helper, 55 CF_RECURSE | CF_IGNORE | CF_SORT, 56 CVS_REQ_CI, 57 CVS_CMD_ALLOWSPEC | CVS_CMD_NEEDLOG | CVS_CMD_SENDDIR | CVS_CMD_SENDARGS2 58 }; 59 60 static char *mfile = NULL; 61 62 int 63 cvs_commit_options(char *opt, int argc, char **argv, int *arg) 64 { 65 int ch; 66 67 while ((ch = getopt(argc, argv, opt)) != -1) { 68 switch (ch) { 69 case 'F': 70 mfile = optarg; 71 break; 72 case 'f': 73 /* XXX half-implemented */ 74 cvs_commit.file_flags &= ~CF_RECURSE; 75 break; 76 case 'l': 77 cvs_commit.file_flags &= ~CF_RECURSE; 78 break; 79 case 'm': 80 cvs_msg = strdup(optarg); 81 if (cvs_msg == NULL) { 82 cvs_log(LP_ERRNO, "failed to copy message"); 83 return (CVS_EX_USAGE); 84 } 85 break; 86 case 'R': 87 cvs_commit.file_flags |= CF_RECURSE; 88 break; 89 default: 90 return (CVS_EX_USAGE); 91 } 92 } 93 94 if ((cvs_msg != NULL) && (mfile != NULL)) { 95 cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive"); 96 return (CVS_EX_USAGE); 97 } 98 99 if ((mfile != NULL) && (cvs_msg = cvs_logmsg_open(mfile)) == NULL) 100 return (CVS_EX_DATA); 101 102 *arg = optind; 103 return (0); 104 } 105 106 int 107 cvs_commit_helper(void) 108 { 109 struct cvs_flist cl; 110 CVSFILE *cfp; 111 112 SIMPLEQ_INIT(&cl); 113 cvs_file_examine(cvs_files, cvs_commit_prepare, &cl); 114 if (SIMPLEQ_EMPTY(&cl)) 115 return (0); 116 117 if (cvs_msg == NULL) 118 cvs_msg = cvs_logmsg_get(CVS_FILE_NAME(cvs_files), 119 NULL, &cl, NULL); 120 121 while (!SIMPLEQ_EMPTY(&cl)) { 122 cfp = SIMPLEQ_FIRST(&cl); 123 SIMPLEQ_REMOVE_HEAD(&cl, cf_list); 124 cvs_file_free(cfp); 125 } 126 127 if (cvs_msg == NULL) 128 return (CVS_EX_DATA); 129 130 return (0); 131 } 132 133 /* 134 * cvs_commit_prepare() 135 * 136 * Examine the file <cf> to see if it will be part of the commit, in which 137 * case it gets added to the list passed as second argument. 138 */ 139 int 140 cvs_commit_prepare(CVSFILE *cf, void *arg) 141 { 142 CVSFILE *copy; 143 struct cvs_flist *clp = (struct cvs_flist *)arg; 144 145 if ((cf->cf_type == DT_REG) && ((cf->cf_cvstat == CVS_FST_MODIFIED) || 146 (cf->cf_cvstat == CVS_FST_ADDED) || 147 (cf->cf_cvstat == CVS_FST_REMOVED))) { 148 copy = cvs_file_copy(cf); 149 if (copy == NULL) 150 return (CVS_EX_DATA); 151 152 SIMPLEQ_INSERT_TAIL(clp, copy, cf_list); 153 } 154 155 return (0); 156 } 157 158 159 /* 160 * cvs_commit_file() 161 * 162 * Commit a single file. 163 */ 164 int 165 cvs_commit_file(CVSFILE *cf, void *arg) 166 { 167 int ret, l; 168 char *repo, rcspath[MAXPATHLEN], fpath[MAXPATHLEN]; 169 RCSFILE *rf; 170 struct cvsroot *root; 171 172 ret = 0; 173 rf = NULL; 174 repo = NULL; 175 root = CVS_DIR_ROOT(cf); 176 177 if (cf->cf_type == DT_DIR) { 178 if (root->cr_method != CVS_METHOD_LOCAL) { 179 if (cf->cf_cvstat != CVS_FST_UNKNOWN) 180 ret = cvs_senddir(root, cf); 181 } 182 183 return (ret); 184 } 185 186 cvs_file_getpath(cf, fpath, sizeof(fpath)); 187 188 if (cf->cf_parent != NULL) 189 repo = cf->cf_parent->cf_repo; 190 191 if ((cf->cf_cvstat == CVS_FST_ADDED) || 192 (cf->cf_cvstat == CVS_FST_MODIFIED) || 193 (cf->cf_cvstat == CVS_FST_REMOVED)) { 194 if (root->cr_method != CVS_METHOD_LOCAL) { 195 if (cvs_sendentry(root, cf) < 0) { 196 return (CVS_EX_PROTO); 197 } 198 199 /* if it's removed, don't bother sending a 200 * Modified request together with the file its 201 * contents. 202 */ 203 if (cf->cf_cvstat == CVS_FST_REMOVED) 204 return (0); 205 206 if (cvs_sendreq(root, CVS_REQ_MODIFIED, 207 CVS_FILE_NAME(cf)) < 0) { 208 return (CVS_EX_PROTO); 209 } 210 211 if (cvs_sendfile(root, fpath) < 0) { 212 return (CVS_EX_PROTO); 213 } 214 } 215 } 216 217 l = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s", 218 root->cr_dir, repo, fpath, RCS_FILE_EXT); 219 if (l == -1 || l >= (int)sizeof(rcspath)) { 220 errno = ENAMETOOLONG; 221 cvs_log(LP_ERRNO, "%s", rcspath); 222 return (-1); 223 } 224 225 return (0); 226 } 227