1 /* $OpenBSD: update.c,v 1.90 2007/01/28 02:04:45 joris Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "includes.h" 19 20 #include "cvs.h" 21 #include "log.h" 22 #include "diff.h" 23 #include "remote.h" 24 25 int prune_dirs = 0; 26 int print = 0; 27 int build_dirs = 0; 28 int reset_stickies = 0; 29 static char *tag = NULL; 30 31 static void update_clear_conflict(struct cvs_file *); 32 33 struct cvs_cmd cvs_cmd_update = { 34 CVS_OP_UPDATE, 0, "update", 35 { "up", "upd" }, 36 "Bring work tree in sync with repository", 37 "[-ACdflPpR] [-D date | -r rev] [-I ign] [-j rev] [-k mode] " 38 "[-t id] ...", 39 "ACD:dfI:j:k:lPpQqRr:t:", 40 NULL, 41 cvs_update 42 }; 43 44 int 45 cvs_update(int argc, char **argv) 46 { 47 int ch; 48 char *arg = "."; 49 int flags; 50 struct cvs_recursion cr; 51 52 flags = CR_RECURSE_DIRS; 53 54 while ((ch = getopt(argc, argv, cvs_cmd_update.cmd_opts)) != -1) { 55 switch (ch) { 56 case 'A': 57 reset_stickies = 1; 58 break; 59 case 'C': 60 case 'D': 61 tag = optarg; 62 break; 63 case 'd': 64 build_dirs = 1; 65 break; 66 case 'f': 67 break; 68 case 'I': 69 break; 70 case 'j': 71 break; 72 case 'k': 73 break; 74 case 'l': 75 flags &= ~CR_RECURSE_DIRS; 76 break; 77 case 'P': 78 prune_dirs = 1; 79 break; 80 case 'p': 81 print = 1; 82 cvs_noexec = 1; 83 break; 84 case 'Q': 85 case 'q': 86 break; 87 case 'R': 88 break; 89 case 'r': 90 tag = optarg; 91 break; 92 default: 93 fatal("%s", cvs_cmd_update.cmd_synopsis); 94 } 95 } 96 97 argc -= optind; 98 argv += optind; 99 100 if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 101 cr.enterdir = cvs_update_enterdir; 102 cr.leavedir = cvs_update_leavedir; 103 cr.fileproc = cvs_update_local; 104 flags |= CR_REPO; 105 } else { 106 cvs_client_connect_to_server(); 107 if (reset_stickies) 108 cvs_client_send_request("Argument -A"); 109 if (build_dirs) 110 cvs_client_send_request("Argument -d"); 111 if (!(flags & CR_RECURSE_DIRS)) 112 cvs_client_send_request("Argument -l"); 113 if (prune_dirs) 114 cvs_client_send_request("Argument -P"); 115 if (print) 116 cvs_client_send_request("Argument -p"); 117 118 cr.enterdir = NULL; 119 cr.leavedir = NULL; 120 cr.fileproc = cvs_client_sendfile; 121 } 122 123 cr.flags = flags; 124 125 if (argc > 0) 126 cvs_file_run(argc, argv, &cr); 127 else 128 cvs_file_run(1, &arg, &cr); 129 130 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 131 cvs_client_send_files(argv, argc); 132 cvs_client_senddir("."); 133 cvs_client_send_request("update"); 134 cvs_client_get_responses(); 135 } 136 137 return (0); 138 } 139 140 void 141 cvs_update_enterdir(struct cvs_file *cf) 142 { 143 int l; 144 char *entry; 145 CVSENTRIES *entlist; 146 147 cvs_log(LP_TRACE, "cvs_update_enterdir(%s)", cf->file_path); 148 149 cvs_file_classify(cf, NULL, 0); 150 151 if (cf->file_status == DIR_CREATE && build_dirs == 1) { 152 cvs_mkpath(cf->file_path); 153 if ((cf->fd = open(cf->file_path, O_RDONLY)) == -1) 154 fatal("cvs_update_enterdir: `%s': %s", 155 cf->file_path, strerror(errno)); 156 157 entry = xmalloc(CVS_ENT_MAXLINELEN); 158 l = snprintf(entry, CVS_ENT_MAXLINELEN, "D/%s////", 159 cf->file_name); 160 if (l == -1 || l >= CVS_ENT_MAXLINELEN) 161 fatal("cvs_update_enterdir: overflow"); 162 163 entlist = cvs_ent_open(cf->file_wd); 164 cvs_ent_add(entlist, entry); 165 cvs_ent_close(entlist, ENT_SYNC); 166 xfree(entry); 167 } else if (cf->file_status == DIR_CREATE && build_dirs == 0) { 168 cf->file_status = FILE_SKIP; 169 } 170 } 171 172 void 173 cvs_update_leavedir(struct cvs_file *cf) 174 { 175 long base; 176 int nbytes; 177 int isempty; 178 size_t bufsize; 179 struct stat st; 180 struct dirent *dp; 181 char *buf, *ebuf, *cp; 182 struct cvs_ent *ent; 183 struct cvs_ent_line *line; 184 CVSENTRIES *entlist; 185 char export[MAXPATHLEN]; 186 187 cvs_log(LP_TRACE, "cvs_update_leavedir(%s)", cf->file_path); 188 189 if (cvs_cmdop == CVS_OP_EXPORT) { 190 if (cvs_path_cat(cf->file_path, CVS_PATH_CVSDIR, export, 191 MAXPATHLEN) >= MAXPATHLEN) 192 fatal("cvs_update_leavedir: truncation"); 193 194 /* XXX */ 195 if (cvs_rmdir(export) == -1) 196 fatal("cvs_update_leavedir: %s: %s:", export, 197 strerror(errno)); 198 199 return; 200 } 201 202 if (fstat(cf->fd, &st) == -1) 203 fatal("cvs_update_leavedir: %s", strerror(errno)); 204 205 bufsize = st.st_size; 206 if (bufsize < st.st_blksize) 207 bufsize = st.st_blksize; 208 209 isempty = 1; 210 buf = xmalloc(bufsize); 211 212 if (lseek(cf->fd, 0, SEEK_SET) == -1) 213 fatal("cvs_update_leavedir: %s", strerror(errno)); 214 215 while ((nbytes = getdirentries(cf->fd, buf, bufsize, &base)) > 0) { 216 ebuf = buf + nbytes; 217 cp = buf; 218 219 while (cp < ebuf) { 220 dp = (struct dirent *)cp; 221 if (!strcmp(dp->d_name, ".") || 222 !strcmp(dp->d_name, "..") || 223 dp->d_reclen == 0) { 224 cp += dp->d_reclen; 225 continue; 226 } 227 228 if (!strcmp(dp->d_name, CVS_PATH_CVSDIR)) { 229 entlist = cvs_ent_open(cf->file_path); 230 TAILQ_FOREACH(line, &(entlist->cef_ent), 231 entries_list) { 232 ent = cvs_ent_parse(line->buf); 233 234 if (ent->ce_status == CVS_ENT_REMOVED) { 235 isempty = 0; 236 cvs_ent_free(ent); 237 break; 238 } 239 240 cvs_ent_free(ent); 241 } 242 cvs_ent_close(entlist, ENT_NOSYNC); 243 } else { 244 isempty = 0; 245 } 246 247 if (isempty == 0) 248 break; 249 250 cp += dp->d_reclen; 251 } 252 } 253 254 if (nbytes == -1) 255 fatal("cvs_update_leavedir: %s", strerror(errno)); 256 257 xfree(buf); 258 259 if (isempty == 1 && prune_dirs == 1) { 260 /* XXX */ 261 cvs_rmdir(cf->file_path); 262 263 if (cvs_server_active == 0) { 264 entlist = cvs_ent_open(cf->file_wd); 265 cvs_ent_remove(entlist, cf->file_name); 266 cvs_ent_close(entlist, ENT_SYNC); 267 } 268 } 269 } 270 271 void 272 cvs_update_local(struct cvs_file *cf) 273 { 274 int ret, flags; 275 CVSENTRIES *entlist; 276 char rbuf[16]; 277 278 cvs_log(LP_TRACE, "cvs_update_local(%s)", cf->file_path); 279 280 if (cf->file_type == CVS_DIR) { 281 if (cf->file_status == FILE_SKIP) 282 return; 283 284 if (cf->file_status != FILE_UNKNOWN && 285 verbosity > 1) 286 cvs_log(LP_NOTICE, "Updating %s", cf->file_path); 287 return; 288 } 289 290 flags = 0; 291 cvs_file_classify(cf, tag, 1); 292 293 if (cf->file_status == FILE_UPTODATE && cf->file_ent != NULL && 294 cf->file_ent->ce_tag != NULL && reset_stickies == 1) { 295 cf->file_status = FILE_CHECKOUT; 296 cf->file_rcsrev = rcs_head_get(cf->file_rcs); 297 } 298 299 if (print && cf->file_status != FILE_UNKNOWN) { 300 rcsnum_tostr(cf->file_rcsrev, rbuf, sizeof(rbuf)); 301 if (verbosity > 1) 302 cvs_printf("%s\nChecking out %s\n" 303 "RCS:\t%s\nVERS:\t%s\n***************\n", 304 RCS_DIFF_DIV, cf->file_path, cf->file_rpath, rbuf); 305 cvs_checkout_file(cf, cf->file_rcsrev, CO_DUMP); 306 return; 307 } 308 309 switch (cf->file_status) { 310 case FILE_UNKNOWN: 311 cvs_printf("? %s\n", cf->file_path); 312 break; 313 case FILE_MODIFIED: 314 ret = update_has_conflict_markers(cf); 315 if (cf->file_ent->ce_conflict != NULL && ret == 1) { 316 cvs_printf("C %s\n", cf->file_path); 317 } else { 318 if (cf->file_ent->ce_conflict != NULL && ret == 0) 319 update_clear_conflict(cf); 320 cvs_printf("M %s\n", cf->file_path); 321 } 322 break; 323 case FILE_ADDED: 324 cvs_printf("A %s\n", cf->file_path); 325 break; 326 case FILE_REMOVED: 327 cvs_printf("R %s\n", cf->file_path); 328 break; 329 case FILE_CONFLICT: 330 cvs_printf("C %s\n", cf->file_path); 331 break; 332 case FILE_LOST: 333 case FILE_CHECKOUT: 334 case FILE_PATCH: 335 if (tag != NULL) 336 flags = CO_SETSTICKY; 337 338 cvs_checkout_file(cf, cf->file_rcsrev, flags); 339 cvs_printf("U %s\n", cf->file_path); 340 break; 341 case FILE_MERGE: 342 cvs_checkout_file(cf, cf->file_rcsrev, CO_MERGE); 343 344 if (diff3_conflicts != 0) { 345 cvs_printf("C %s\n", cf->file_path); 346 } else { 347 update_clear_conflict(cf); 348 cvs_printf("M %s\n", cf->file_path); 349 } 350 break; 351 case FILE_UNLINK: 352 (void)unlink(cf->file_path); 353 case FILE_REMOVE_ENTRY: 354 entlist = cvs_ent_open(cf->file_wd); 355 cvs_ent_remove(entlist, cf->file_name); 356 cvs_ent_close(entlist, ENT_SYNC); 357 break; 358 default: 359 break; 360 } 361 } 362 363 static void 364 update_clear_conflict(struct cvs_file *cf) 365 { 366 int l; 367 time_t now; 368 CVSENTRIES *entlist; 369 char *entry, revbuf[16], timebuf[32]; 370 371 cvs_log(LP_TRACE, "update_clear_conflict(%s)", cf->file_path); 372 373 time(&now); 374 ctime_r(&now, timebuf); 375 if (timebuf[strlen(timebuf) - 1] == '\n') 376 timebuf[strlen(timebuf) - 1] = '\0'; 377 378 rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf)); 379 380 entry = xmalloc(CVS_ENT_MAXLINELEN); 381 l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s//", 382 cf->file_name, revbuf, timebuf); 383 if (l == -1 || l >= CVS_ENT_MAXLINELEN) 384 fatal("update_clear_conflict: overflow"); 385 386 entlist = cvs_ent_open(cf->file_wd); 387 cvs_ent_add(entlist, entry); 388 cvs_ent_close(entlist, ENT_SYNC); 389 xfree(entry); 390 } 391 392 /* 393 * XXX - this is the way GNU cvs checks for outstanding conflicts 394 * in a file after a merge. It is a very very bad approach and 395 * should be looked at once opencvs is working decently. 396 */ 397 int 398 update_has_conflict_markers(struct cvs_file *cf) 399 { 400 BUF *bp; 401 int conflict; 402 char *content; 403 struct cvs_line *lp; 404 struct cvs_lines *lines; 405 size_t len; 406 407 cvs_log(LP_TRACE, "update_has_conflict_markers(%s)", cf->file_path); 408 409 if (cf->fd == -1) 410 return (0); 411 412 if ((bp = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL) 413 fatal("update_has_conflict_markers: failed to load %s", 414 cf->file_path); 415 416 cvs_buf_putc(bp, '\0'); 417 len = cvs_buf_len(bp); 418 content = cvs_buf_release(bp); 419 if ((lines = cvs_splitlines(content, len)) == NULL) 420 fatal("update_has_conflict_markers: failed to split lines"); 421 422 conflict = 0; 423 TAILQ_FOREACH(lp, &(lines->l_lines), l_list) { 424 if (lp->l_line == NULL) 425 continue; 426 427 if (!strncmp(lp->l_line, RCS_CONFLICT_MARKER1, 428 strlen(RCS_CONFLICT_MARKER1)) || 429 !strncmp(lp->l_line, RCS_CONFLICT_MARKER2, 430 strlen(RCS_CONFLICT_MARKER2)) || 431 !strncmp(lp->l_line, RCS_CONFLICT_MARKER3, 432 strlen(RCS_CONFLICT_MARKER3))) { 433 conflict = 1; 434 break; 435 } 436 } 437 438 cvs_freelines(lines); 439 xfree(content); 440 return (conflict); 441 } 442