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