1 /* $OpenBSD: add.c,v 1.97 2008/03/09 03:41:55 joris 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_entry(struct cvs_file *); 31 void cvs_add_remote(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 int kflag = 0; 38 static char kbuf[8]; 39 40 char *logmsg; 41 42 struct cvs_cmd cvs_cmd_add = { 43 CVS_OP_ADD, CVS_USE_WDIR, "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 kflag = rcs_kflag_get(optarg); 65 if (RCS_KWEXP_INVAL(kflag)) { 66 cvs_log(LP_ERR, 67 "invalid RCS keyword expension mode"); 68 fatal("%s", cvs_cmd_add.cmd_synopsis); 69 } 70 (void)xsnprintf(kbuf, sizeof(kbuf), "-k%s", optarg); 71 break; 72 case 'm': 73 logmsg = optarg; 74 break; 75 default: 76 fatal("%s", cvs_cmd_add.cmd_synopsis); 77 } 78 } 79 80 argc -= optind; 81 argv += optind; 82 83 if (argc == 0) 84 fatal("%s", cvs_cmd_add.cmd_synopsis); 85 86 cr.enterdir = NULL; 87 cr.leavedir = NULL; 88 89 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 90 cvs_client_connect_to_server(); 91 cr.fileproc = cvs_add_remote; 92 flags = 0; 93 94 if (kflag) 95 cvs_client_send_request("Argument %s", kbuf); 96 97 if (logmsg != NULL) 98 cvs_client_send_logmsg(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_senddir("."); 109 cvs_client_send_files(argv, argc); 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; 126 CVSENTRIES *entlist; 127 128 if (cf->file_type == CVS_DIR) { 129 entry = xmalloc(CVS_ENT_MAXLINELEN); 130 cvs_ent_line_str(cf->file_name, NULL, NULL, NULL, NULL, 1, 0, 131 entry, CVS_ENT_MAXLINELEN); 132 133 entlist = cvs_ent_open(cf->file_wd); 134 cvs_ent_add(entlist, entry); 135 cvs_ent_close(entlist, ENT_SYNC); 136 137 xfree(entry); 138 } else { 139 add_entry(cf); 140 } 141 } 142 143 void 144 cvs_add_local(struct cvs_file *cf) 145 { 146 cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path); 147 148 if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_UPDATE) 149 cvs_file_classify(cf, cvs_directory_tag); 150 151 /* dont use `cvs add *' */ 152 if (strcmp(cf->file_name, ".") == 0 || 153 strcmp(cf->file_name, "..") == 0 || 154 strcmp(cf->file_name, CVS_PATH_CVSDIR) == 0) { 155 if (verbosity > 1) 156 cvs_log(LP_ERR, 157 "cannot add special file `%s'; skipping", 158 cf->file_name); 159 return; 160 } 161 162 if (cf->file_type == CVS_DIR) 163 add_directory(cf); 164 else 165 add_file(cf); 166 } 167 168 void 169 cvs_add_remote(struct cvs_file *cf) 170 { 171 char path[MAXPATHLEN]; 172 173 cvs_log(LP_TRACE, "cvs_add_remote(%s)", cf->file_path); 174 175 cvs_file_classify(cf, cvs_directory_tag); 176 177 if (cf->file_type == CVS_DIR) { 178 cvs_get_repository_path(cf->file_wd, path, MAXPATHLEN); 179 if (strlcat(path, "/", sizeof(path)) >= sizeof(path)) 180 fatal("cvs_add_remote: truncation"); 181 if (strlcat(path, cf->file_path, sizeof(path)) >= sizeof(path)) 182 fatal("cvs_add_remote: truncation"); 183 cvs_client_send_request("Directory %s\n%s", cf->file_path, 184 path); 185 186 add_directory(cf); 187 } else { 188 cvs_client_sendfile(cf); 189 } 190 } 191 192 static void 193 add_directory(struct cvs_file *cf) 194 { 195 int added, nb; 196 struct stat st; 197 CVSENTRIES *entlist; 198 char *date, entry[MAXPATHLEN], msg[1024], repo[MAXPATHLEN], *tag, *p; 199 200 cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path); 201 202 (void)xsnprintf(entry, MAXPATHLEN, "%s%s", 203 cf->file_rpath, RCS_FILE_EXT); 204 205 added = 1; 206 if (stat(entry, &st) != -1) { 207 cvs_log(LP_NOTICE, "cannot add directory %s: " 208 "a file with that name already exists", 209 cf->file_path); 210 added = 0; 211 } else { 212 /* Let's see if we have any per-directory tags first. */ 213 cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb); 214 215 (void)xsnprintf(entry, MAXPATHLEN, "%s/%s", 216 cf->file_path, CVS_PATH_CVSDIR); 217 218 if (cvs_server_active) { 219 if (mkdir(cf->file_rpath, 0755) == -1 && 220 errno != EEXIST) 221 fatal("add_directory: %s: %s", cf->file_rpath, 222 strerror(errno)); 223 } else if (stat(entry, &st) != -1) { 224 if (!S_ISDIR(st.st_mode)) { 225 cvs_log(LP_ERR, "%s exists but is not " 226 "directory", entry); 227 } else { 228 cvs_log(LP_NOTICE, "%s already exists", 229 entry); 230 } 231 added = 0; 232 } else if (cvs_noexec != 1) { 233 if (mkdir(cf->file_rpath, 0755) == -1 && 234 errno != EEXIST) 235 fatal("add_directory: %s: %s", cf->file_rpath, 236 strerror(errno)); 237 238 cvs_get_repository_name(cf->file_wd, repo, 239 MAXPATHLEN); 240 241 (void)xsnprintf(entry, MAXPATHLEN, "%s/%s", 242 repo, cf->file_name); 243 244 cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir, 245 entry, tag, date); 246 247 p = xmalloc(CVS_ENT_MAXLINELEN); 248 cvs_ent_line_str(cf->file_name, NULL, NULL, NULL, 249 NULL, 1, 0, p, CVS_ENT_MAXLINELEN); 250 251 entlist = cvs_ent_open(cf->file_wd); 252 cvs_ent_add(entlist, p); 253 cvs_ent_close(entlist, ENT_SYNC); 254 xfree(p); 255 } 256 } 257 258 if (added == 1 && current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 259 (void)xsnprintf(msg, sizeof(msg), 260 "Directory %s added to the repository", cf->file_rpath); 261 262 if (tag != NULL) { 263 (void)strlcat(msg, 264 "\n--> Using per-directory sticky tag ", 265 sizeof(msg)); 266 (void)strlcat(msg, tag, sizeof(msg)); 267 } 268 if (date != NULL) { 269 (void)strlcat(msg, 270 "\n--> Using per-directory sticky date ", 271 sizeof(msg)); 272 (void)strlcat(msg, date, sizeof(msg)); 273 } 274 cvs_printf("%s\n", msg); 275 276 if (tag != NULL) 277 xfree(tag); 278 if (date != NULL) 279 xfree(date); 280 } 281 282 cf->file_status = FILE_SKIP; 283 } 284 285 static void 286 add_file(struct cvs_file *cf) 287 { 288 int added, nb, stop; 289 char revbuf[CVS_REV_BUFSZ]; 290 RCSNUM *head; 291 char *tag; 292 293 cvs_parse_tagfile(cf->file_wd, &tag, NULL, &nb); 294 if (nb) { 295 cvs_log(LP_ERR, "cannot add file on non-branch tag %s", tag); 296 return; 297 } 298 299 if (cf->file_rcs != NULL) { 300 head = rcs_head_get(cf->file_rcs); 301 if (head == NULL) 302 fatal("RCS head empty or missing in %s\n", 303 cf->file_rcs->rf_path); 304 rcsnum_tostr(head, revbuf, sizeof(revbuf)); 305 rcsnum_free(head); 306 } 307 308 added = stop = 0; 309 switch (cf->file_status) { 310 case FILE_ADDED: 311 if (verbosity > 1) 312 cvs_log(LP_NOTICE, "%s has already been entered", 313 cf->file_path); 314 stop = 1; 315 break; 316 case FILE_REMOVED: 317 if (cf->file_rcs == NULL) { 318 cvs_log(LP_NOTICE, "cannot resurrect %s; " 319 "RCS file removed by second party", cf->file_name); 320 } else if (cf->fd == -1) { 321 add_entry(cf); 322 323 /* Restore the file. */ 324 head = rcs_head_get(cf->file_rcs); 325 if (head == NULL) 326 fatal("RCS head empty or missing in %s\n", 327 cf->file_rcs->rf_path); 328 cvs_checkout_file(cf, head, NULL, 0); 329 rcsnum_free(head); 330 331 cvs_printf("U %s\n", cf->file_path); 332 333 cvs_log(LP_NOTICE, "%s, version %s, resurrected", 334 cf->file_name, revbuf); 335 336 cf->file_status = FILE_UPTODATE; 337 } 338 stop = 1; 339 break; 340 case FILE_CONFLICT: 341 case FILE_LOST: 342 case FILE_MODIFIED: 343 case FILE_UPTODATE: 344 if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) { 345 cvs_log(LP_NOTICE, "%s already exists, with version " 346 "number %s", cf->file_path, revbuf); 347 stop = 1; 348 } 349 break; 350 case FILE_UNKNOWN: 351 if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) { 352 cvs_log(LP_NOTICE, "re-adding file %s " 353 "(instead of dead revision %s)", 354 cf->file_path, revbuf); 355 added++; 356 } else if (cf->fd != -1) { 357 cvs_log(LP_NOTICE, "scheduling file '%s' for addition", 358 cf->file_path); 359 added++; 360 } else { 361 stop = 1; 362 } 363 break; 364 default: 365 break; 366 } 367 368 if (stop == 1) 369 return; 370 371 add_entry(cf); 372 373 if (added != 0) { 374 cvs_log(LP_NOTICE, "use '%s commit' to add %s " 375 "permanently", __progname, 376 (added == 1) ? "this file" : "these files"); 377 } 378 } 379 380 static void 381 add_entry(struct cvs_file *cf) 382 { 383 FILE *fp; 384 char *entry, path[MAXPATHLEN]; 385 char revbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ]; 386 char sticky[CVS_ENT_MAXLINELEN]; 387 CVSENTRIES *entlist; 388 389 if (cvs_noexec == 1) 390 return; 391 392 sticky[0] = '\0'; 393 entry = xmalloc(CVS_ENT_MAXLINELEN); 394 395 if (cf->file_status == FILE_REMOVED) { 396 rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf)); 397 398 ctime_r(&cf->file_ent->ce_mtime, tbuf); 399 tbuf[strcspn(tbuf, "\n")] = '\0'; 400 401 if (cf->file_ent->ce_tag != NULL) 402 (void)xsnprintf(sticky, sizeof(sticky), "T%s", 403 cf->file_ent->ce_tag); 404 405 /* Remove the '-' prefixing the version number. */ 406 cvs_ent_line_str(cf->file_name, revbuf, tbuf, 407 cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "", sticky, 408 0, 0, entry, CVS_ENT_MAXLINELEN); 409 } else { 410 if (logmsg != NULL) { 411 (void)xsnprintf(path, MAXPATHLEN, "%s/%s%s", 412 CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT); 413 414 if ((fp = fopen(path, "w+")) == NULL) 415 fatal("add_entry: fopen `%s': %s", 416 path, strerror(errno)); 417 418 if (fputs(logmsg, fp) == EOF) { 419 (void)unlink(path); 420 fatal("add_entry: fputs `%s': %s", 421 path, strerror(errno)); 422 } 423 (void)fclose(fp); 424 } 425 426 if (cvs_directory_tag != NULL) 427 (void)xsnprintf(sticky, sizeof(sticky), "T%s", 428 cvs_directory_tag); 429 430 tbuf[0] = '\0'; 431 if (!cvs_server_active) 432 (void)xsnprintf(tbuf, sizeof(tbuf), "Initial %s", 433 cf->file_name); 434 435 cvs_ent_line_str(cf->file_name, "0", tbuf, kflag ? kbuf : "", 436 sticky, 0, 0, entry, CVS_ENT_MAXLINELEN); 437 } 438 439 if (cvs_server_active) { 440 cvs_server_send_response("Checked-in %s/", cf->file_wd); 441 cvs_server_send_response(cf->file_path); 442 cvs_server_send_response(entry); 443 } else { 444 entlist = cvs_ent_open(cf->file_wd); 445 cvs_ent_add(entlist, entry); 446 cvs_ent_close(entlist, ENT_SYNC); 447 } 448 xfree(entry); 449 } 450