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