1 /* $OpenBSD: add.c,v 1.77 2007/02/22 06:42:09 otto Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/stat.h> 20 21 #include <errno.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #include "cvs.h" 26 #include "remote.h" 27 28 extern char *__progname; 29 30 void cvs_add_local(struct cvs_file *); 31 void cvs_add_entry(struct cvs_file *); 32 33 static void add_directory(struct cvs_file *); 34 static void add_file(struct cvs_file *); 35 static void add_entry(struct cvs_file *); 36 37 static int kflag = RCS_KWEXP_DEFAULT; 38 static char kbuf[8], *koptstr; 39 40 char *logmsg; 41 42 struct cvs_cmd cvs_cmd_add = { 43 CVS_OP_ADD, 0, "add", 44 { "ad", "new" }, 45 "Add a new file or directory to the repository", 46 "[-k mode] [-m message] ...", 47 "k:m:", 48 NULL, 49 cvs_add 50 }; 51 52 int 53 cvs_add(int argc, char **argv) 54 { 55 int ch; 56 int flags; 57 struct cvs_recursion cr; 58 59 flags = CR_REPO; 60 61 while ((ch = getopt(argc, argv, cvs_cmd_add.cmd_opts)) != -1) { 62 switch (ch) { 63 case 'k': 64 koptstr = optarg; 65 kflag = rcs_kflag_get(koptstr); 66 if (RCS_KWEXP_INVAL(kflag)) { 67 cvs_log(LP_ERR, 68 "invalid RCS keyword expension mode"); 69 fatal("%s", cvs_cmd_add.cmd_synopsis); 70 } 71 (void)xsnprintf(kbuf, sizeof(kbuf), "-k%s", koptstr); 72 break; 73 case 'm': 74 logmsg = xstrdup(optarg); 75 break; 76 default: 77 fatal("%s", cvs_cmd_add.cmd_synopsis); 78 } 79 } 80 81 argc -= optind; 82 argv += optind; 83 84 if (argc == 0) 85 fatal("%s", cvs_cmd_add.cmd_synopsis); 86 87 cr.enterdir = NULL; 88 cr.leavedir = NULL; 89 90 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 91 cvs_client_connect_to_server(); 92 cr.fileproc = cvs_client_sendfile; 93 94 if (kflag != RCS_KWEXP_DEFAULT) 95 cvs_client_send_request("Argument %s", kbuf); 96 97 if (logmsg != NULL) 98 cvs_client_send_request("Argument -m%s", logmsg); 99 } else { 100 cr.fileproc = cvs_add_local; 101 } 102 103 cr.flags = flags; 104 105 cvs_file_run(argc, argv, &cr); 106 107 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 108 cvs_client_send_files(argv, argc); 109 cvs_client_senddir("."); 110 cvs_client_send_request("add"); 111 cvs_client_get_responses(); 112 113 if (server_response == SERVER_OK) { 114 cr.fileproc = cvs_add_entry; 115 cvs_file_run(argc, argv, &cr); 116 } 117 } 118 119 return (0); 120 } 121 122 void 123 cvs_add_entry(struct cvs_file *cf) 124 { 125 char entry[CVS_ENT_MAXLINELEN]; 126 CVSENTRIES *entlist; 127 128 if (cf->file_type == CVS_DIR) { 129 (void)xsnprintf(entry, CVS_ENT_MAXLINELEN, 130 "D/%s/////", cf->file_name); 131 132 entlist = cvs_ent_open(cf->file_wd); 133 cvs_ent_add(entlist, entry); 134 cvs_ent_close(entlist, ENT_SYNC); 135 } else { 136 add_entry(cf); 137 } 138 } 139 140 void 141 cvs_add_local(struct cvs_file *cf) 142 { 143 cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path); 144 145 cvs_file_classify(cf, NULL); 146 147 /* dont use `cvs add *' */ 148 if (strcmp(cf->file_name, ".") == 0 || 149 strcmp(cf->file_name, "..") == 0 || 150 strcmp(cf->file_name, CVS_PATH_CVSDIR) == 0) { 151 if (verbosity > 1) 152 cvs_log(LP_ERR, 153 "cannot add special file `%s'; skipping", 154 cf->file_name); 155 return; 156 } 157 158 if (cf->file_type == CVS_DIR) 159 add_directory(cf); 160 else 161 add_file(cf); 162 } 163 164 static void 165 add_directory(struct cvs_file *cf) 166 { 167 int added, nb; 168 struct stat st; 169 CVSENTRIES *entlist; 170 char *date, entry[MAXPATHLEN], msg[1024], repo[MAXPATHLEN], *tag, *p; 171 172 cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path); 173 174 (void)xsnprintf(entry, MAXPATHLEN, "%s%s", 175 cf->file_rpath, RCS_FILE_EXT); 176 177 added = 1; 178 if (stat(entry, &st) != -1) { 179 cvs_log(LP_NOTICE, "cannot add directory %s: " 180 "a file with that name already exists", 181 cf->file_path); 182 added = 0; 183 } else { 184 /* Let's see if we have any per-directory tags first. */ 185 cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb); 186 187 (void)xsnprintf(entry, MAXPATHLEN, "%s/%s", 188 cf->file_path, CVS_PATH_CVSDIR); 189 190 if (stat(entry, &st) != -1) { 191 if (!S_ISDIR(st.st_mode)) { 192 cvs_log(LP_ERR, "%s exists but is not " 193 "directory", entry); 194 } else { 195 cvs_log(LP_NOTICE, "%s already exists", 196 entry); 197 } 198 added = 0; 199 } else if (cvs_noexec != 1) { 200 if (mkdir(cf->file_rpath, 0755) == -1 && 201 errno != EEXIST) 202 fatal("add_directory: %s: %s", cf->file_path, 203 strerror(errno)); 204 205 cvs_get_repository_name(cf->file_wd, repo, 206 MAXPATHLEN); 207 208 (void)xsnprintf(entry, MAXPATHLEN, "%s/%s", 209 repo, cf->file_name); 210 211 cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir, 212 entry, tag, date, nb); 213 214 p = xmalloc(CVS_ENT_MAXLINELEN); 215 (void)xsnprintf(p, CVS_ENT_MAXLINELEN, 216 "D/%s/////", cf->file_name); 217 entlist = cvs_ent_open(cf->file_wd); 218 cvs_ent_add(entlist, p); 219 cvs_ent_close(entlist, ENT_SYNC); 220 } 221 } 222 223 if (added == 1) { 224 (void)xsnprintf(msg, sizeof(msg), 225 "Directory %s added to the repository", cf->file_rpath); 226 227 if (tag != NULL) { 228 (void)strlcat(msg, 229 "\n--> Using per-directory sticky tag ", 230 sizeof(msg)); 231 (void)strlcat(msg, tag, sizeof(msg)); 232 } 233 if (date != NULL) { 234 (void)strlcat(msg, 235 "\n--> Using per-directory sticky date ", 236 sizeof(msg)); 237 (void)strlcat(msg, date, sizeof(msg)); 238 } 239 cvs_printf("%s\n", msg); 240 241 if (tag != NULL) 242 xfree(tag); 243 if (date != NULL) 244 xfree(date); 245 } 246 247 cf->file_status = FILE_SKIP; 248 } 249 250 static void 251 add_file(struct cvs_file *cf) 252 { 253 int added, stop; 254 char revbuf[16]; 255 RCSNUM *head; 256 257 if (cf->file_rcs != NULL) { 258 head = rcs_head_get(cf->file_rcs); 259 rcsnum_tostr(head, revbuf, sizeof(revbuf)); 260 rcsnum_free(head); 261 } 262 263 added = stop = 0; 264 switch (cf->file_status) { 265 case FILE_ADDED: 266 if (verbosity > 1) 267 cvs_log(LP_NOTICE, "%s has already been entered", 268 cf->file_path); 269 stop = 1; 270 break; 271 case FILE_REMOVED: 272 if (cf->file_rcs == NULL) { 273 cvs_log(LP_NOTICE, "cannot resurrect %s; " 274 "RCS file removed by second party", cf->file_name); 275 } else if (cf->fd == -1) { 276 add_entry(cf); 277 278 /* Restore the file. */ 279 head = rcs_head_get(cf->file_rcs); 280 cvs_checkout_file(cf, head, 0); 281 rcsnum_free(head); 282 283 cvs_printf("U %s\n", cf->file_path); 284 285 cvs_log(LP_NOTICE, "%s, version %s, resurrected", 286 cf->file_name, revbuf); 287 288 cf->file_status = FILE_UPTODATE; 289 } 290 stop = 1; 291 break; 292 case FILE_CONFLICT: 293 case FILE_LOST: 294 case FILE_MODIFIED: 295 case FILE_UPTODATE: 296 if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) { 297 cvs_log(LP_NOTICE, "%s already exists, with version " 298 "number %s", cf->file_path, revbuf); 299 stop = 1; 300 } 301 break; 302 case FILE_UNKNOWN: 303 if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) { 304 cvs_log(LP_NOTICE, "re-adding file %s " 305 "(instead of dead revision %s)", 306 cf->file_path, revbuf); 307 added++; 308 } else if (cf->fd != -1) { 309 cvs_log(LP_NOTICE, "scheduling file '%s' for addition", 310 cf->file_path); 311 added++; 312 } else { 313 stop = 1; 314 } 315 break; 316 default: 317 break; 318 } 319 320 if (stop == 1) 321 return; 322 323 add_entry(cf); 324 325 if (added != 0) { 326 cvs_log(LP_NOTICE, "use '%s commit' to add %s " 327 "permanently", __progname, 328 (added == 1) ? "this file" : "these files"); 329 } 330 } 331 332 static void 333 add_entry(struct cvs_file *cf) 334 { 335 FILE *fp; 336 char entry[CVS_ENT_MAXLINELEN], path[MAXPATHLEN], revbuf[16], tbuf[32]; 337 CVSENTRIES *entlist; 338 339 if (cvs_noexec == 1) 340 return; 341 342 if (cf->file_status == FILE_REMOVED) { 343 rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf)); 344 345 ctime_r(&cf->file_ent->ce_mtime, tbuf); 346 if (tbuf[strlen(tbuf) - 1] == '\n') 347 tbuf[strlen(tbuf) - 1] = '\0'; 348 349 /* Remove the '-' prefixing the version number. */ 350 (void)xsnprintf(entry, CVS_ENT_MAXLINELEN, 351 "/%s/%s/%s/%s/", cf->file_name, revbuf, tbuf, 352 cf->file_ent->ce_opts ? cf->file_ent->ce_opts : ""); 353 } else { 354 if (logmsg != NULL) { 355 (void)xsnprintf(path, MAXPATHLEN, "%s/%s%s", 356 CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT); 357 358 if ((fp = fopen(path, "w+")) == NULL) 359 fatal("add_entry: fopen `%s': %s", 360 path, strerror(errno)); 361 362 if (fputs(logmsg, fp) == EOF) { 363 (void)unlink(path); 364 fatal("add_entry: fputs `%s': %s", 365 path, strerror(errno)); 366 } 367 (void)fclose(fp); 368 } 369 370 (void)xsnprintf(entry, CVS_ENT_MAXLINELEN, 371 "/%s/0/Initial %s/%s/", cf->file_name, cf->file_name, 372 (kflag != RCS_KWEXP_DEFAULT) ? kbuf : ""); 373 } 374 375 entlist = cvs_ent_open(cf->file_wd); 376 cvs_ent_add(entlist, entry); 377 cvs_ent_close(entlist, ENT_SYNC); 378 } 379