1 /* $OpenBSD: checkout.c,v 1.87 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 static void checkout_check_repository(int, char **); 26 static void checkout_repository(const char *, const char *); 27 28 extern int prune_dirs; 29 extern int build_dirs; 30 extern int reset_stickies; 31 32 static int flags = CR_REPO | CR_RECURSE_DIRS; 33 34 struct cvs_cmd cvs_cmd_checkout = { 35 CVS_OP_CHECKOUT, 0, "checkout", 36 { "co", "get" }, 37 "Checkout a working copy of a repository", 38 "[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] " 39 "[-t id] module ...", 40 "AcD:d:fj:k:lNnPpRr:st:", 41 NULL, 42 cvs_checkout 43 }; 44 45 struct cvs_cmd cvs_cmd_export = { 46 CVS_OP_EXPORT, 0, "export", 47 { "exp", "ex" }, 48 "Export sources from CVS, similar to checkout", 49 "[-flNnR] [-d dir] [-k mode] -D date | -r rev module ...", 50 "D:d:k:flNnRr:", 51 NULL, 52 cvs_export 53 }; 54 55 int 56 cvs_checkout(int argc, char **argv) 57 { 58 int ch; 59 60 while ((ch = getopt(argc, argv, cvs_cmd_checkout.cmd_opts)) != -1) { 61 switch (ch) { 62 case 'A': 63 reset_stickies = 1; 64 break; 65 case 'l': 66 flags &= ~CR_RECURSE_DIRS; 67 break; 68 case 'P': 69 prune_dirs = 1; 70 break; 71 case 'R': 72 break; 73 default: 74 fatal("%s", cvs_cmd_checkout.cmd_synopsis); 75 } 76 } 77 78 argc -= optind; 79 argv += optind; 80 81 if (argc == 0) 82 fatal("%s", cvs_cmd_checkout.cmd_synopsis); 83 84 checkout_check_repository(argc, argv); 85 86 return (0); 87 } 88 89 int 90 cvs_export(int argc, char **argv) 91 { 92 int ch; 93 94 prune_dirs = 1; 95 96 while ((ch = getopt(argc, argv, cvs_cmd_export.cmd_opts)) != -1) { 97 switch (ch) { 98 case 'l': 99 flags &= ~CR_RECURSE_DIRS; 100 break; 101 case 'R': 102 break; 103 default: 104 fatal("%s", cvs_cmd_export.cmd_synopsis); 105 } 106 } 107 108 argc -= optind; 109 argv += optind; 110 111 if (argc == 0) 112 fatal("%s", cvs_cmd_export.cmd_synopsis); 113 114 checkout_check_repository(argc, argv); 115 116 return (0); 117 } 118 119 static void 120 checkout_check_repository(int argc, char **argv) 121 { 122 int i; 123 char repo[MAXPATHLEN]; 124 struct stat st; 125 struct cvs_recursion cr; 126 127 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 128 cvs_client_connect_to_server(); 129 130 if (reset_stickies == 1) 131 cvs_client_send_request("Argument -A"); 132 133 if (!(flags & CR_RECURSE_DIRS)) 134 cvs_client_send_request("Argument -l"); 135 136 if (cvs_cmdop == CVS_OP_CHECKOUT && prune_dirs == 1) 137 cvs_client_send_request("Argument -P"); 138 139 cr.enterdir = NULL; 140 cr.leavedir = NULL; 141 cr.fileproc = cvs_client_sendfile; 142 cr.flags = flags; 143 144 cvs_file_run(argc, argv, &cr); 145 146 cvs_client_send_files(argv, argc); 147 cvs_client_senddir("."); 148 149 cvs_client_send_request("%s", 150 (cvs_cmdop == CVS_OP_CHECKOUT) ? "co" : "export"); 151 152 cvs_client_get_responses(); 153 154 return; 155 } 156 157 for (i = 0; i < argc; i++) { 158 if (cvs_path_cat(current_cvsroot->cr_dir, argv[i], repo, 159 sizeof(repo)) >= sizeof(repo)) 160 fatal("checkout_check_repository: truncation"); 161 162 if (stat(repo, &st) == -1) { 163 cvs_log(LP_ERR, "cannot find module `%s' - ignored", 164 argv[i]); 165 continue; 166 } 167 cvs_mkpath(argv[i]); 168 checkout_repository(repo, argv[i]); 169 } 170 } 171 172 static void 173 checkout_repository(const char *repobase, const char *wdbase) 174 { 175 struct cvs_flisthead fl, dl; 176 struct cvs_recursion cr; 177 178 TAILQ_INIT(&fl); 179 TAILQ_INIT(&dl); 180 181 build_dirs = 1; 182 cr.enterdir = cvs_update_enterdir; 183 cr.leavedir = cvs_update_leavedir; 184 cr.fileproc = cvs_update_local; 185 cr.flags = flags; 186 187 cvs_repository_lock(repobase); 188 cvs_repository_getdir(repobase, wdbase, &fl, &dl, 1); 189 190 cvs_file_walklist(&fl, &cr); 191 cvs_file_freelist(&fl); 192 193 cvs_repository_unlock(repobase); 194 195 cvs_file_walklist(&dl, &cr); 196 cvs_file_freelist(&dl); 197 } 198 199 void 200 cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, int co_flags) 201 { 202 int kflag, l, oflags, exists; 203 time_t rcstime; 204 CVSENTRIES *ent; 205 struct timeval tv[2]; 206 char *tosend; 207 char template[MAXPATHLEN], *p, entry[CVS_ENT_MAXLINELEN], rev[16]; 208 char timebuf[64], kbuf[8], tbuf[32], stickytag[32]; 209 210 exists = 0; 211 tosend = NULL; 212 rcsnum_tostr(rnum, rev, sizeof(rev)); 213 214 cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d) -> %s", 215 cf->file_path, rev, co_flags, 216 (cvs_server_active) ? "to client" : "to disk"); 217 218 if (co_flags & CO_DUMP) { 219 if (cvs_server_active) { 220 cvs_printf("dump file %s to client\n", cf->file_path); 221 } else { 222 rcs_rev_write_fd(cf->file_rcs, rnum, 223 STDOUT_FILENO, 1); 224 } 225 226 return; 227 } 228 229 if (co_flags & CO_MERGE) 230 printf("merge on %s\n", cf->file_path); 231 232 if (cvs_server_active == 0) { 233 if (!(co_flags & CO_MERGE)) { 234 oflags = O_WRONLY | O_TRUNC; 235 if (cf->fd != -1) { 236 exists = 1; 237 (void)close(cf->fd); 238 } else { 239 oflags |= O_CREAT; 240 } 241 242 cf->fd = open(cf->file_path, oflags); 243 if (cf->fd == -1) 244 fatal("cvs_checkout_file: open: %s", 245 strerror(errno)); 246 247 rcs_rev_write_fd(cf->file_rcs, rnum, cf->fd, 1); 248 } else { 249 cvs_merge_file(cf, 1); 250 } 251 252 if (fchmod(cf->fd, 0644) == -1) 253 fatal("cvs_checkout_file: fchmod: %s", strerror(errno)); 254 255 if ((exists == 0) && (cf->file_ent == NULL) && 256 !(co_flags & CO_MERGE)) 257 rcstime = rcs_rev_getdate(cf->file_rcs, rnum); 258 else 259 time(&rcstime); 260 261 tv[0].tv_sec = rcstime; 262 tv[0].tv_usec = 0; 263 tv[1] = tv[0]; 264 if (futimes(cf->fd, tv) == -1) 265 fatal("cvs_checkout_file: futimes: %s", 266 strerror(errno)); 267 } else { 268 time(&rcstime); 269 } 270 271 asctime_r(gmtime(&rcstime), tbuf); 272 if (tbuf[strlen(tbuf) - 1] == '\n') 273 tbuf[strlen(tbuf) - 1] = '\0'; 274 275 if (co_flags & CO_MERGE) { 276 l = snprintf(timebuf, sizeof(timebuf), "Result of merge+%s", 277 tbuf); 278 if (l == -1 || l >= (int)sizeof(timebuf)) 279 fatal("cvs_checkout_file: overflow"); 280 } else { 281 strlcpy(timebuf, tbuf, sizeof(timebuf)); 282 } 283 284 if (co_flags & CO_SETSTICKY) { 285 l = snprintf(stickytag, sizeof(stickytag), "T%s", rev); 286 if (l == -1 || l >= (int)sizeof(stickytag)) 287 fatal("cvs_checkout_file: overflow"); 288 } else { 289 stickytag[0] = '\0'; 290 } 291 292 kbuf[0] = '\0'; 293 if (cf->file_ent != NULL) { 294 if (cf->file_ent->ce_opts != NULL) 295 strlcpy(kbuf, cf->file_ent->ce_opts, sizeof(kbuf)); 296 } else if (cf->file_rcs->rf_expand != NULL) { 297 kflag = rcs_kflag_get(cf->file_rcs->rf_expand); 298 if (!(kflag & RCS_KWEXP_DEFAULT)) 299 snprintf(kbuf, sizeof(kbuf), 300 "-k%s", cf->file_rcs->rf_expand); 301 } 302 303 l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s/%s/%s", 304 cf->file_name, rev, timebuf, kbuf, stickytag); 305 306 if (cvs_server_active == 0) { 307 ent = cvs_ent_open(cf->file_wd); 308 cvs_ent_add(ent, entry); 309 cvs_ent_close(ent, ENT_SYNC); 310 } else { 311 if ((p = strrchr(cf->file_rpath, ',')) != NULL) 312 *p = '\0'; 313 314 if (co_flags & CO_MERGE) { 315 cvs_merge_file(cf, 1); 316 tosend = cf->file_path; 317 } 318 319 if (co_flags & CO_COMMIT) 320 cvs_server_update_entry("Checked-in", cf); 321 else if (co_flags & CO_MERGE) 322 cvs_server_update_entry("Merged", cf); 323 else 324 cvs_server_update_entry("Updated", cf); 325 326 cvs_remote_output(entry); 327 328 if (!(co_flags & CO_COMMIT)) { 329 if (!(co_flags & CO_MERGE)) { 330 l = snprintf(template, MAXPATHLEN, 331 "%s/checkout.XXXXXXXXXX", cvs_tmpdir); 332 if (l == -1 || l >= MAXPATHLEN) 333 fatal("cvs_Checkout_file: overflow"); 334 rcs_rev_write_stmp(cf->file_rcs, rnum, 335 template, 0); 336 tosend = template; 337 } 338 339 cvs_remote_send_file(tosend); 340 341 if (!(co_flags & CO_MERGE)) { 342 (void)unlink(template); 343 cvs_worklist_run(&temp_files, 344 cvs_worklist_unlink); 345 xfree(template); 346 } 347 } 348 349 if (p != NULL) 350 *p = ','; 351 } 352 } 353