checkout.c revision 81405
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1992, Brian Berliner and Jeff Polk 31553Srgrimes * Copyright (c) 1989-1992, Brian Berliner 41553Srgrimes * 51553Srgrimes * You may distribute under the terms of the GNU General Public License as 61553Srgrimes * specified in the README file that comes with the CVS source distribution. 71553Srgrimes * 81553Srgrimes * Create Version 91553Srgrimes * 101553Srgrimes * "checkout" creates a "version" of an RCS repository. This version is owned 111553Srgrimes * totally by the user and is actually an independent copy, to be dealt with 121553Srgrimes * as seen fit. Once "checkout" has been called in a given directory, it 131553Srgrimes * never needs to be called again. The user can keep up-to-date by calling 141553Srgrimes * "update" when he feels like it; this will supply him with a merge of his 151553Srgrimes * own modifications and the changes made in the RCS original. See "update" 161553Srgrimes * for details. 171553Srgrimes * 181553Srgrimes * "checkout" can be given a list of directories or files to be updated and in 191553Srgrimes * the case of a directory, will recursivley create any sub-directories that 201553Srgrimes * exist in the repository. 211553Srgrimes * 221553Srgrimes * When the user is satisfied with his own modifications, the present version 231553Srgrimes * can be committed by "commit"; this keeps the present version in tact, 241553Srgrimes * usually. 251553Srgrimes * 261553Srgrimes * The call is cvs checkout [options] <module-name>... 271553Srgrimes * 281553Srgrimes * "checkout" creates a directory ./CVS, in which it keeps its administration, 291553Srgrimes * in two files, Repository and Entries. The first contains the name of the 301553Srgrimes * repository. The second contains one line for each registered file, 311553Srgrimes * consisting of the version number it derives from, its time stamp at 321553Srgrimes * derivation time and its name. Both files are normal files and can be 331553Srgrimes * edited by the user, if necessary (when the repository is moved, e.g.) 341553Srgrimes */ 351553Srgrimes 3631492Swollman#include <assert.h> 371553Srgrimes#include "cvs.h" 381553Srgrimes 391553Srgrimesstatic char *findslash PROTO((char *start, char *p)); 401553Srgrimesstatic int checkout_proc PROTO((int argc, char **argv, char *where, 41117554Sgad char *mwhere, char *mfile, int shorten, 42117587Sgad int local_specified, char *omodule, 4315648Sjoerg char *msg)); 44117587Sgad 45117554Sgadstatic const char *const checkout_usage[] = 461553Srgrimes{ 47117554Sgad "Usage:\n %s %s [-ANPRcflnps] [-r rev] [-D date] [-d dir]\n", 48117554Sgad " [-j rev1] [-j rev2] [-k kopt] modules...\n", 491553Srgrimes "\t-A\tReset any sticky tags/date/kopts.\n", 501553Srgrimes "\t-N\tDon't shorten module paths if -d specified.\n", 511553Srgrimes "\t-P\tPrune empty directories.\n", 521553Srgrimes "\t-R\tProcess directories recursively.\n", 531553Srgrimes "\t-c\t\"cat\" the module database.\n", 541553Srgrimes "\t-f\tForce a head revision match if tag/date not found.\n", 551553Srgrimes "\t-l\tLocal directory only, not recursive\n", 561553Srgrimes "\t-n\tDo not run module program (if any).\n", 571553Srgrimes "\t-p\tCheck out files to standard output (avoids stickiness).\n", 581553Srgrimes "\t-s\tLike -c, but include module status.\n", 591553Srgrimes "\t-r rev\tCheck out revision or tag. (implies -P) (is sticky)\n", 601553Srgrimes "\t-D date\tCheck out revisions as of date. (implies -P) (is sticky)\n", 611553Srgrimes "\t-d dir\tCheck out into dir instead of module name.\n", 621553Srgrimes "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n", 631553Srgrimes "\t-j rev\tMerge in changes made between current revision and rev.\n", 641553Srgrimes "(Specify the --help global option for a list of other help options)\n", 651553Srgrimes NULL 661553Srgrimes}; 671553Srgrimes 681553Srgrimesstatic const char *const export_usage[] = 691553Srgrimes{ 701553Srgrimes "Usage: %s %s [-NRfln] [-r rev] [-D date] [-d dir] [-k kopt] module...\n", 711553Srgrimes "\t-N\tDon't shorten module paths if -d specified.\n", 7215032Ssef "\t-f\tForce a head revision match if tag/date not found.\n", 7315032Ssef "\t-l\tLocal directory only, not recursive\n", 7415703Sjoerg "\t-R\tProcess directories recursively (default).\n", 751553Srgrimes "\t-n\tDo not run module program (if any).\n", 761553Srgrimes "\t-r rev\tExport revision or tag.\n", 771553Srgrimes "\t-D date\tExport revisions as of date.\n", 781553Srgrimes "\t-d dir\tExport into dir instead of module name.\n", 791553Srgrimes "\t-k kopt\tUse RCS kopt -k option on checkout.\n", 8080230Sgad "(Specify the --help global option for a list of other help options)\n", 8180230Sgad NULL 821553Srgrimes}; 831553Srgrimes 841553Srgrimesstatic int checkout_prune_dirs; 851553Srgrimesstatic int force_tag_match = 1; 861553Srgrimesstatic int pipeout; 871553Srgrimesstatic int aflag; 881553Srgrimesstatic char *options = NULL; 891553Srgrimesstatic char *tag = NULL; 901553Srgrimesstatic int tag_validated = 0; 911553Srgrimesstatic char *date = NULL; 921553Srgrimesstatic char *join_rev1 = NULL; 931553Srgrimesstatic char *join_rev2 = NULL; 941553Srgrimesstatic int join_tags_validated = 0; 951553Srgrimesstatic char *preload_update_dir = NULL; 961553Srgrimesstatic char *history_name = NULL; 9797793Sgadstatic enum mtype m_type; 981553Srgrimes 9968253Sgadint 1001553Srgrimescheckout (argc, argv) 1011553Srgrimes int argc; 10224831Sbrian char **argv; 1031553Srgrimes{ 1041553Srgrimes int i; 1051553Srgrimes int c; 10653956Sache DBM *db; 1071553Srgrimes int cat = 0, err = 0, status = 0; 10880230Sgad int run_module_prog = 1; 10980230Sgad int local = 0; 11080230Sgad int shorten = -1; 11180230Sgad char *where = NULL; 1121553Srgrimes char *valid_options; 11378300Sgad const char *const *valid_usage; 1141553Srgrimes 1158857Srgrimes /* 1161553Srgrimes * A smaller subset of options are allowed for the export command, which 1171553Srgrimes * is essentially like checkout, except that it hard-codes certain 1181553Srgrimes * options to be default (like -kv) and takes care to remove the CVS 1191553Srgrimes * directory when it has done its duty 1201553Srgrimes */ 12168664Sgad if (strcmp (command_name, "export") == 0) 12268664Sgad { 1231553Srgrimes m_type = EXPORT; 12424831Sbrian valid_options = "+Nnk:d:flRQqr:D:"; 12524831Sbrian valid_usage = export_usage; 1261553Srgrimes } 12778146Sgad else 12878146Sgad { 12978146Sgad m_type = CHECKOUT; 13078146Sgad valid_options = "+ANnk:d:flRpQqcsr:D:j:P"; 13178146Sgad valid_usage = checkout_usage; 13294032Sgad } 13394032Sgad 13478146Sgad if (argc == -1) 13578146Sgad usage (valid_usage); 13678146Sgad 13778146Sgad ign_setup (); 13878146Sgad wrap_setup (); 13978146Sgad 14078146Sgad optind = 0; 14179739Sgad while ((c = getopt (argc, argv, valid_options)) != -1) 14279739Sgad { 14378146Sgad switch (c) 14478146Sgad { 14578146Sgad case 'A': 14678146Sgad aflag = 1; 14778146Sgad break; 14895293Sgad case 'N': 14978146Sgad shorten = 0; 15094038Sgad break; 15178146Sgad case 'k': 1521553Srgrimes if (options) 1531553Srgrimes free (options); 15478146Sgad options = RCS_check_kflag (optarg); 1551553Srgrimes break; 1561553Srgrimes case 'n': 15768401Sgad run_module_prog = 0; 15868401Sgad break; 1591553Srgrimes case 'Q': 16068733Sgad case 'q': 16197793Sgad#ifdef SERVER_SUPPORT 16268733Sgad /* The CVS 1.5 client sends these options (in addition to 1631553Srgrimes Global_option requests), so we must ignore them. */ 16468733Sgad if (!server_active) 16531492Swollman#endif 16631492Swollman error (1, 0, 1671553Srgrimes "-q or -Q must be specified before \"%s\"", 16831492Swollman command_name); 16997792Sgad break; 17097792Sgad case 'l': 1711553Srgrimes local = 1; 1721553Srgrimes break; 1731553Srgrimes case 'R': 17497793Sgad local = 0; 17597793Sgad break; 17679735Sgad case 'P': 17779735Sgad checkout_prune_dirs = 1; 17879735Sgad break; 17979735Sgad case 'p': 18079735Sgad pipeout = 1; 18179735Sgad run_module_prog = 0; /* don't run module prog when piping */ 18279735Sgad noexec = 1; /* so no locks will be created */ 18379735Sgad break; 1841553Srgrimes case 'c': 1851553Srgrimes cat = 1; 1861553Srgrimes break; 1871553Srgrimes case 'd': 1881553Srgrimes where = optarg; 1891553Srgrimes if (shorten == -1) 1901553Srgrimes shorten = 1; 1911553Srgrimes break; 19231492Swollman case 's': 19397792Sgad cat = status = 1; 19497792Sgad break; 1951553Srgrimes case 'f': 1961553Srgrimes force_tag_match = 0; 19731492Swollman break; 1981553Srgrimes case 'r': 19931492Swollman tag = optarg; 20031492Swollman checkout_prune_dirs = 1; 2011553Srgrimes break; 20231492Swollman case 'D': 20331492Swollman date = Make_Date (optarg); 20497792Sgad checkout_prune_dirs = 1; 20597792Sgad break; 2061553Srgrimes case 'j': 2071553Srgrimes if (join_rev2) 20831492Swollman error (1, 0, "only two -j options can be specified"); 20931492Swollman if (join_rev1) 21097792Sgad join_rev2 = optarg; 21197792Sgad else 2121553Srgrimes join_rev1 = optarg; 2131553Srgrimes break; 2141553Srgrimes case '?': 2151553Srgrimes default: 2161553Srgrimes usage (valid_usage); 2171553Srgrimes break; 21897793Sgad } 2191553Srgrimes } 2201553Srgrimes argc -= optind; 22197792Sgad argv += optind; 22297792Sgad 2231553Srgrimes if (shorten == -1) 2241553Srgrimes shorten = 0; 2251553Srgrimes 2261553Srgrimes if (cat && argc != 0) 2271553Srgrimes error (1, 0, "-c and -s must not get any arguments"); 22831492Swollman 22931492Swollman if (!cat && argc == 0) 23097792Sgad error (1, 0, "must specify at least one module or directory"); 2311553Srgrimes 2321553Srgrimes if (where && pipeout) 2331553Srgrimes error (1, 0, "-d and -p are mutually exclusive"); 2341553Srgrimes 23531492Swollman if (m_type == EXPORT) 23631492Swollman { 23797792Sgad if (!tag && !date) 23897792Sgad error (1, 0, "must specify a tag or date"); 2391553Srgrimes 24068664Sgad if (tag && isdigit ((unsigned char) tag[0])) 24168664Sgad error (1, 0, "tag `%s' must be a symbolic tag", tag); 24268664Sgad } 24368664Sgad 24497792Sgad#ifdef SERVER_SUPPORT 24568732Sgad if (server_active && where != NULL) 24668664Sgad { 24768664Sgad server_pathname_check (where); 24868664Sgad } 24997792Sgad#endif 25068732Sgad 25168664Sgad if (!cat && !safe_location()) { 25268664Sgad error(1, 0, "Cannot check out files into the repository itself"); 25368664Sgad } 25468664Sgad 25531492Swollman#ifdef CLIENT_SUPPORT 2561553Srgrimes if (current_parsed_root->isremote) 2571553Srgrimes { 2581553Srgrimes int expand_modules; 2591553Srgrimes 2601553Srgrimes start_server (); 2611553Srgrimes 2621553Srgrimes ign_setup (); 2631553Srgrimes 26468401Sgad /* We have to expand names here because the "expand-modules" 2651553Srgrimes directive to the server has the side-effect of having the 26615648Sjoerg server send the check-in and update programs for the 2671553Srgrimes various modules/dirs requested. If we turn this off and 26815648Sjoerg simply request the names of the modules and directories (as 26968401Sgad below in !expand_modules), those files (CVS/Checkin.prog 2701553Srgrimes or CVS/Update.prog) don't get created. Grrr. */ 2711553Srgrimes 27297792Sgad expand_modules = (!cat && !pipeout 27397792Sgad && supported_request ("expand-modules")); 27431492Swollman 27568401Sgad if (expand_modules) 2761553Srgrimes { 27768401Sgad /* This is done here because we need to read responses 2781553Srgrimes from the server before we send the command checkout or 2791553Srgrimes export files. */ 2801553Srgrimes 2811553Srgrimes client_expand_modules (argc, argv, local); 2821553Srgrimes } 2831553Srgrimes 28431492Swollman if (!run_module_prog) 2851553Srgrimes send_arg ("-n"); 2861553Srgrimes if (local) 28731492Swollman send_arg ("-l"); 28831492Swollman if (pipeout) 2891553Srgrimes send_arg ("-p"); 29031492Swollman if (!force_tag_match) 29131492Swollman send_arg ("-f"); 29297792Sgad if (aflag) 29397792Sgad send_arg("-A"); 29497792Sgad if (!shorten) 2951553Srgrimes send_arg("-N"); 2961553Srgrimes if (checkout_prune_dirs && m_type == CHECKOUT) 2971553Srgrimes send_arg("-P"); 29868733Sgad client_prune_dirs = checkout_prune_dirs; 29968733Sgad if (cat && !status) 30015648Sjoerg send_arg("-c"); 30115648Sjoerg if (where != NULL) 30231492Swollman option_with_arg ("-d", where); 30397793Sgad if (status) 30497793Sgad send_arg("-s"); 3051553Srgrimes if (options != NULL && options[0] != '\0') 30697793Sgad send_arg (options); 3071553Srgrimes option_with_arg ("-r", tag); 30879735Sgad if (date) 30979735Sgad client_senddate (date); 31097793Sgad if (join_rev1 != NULL) 31197793Sgad option_with_arg ("-j", join_rev1); 3121553Srgrimes if (join_rev2 != NULL) 3131553Srgrimes option_with_arg ("-j", join_rev2); 3141553Srgrimes 31597792Sgad if (expand_modules) 31697792Sgad { 31731492Swollman client_send_expansions (local, where, 1); 3181553Srgrimes } 31915648Sjoerg else 32031492Swollman { 32197792Sgad int i; 32297792Sgad for (i = 0; i < argc; ++i) 32397792Sgad send_arg (argv[i]); 32415648Sjoerg client_nonexpanded_setup (); 32527748Simp } 32668401Sgad 32768401Sgad send_to_server (m_type == EXPORT ? "export\012" : "co\012", 0); 32868401Sgad return get_responses_and_close (); 32915648Sjoerg } 33031492Swollman#endif /* CLIENT_SUPPORT */ 33115648Sjoerg 3321553Srgrimes if (cat) 3331553Srgrimes { 33431492Swollman cat_module (status); 3351553Srgrimes if (options) 3361553Srgrimes free (options); 3371553Srgrimes return (0); 33831492Swollman } 33931492Swollman db = open_module (); 34097792Sgad 3411553Srgrimes 3421553Srgrimes /* If we've specified something like "cvs co foo/bar baz/quux" 3431553Srgrimes don't try to shorten names. There are a few cases in which we 3441553Srgrimes could shorten (e.g. "cvs co foo/bar foo/baz"), but we don't 34568733Sgad handle those yet. Better to have an extra directory created 34631492Swollman than the thing checked out under the wrong directory name. */ 34731492Swollman 34831492Swollman if (argc > 1) 34931492Swollman shorten = 0; 35031492Swollman 35131492Swollman 3521553Srgrimes /* If we will be calling history_write, work out the name to pass 35319202Simp it. */ 35419202Simp if (m_type == CHECKOUT && !pipeout) 35568664Sgad { 3561553Srgrimes if (tag && date) 3571553Srgrimes { 3581553Srgrimes history_name = xmalloc (strlen (tag) + strlen (date) + 2); 3591553Srgrimes sprintf (history_name, "%s:%s", tag, date); 3601553Srgrimes } 3611553Srgrimes else if (tag) 3621553Srgrimes history_name = tag; 3631553Srgrimes else 3641553Srgrimes history_name = date; 3651553Srgrimes } 3661553Srgrimes 3671553Srgrimes 3681553Srgrimes for (i = 0; i < argc; i++) 3691553Srgrimes err += do_module (db, argv[i], m_type, "Updating", checkout_proc, 3701553Srgrimes where, shorten, local, run_module_prog, !pipeout, 3711553Srgrimes (char *) NULL); 3721553Srgrimes close_module (db); 3731553Srgrimes if (options) 3741553Srgrimes free (options); 37578146Sgad return (err); 3761553Srgrimes} 3771553Srgrimes 37868734Sgad/* FIXME: This is and emptydir_name are in checkout.c for historical 37968734Sgad reasons, probably want to move them. */ 3801553Srgrimes 38168734Sgadint 38268734Sgadsafe_location () 3831553Srgrimes{ 3841553Srgrimes char *current; 3851553Srgrimes char hardpath[PATH_MAX+5]; 3861553Srgrimes size_t hardpath_len; 38797792Sgad int x; 38897791Sgad int retval; 3891553Srgrimes 3901553Srgrimes#ifdef HAVE_READLINK 3911553Srgrimes /* FIXME-arbitrary limit: should be retrying this like xgetwd. 3921553Srgrimes But how does readlink let us know that the buffer was too small? 3931553Srgrimes (by returning sizeof hardpath - 1?). */ 3941553Srgrimes x = readlink(current_parsed_root->directory, hardpath, sizeof hardpath - 1); 39531492Swollman#else 3961553Srgrimes x = -1; 3971553Srgrimes#endif 39868253Sgad if (x == -1) 39968253Sgad { 40068253Sgad strcpy(hardpath, current_parsed_root->directory); 4011553Srgrimes } 4021553Srgrimes else 4031553Srgrimes { 4041553Srgrimes hardpath[x] = '\0'; 4051553Srgrimes } 4061553Srgrimes current = xgetwd (); 4071553Srgrimes if (current == NULL) 4081553Srgrimes error (1, errno, "could not get working directory"); 4091553Srgrimes hardpath_len = strlen (hardpath); 4101553Srgrimes if (strlen (current) >= hardpath_len 4111553Srgrimes && strncmp (current, hardpath, hardpath_len) == 0) 4121553Srgrimes { 4131553Srgrimes if (/* Current is a subdirectory of hardpath. */ 4141553Srgrimes current[hardpath_len] == '/' 4151553Srgrimes 41615648Sjoerg /* Current is hardpath itself. */ 4171553Srgrimes || current[hardpath_len] == '\0') 4181553Srgrimes retval = 0; 41983684Sgad else 42083684Sgad /* It isn't a problem. For example, current is 4211553Srgrimes "/foo/cvsroot-bar" and hardpath is "/foo/cvsroot". */ 4221553Srgrimes retval = 1; 4231553Srgrimes } 4241553Srgrimes else 4251553Srgrimes retval = 1; 4261553Srgrimes free (current); 4271553Srgrimes return retval; 4281553Srgrimes} 4291553Srgrimes 4301553Srgrimesstruct dir_to_build 4311553Srgrimes{ 4321553Srgrimes /* What to put in CVS/Repository. */ 4331553Srgrimes char *repository; 4341553Srgrimes /* The path to the directory. */ 4351553Srgrimes char *dirpath; 43653956Sache 4371553Srgrimes /* If set, don't build the directory, just change to it. 4381553Srgrimes The caller will also want to set REPOSITORY to NULL. */ 4391553Srgrimes int just_chdir; 4401553Srgrimes 4411553Srgrimes struct dir_to_build *next; 4421553Srgrimes}; 4431553Srgrimes 4441553Srgrimesstatic int build_dirs_and_chdir PROTO ((struct dir_to_build *list, 4451553Srgrimes int sticky)); 44678300Sgad 44727748Simpstatic void build_one_dir PROTO ((char *, char *, int)); 44880133Sgad 44927748Simpstatic void 4501553Srgrimesbuild_one_dir (repository, dirpath, sticky) 4511553Srgrimes char *repository; 4521553Srgrimes char *dirpath; 45380133Sgad int sticky; 45431492Swollman{ 4551553Srgrimes FILE *fp; 4561553Srgrimes 45731492Swollman if (isfile (CVSADM)) 4581553Srgrimes { 4591553Srgrimes if (m_type == EXPORT) 4601553Srgrimes error (1, 0, "cannot export into a working directory"); 4611553Srgrimes } 4621553Srgrimes else if (m_type == CHECKOUT) 4631553Srgrimes { 4641553Srgrimes /* I suspect that this check could be omitted. */ 4651553Srgrimes if (!isdir (repository)) 4661553Srgrimes error (1, 0, "there is no repository %s", repository); 4671553Srgrimes 4681553Srgrimes if (Create_Admin (".", dirpath, repository, 4691553Srgrimes sticky ? tag : (char *) NULL, 4701553Srgrimes sticky ? date : (char *) NULL, 4711553Srgrimes 4721553Srgrimes /* FIXME? This is a guess. If it is important 4731553Srgrimes for nonbranch to be set correctly here I 4741553Srgrimes think we need to write it one way now and 4751553Srgrimes then rewrite it later via WriteTag, once 4761553Srgrimes we've had a chance to call RCS_nodeisbranch 47727748Simp on each file. */ 47880133Sgad 0, 1, 1)) 47927748Simp return; 4801553Srgrimes 4811553Srgrimes if (!noexec) 4821553Srgrimes { 4831553Srgrimes fp = open_file (CVSADM_ENTSTAT, "w+"); 4841553Srgrimes if (fclose (fp) == EOF) 48580133Sgad error (1, errno, "cannot close %s", CVSADM_ENTSTAT); 48680133Sgad#ifdef SERVER_SUPPORT 48780133Sgad if (server_active) 48880133Sgad server_set_entstat (dirpath, repository); 4891553Srgrimes#endif 49080133Sgad } 49180133Sgad } 4921553Srgrimes} 4931553Srgrimes 4941553Srgrimes/* 49580133Sgad * process_module calls us back here so we do the actual checkout stuff 4961553Srgrimes */ 4971553Srgrimes/* ARGSUSED */ 4981553Srgrimesstatic int 49931492Swollmancheckout_proc (argc, argv, where_orig, mwhere, mfile, shorten, 50031492Swollman local_specified, omodule, msg) 5011553Srgrimes int argc; 5021553Srgrimes char **argv; 5031553Srgrimes char *where_orig; 5041553Srgrimes char *mwhere; 5051553Srgrimes char *mfile; 5061553Srgrimes int shorten; 50727748Simp int local_specified; 50880133Sgad char *omodule; 50980133Sgad char *msg; 51027748Simp{ 5111553Srgrimes char *myargv[2]; 5121553Srgrimes int err = 0; 5131553Srgrimes int which; 51480133Sgad char *cp; 5151553Srgrimes char *repository; 5161553Srgrimes char *oldupdate = NULL; 5171553Srgrimes char *where; 51880133Sgad 5191553Srgrimes /* 5201553Srgrimes * OK, so we're doing the checkout! Our args are as follows: 52153956Sache * argc,argv contain either dir or dir followed by a list of files 52280133Sgad * where contains where to put it (if supplied by checkout) 52353956Sache * mwhere contains the module name or -d from module file 52453956Sache * mfile says do only that part of the module 5251553Srgrimes * shorten = 1 says shorten as much as possible 52668467Sgad * omodule is the original arg to do_module() 52768467Sgad */ 52868467Sgad 52968467Sgad /* Set up the repository (maybe) for the bottom directory. 53068467Sgad Allocate more space than we need so we don't need to keep 53168467Sgad reallocating this string. */ 53268467Sgad repository = xmalloc (strlen (current_parsed_root->directory) 53397792Sgad + strlen (argv[0]) 53468467Sgad + (mfile == NULL ? 0 : strlen (mfile)) 53568467Sgad + 10); 53668467Sgad (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); 53797792Sgad Sanitize_Repository_Name (repository); 53868467Sgad 53968467Sgad 54068467Sgad /* save the original value of preload_update_dir */ 54168467Sgad if (preload_update_dir != NULL) 5421553Srgrimes oldupdate = xstrdup (preload_update_dir); 5431553Srgrimes 5441553Srgrimes 5451553Srgrimes /* Allocate space and set up the where variable. We allocate more 5461553Srgrimes space than necessary here so that we don't have to keep 5471553Srgrimes reallocaing it later on. */ 54897791Sgad 5491553Srgrimes where = xmalloc (strlen (argv[0]) 5501553Srgrimes + (mfile == NULL ? 0 : strlen (mfile)) 5511553Srgrimes + (mwhere == NULL ? 0 : strlen (mwhere)) 55231492Swollman + (where_orig == NULL ? 0 : strlen (where_orig)) 5531553Srgrimes + 10); 5541553Srgrimes 5551553Srgrimes /* Yes, this could be written in a less verbose way, but in this 5561553Srgrimes form it is quite easy to read. 5571553Srgrimes 5581553Srgrimes FIXME? The following code that sets should probably be moved 5591553Srgrimes to do_module in modules.c, since there is similar code in 56015648Sjoerg patch.c and rtag.c. */ 5611553Srgrimes 5621553Srgrimes if (shorten) 5631553Srgrimes { 5641553Srgrimes if (where_orig != NULL) 5651553Srgrimes { 5661553Srgrimes /* If the user has specified a directory with `-d' on the 5671553Srgrimes command line, use it preferentially, even over the `-d' 5681553Srgrimes flag in the modules file. */ 5691553Srgrimes 5701553Srgrimes (void) strcpy (where, where_orig); 57131492Swollman } 57231492Swollman else if (mwhere != NULL) 5731553Srgrimes { 5741553Srgrimes /* Second preference is the value of mwhere, which is from 5751553Srgrimes the `-d' flag in the modules file. */ 5761553Srgrimes 57731492Swollman (void) strcpy (where, mwhere); 5781553Srgrimes } 5791553Srgrimes else 5801553Srgrimes { 58127748Simp /* Third preference is the directory specified in argv[0] 58227748Simp which is this module'e directory in the repository. */ 5831553Srgrimes 5841553Srgrimes (void) strcpy (where, argv[0]); 5851553Srgrimes } 5861553Srgrimes } 5871553Srgrimes else 5881553Srgrimes { 5891553Srgrimes /* Use the same preferences here, bug don't shorten -- that 59097791Sgad is, tack on where_orig if it exists. */ 5911553Srgrimes 5921553Srgrimes *where = '\0'; 5931553Srgrimes 5941553Srgrimes if (where_orig != NULL) 5951553Srgrimes { 5961553Srgrimes (void) strcat (where, where_orig); 5971553Srgrimes (void) strcat (where, "/"); 5981553Srgrimes } 5991553Srgrimes 6001553Srgrimes /* If the -d flag in the modules file specified an absolute 6011553Srgrimes directory, let the user override it with the command-line 6021553Srgrimes -d option. */ 6031553Srgrimes 60478146Sgad if ((mwhere != NULL) && (! isabsolute (mwhere))) 6051553Srgrimes (void) strcat (where, mwhere); 60653956Sache else 6071553Srgrimes (void) strcat (where, argv[0]); 60831492Swollman } 6091553Srgrimes strip_trailing_slashes (where); /* necessary? */ 6101553Srgrimes 61197793Sgad 61297793Sgad /* At this point, the user may have asked for a single file or 6131553Srgrimes directory from within a module. In that case, we should modify 6141553Srgrimes where, repository, and argv as appropriate. */ 61568467Sgad 61668467Sgad if (mfile != NULL) 61797792Sgad { 61897791Sgad /* The mfile variable can have one or more path elements. If 61968467Sgad it has multiple elements, we want to tack those onto both 6201553Srgrimes repository and where. The last element may refer to either 6211553Srgrimes a file or directory. Here's what to do: 6221553Srgrimes 6231553Srgrimes it refers to a directory 6241553Srgrimes -> simply tack it on to where and repository 6251553Srgrimes it refers to a file 6261553Srgrimes -> munge argv to contain `basename mfile` */ 62797791Sgad 62868253Sgad char *cp; 62968253Sgad char *path; 63068734Sgad 63168253Sgad 63268253Sgad /* Paranoia check. */ 63331492Swollman 63431492Swollman if (mfile[strlen (mfile) - 1] == '/') 63531492Swollman { 6361553Srgrimes error (0, 0, "checkout_proc: trailing slash on mfile (%s)!", 63731492Swollman mfile); 63883684Sgad } 63931492Swollman 6401553Srgrimes 6411553Srgrimes /* Does mfile have multiple path elements? */ 6421553Srgrimes 64397791Sgad cp = strrchr (mfile, '/'); 6441553Srgrimes if (cp != NULL) 6451553Srgrimes { 64697791Sgad *cp = '\0'; 6471553Srgrimes (void) strcat (repository, "/"); 6481553Srgrimes (void) strcat (repository, mfile); 6491553Srgrimes (void) strcat (where, "/"); 65031492Swollman (void) strcat (where, mfile); 6511553Srgrimes mfile = cp + 1; 65253956Sache } 65353956Sache 65453956Sache 65553956Sache /* Now mfile is a single path element. */ 65653956Sache 65753956Sache path = xmalloc (strlen (repository) + strlen (mfile) + 5); 65853956Sache (void) sprintf (path, "%s/%s", repository, mfile); 65953956Sache if (isdir (path)) 66053956Sache { 66153956Sache /* It's a directory, so tack it on to repository and 6621553Srgrimes where, as we did above. */ 6631553Srgrimes 6641553Srgrimes (void) strcat (repository, "/"); 6651553Srgrimes (void) strcat (repository, mfile); 66631492Swollman (void) strcat (where, "/"); 6671553Srgrimes (void) strcat (where, mfile); 6681553Srgrimes } 6698094Sjkh else 67031492Swollman { 6711553Srgrimes /* It's a file, which means we have to screw around with 67253956Sache argv. */ 67353956Sache myargv[0] = argv[0]; 67479452Sbrian myargv[1] = mfile; 6751553Srgrimes argc = 2; 6761553Srgrimes argv = myargv; 6771553Srgrimes } 6781553Srgrimes free (path); 6791553Srgrimes } 6801553Srgrimes 6811553Srgrimes if (preload_update_dir != NULL) 6821553Srgrimes { 68397791Sgad preload_update_dir = 6841553Srgrimes xrealloc (preload_update_dir, 6851553Srgrimes strlen (preload_update_dir) + strlen (where) + 5); 6861553Srgrimes strcat (preload_update_dir, "/"); 68731492Swollman strcat (preload_update_dir, where); 6881553Srgrimes } 6891553Srgrimes else 6901553Srgrimes preload_update_dir = xstrdup (where); 6911553Srgrimes 6921553Srgrimes /* 69386935Sgad * At this point, where is the directory we want to build, repository is 69486935Sgad * the repository for the lowest level of the path. 69586935Sgad * 69686935Sgad * We need to tell build_dirs not only the path we want it to 69786935Sgad * build, but also the repositories we want it to populate the 69886935Sgad * path with. To accomplish this, we walk the path backwards, one 69986935Sgad * pathname component at a time, constucting a linked list of 70086935Sgad * struct dir_to_build. 70186935Sgad */ 70286935Sgad 70386935Sgad /* 7041553Srgrimes * If we are sending everything to stdout, we can skip a whole bunch of 70531492Swollman * work from here 7061553Srgrimes */ 7071553Srgrimes if (!pipeout) 7081553Srgrimes { 7091553Srgrimes struct dir_to_build *head; 7101553Srgrimes char *reposcopy; 7111553Srgrimes 7121553Srgrimes if (strncmp (repository, current_parsed_root->directory, 71331492Swollman strlen (current_parsed_root->directory)) != 0) 7141553Srgrimes error (1, 0, "\ 7151553Srgrimesinternal error: %s doesn't start with %s in checkout_proc", 7161553Srgrimes repository, current_parsed_root->directory); 7171553Srgrimes 7181553Srgrimes /* We always create at least one directory, which corresponds to 7191553Srgrimes the entire strings for WHERE and REPOSITORY. */ 7201553Srgrimes head = (struct dir_to_build *) xmalloc (sizeof (struct dir_to_build)); 7211553Srgrimes /* Special marker to indicate that we don't want build_dirs_and_chdir 7221553Srgrimes to create the CVSADM directory for us. */ 72331492Swollman head->repository = NULL; 72497792Sgad head->dirpath = xstrdup (where); 7251553Srgrimes head->next = NULL; 7261553Srgrimes head->just_chdir = 0; 7271553Srgrimes 7281553Srgrimes 7291553Srgrimes /* Make a copy of the repository name to play with. */ 7301553Srgrimes reposcopy = xstrdup (repository); 7311553Srgrimes 7321553Srgrimes /* FIXME: this should be written in terms of last_component 7331553Srgrimes instead of hardcoding '/'. This presumably affects OS/2, 7341553Srgrimes NT, &c, if the user specifies '\'. Likewise for the call 7351553Srgrimes to findslash. */ 73631492Swollman cp = where + strlen (where); 73731492Swollman while (cp > where) 73831492Swollman { 7391553Srgrimes struct dir_to_build *new; 7401553Srgrimes 7411553Srgrimes cp = findslash (where, cp - 1); 7421553Srgrimes if (cp == NULL) 7431553Srgrimes break; /* we're done */ 74431492Swollman 7451553Srgrimes new = (struct dir_to_build *) 7461553Srgrimes xmalloc (sizeof (struct dir_to_build)); 7471553Srgrimes new->dirpath = xmalloc (strlen (where)); 7481553Srgrimes 7491553Srgrimes /* If the user specified an absolute path for where, the 75031492Swollman last path element we create should be the top-level 7511553Srgrimes directory. */ 7521553Srgrimes 7531553Srgrimes if (cp > where) 7541553Srgrimes { 7551553Srgrimes strncpy (new->dirpath, where, cp - where); 75631492Swollman new->dirpath[cp - where] = '\0'; 7571553Srgrimes } 7581553Srgrimes else 7591553Srgrimes { 7601553Srgrimes /* where should always be at least one character long. */ 7611553Srgrimes assert (where[0] != '\0'); 7621553Srgrimes strcpy (new->dirpath, "/"); 7631553Srgrimes } 76497792Sgad new->next = head; 76597791Sgad head = new; 7661553Srgrimes 76715648Sjoerg /* If where consists of multiple pathname components, 76815648Sjoerg then we want to just cd into it, without creating 76915648Sjoerg directories or modifying CVS directories as we go. 77015648Sjoerg In CVS 1.9 and earlier, the code actually does a 77131492Swollman CVS_CHDIR up-front; I'm not going to try to go back 77297791Sgad to that exact code but this is somewhat similar 77315648Sjoerg in spirit. */ 77427635Simp if (where_orig != NULL 7751553Srgrimes && cp - where < strlen (where_orig)) 7761553Srgrimes { 7771553Srgrimes new->repository = NULL; 7781553Srgrimes new->just_chdir = 1; 7791553Srgrimes continue; 7801553Srgrimes } 78178300Sgad 78231492Swollman new->just_chdir = 0; 7831553Srgrimes 7841553Srgrimes /* Now figure out what repository directory to generate. 78597793Sgad The most complete case would be something like this: 7861553Srgrimes 78797793Sgad The modules file contains 78897793Sgad foo -d bar/baz quux 7891553Srgrimes 79097793Sgad The command issued was: 79197792Sgad cvs co -d what/ever -N foo 79297792Sgad 79379735Sgad The results in the CVS/Repository files should be: 79497781Sgad . -> (don't touch CVS/Repository) 7951553Srgrimes (I think this case might be buggy currently) 79697781Sgad what -> (don't touch CVS/Repository) 79797781Sgad ever -> . (same as "cd what/ever; cvs co -N foo") 79897793Sgad bar -> Emptydir (generated dir -- not in repos) 79997781Sgad baz -> quux (finally!) */ 80097791Sgad 8011553Srgrimes if (strcmp (reposcopy, current_parsed_root->directory) == 0) 8021553Srgrimes { 8031553Srgrimes /* We can't walk up past CVSROOT. Instead, the 8041553Srgrimes repository should be Emptydir. */ 80531492Swollman new->repository = emptydir_name (); 8061553Srgrimes } 8071553Srgrimes else 80868664Sgad { 80968664Sgad /* It's a directory in the repository! */ 81068664Sgad 8111553Srgrimes char *rp; 8121553Srgrimes 8138094Sjkh /* We'll always be below CVSROOT, but check for 81431492Swollman paranoia's sake. */ 8151553Srgrimes rp = strrchr (reposcopy, '/'); 81695067Sgad if (rp == NULL) 81795067Sgad error (1, 0, 8181553Srgrimes "internal error: %s doesn't contain a slash", 8191553Srgrimes reposcopy); 8201553Srgrimes 82197789Sgad *rp = '\0'; 8221553Srgrimes new->repository = xmalloc (strlen (reposcopy) + 5); 82397781Sgad (void) strcpy (new->repository, reposcopy); 82479735Sgad 82597793Sgad if (strcmp (reposcopy, current_parsed_root->directory) == 0) 8261553Srgrimes { 82797793Sgad /* Special case -- the repository name needs 82897781Sgad to be "/path/to/repos/." (the trailing dot 82997792Sgad is important). We might be able to get rid 83097792Sgad of this after the we check out the other 83179735Sgad code that handles repository names. */ 83297789Sgad (void) strcat (new->repository, "/."); 83397789Sgad } 83497781Sgad } 83597789Sgad } 83679735Sgad 8371553Srgrimes /* clean up */ 8381553Srgrimes free (reposcopy); 8391553Srgrimes 84097793Sgad { 8411553Srgrimes int where_is_absolute = isabsolute (where); 8421553Srgrimes 8431553Srgrimes /* The top-level CVSADM directory should always be 8441553Srgrimes current_parsed_root->directory. Create it, but only if WHERE is 84531492Swollman relative. If WHERE is absolute, our current directory 8461553Srgrimes may not have a thing to do with where the sources are 84768664Sgad being checked out. If it does, build_dirs_and_chdir 84868664Sgad will take care of creating adm files here. */ 8491553Srgrimes /* FIXME: checking where_is_absolute is a horrid kludge; 8501553Srgrimes I suspect we probably can just skip the call to 8511553Srgrimes build_one_dir whenever the -d command option was specified 8521553Srgrimes to checkout. */ 8531553Srgrimes 85497789Sgad if (! where_is_absolute && top_level_admin) 85515648Sjoerg { 85697781Sgad /* It may be argued that we shouldn't set any sticky 85797791Sgad bits for the top-level repository. FIXME? */ 8581553Srgrimes build_one_dir (current_parsed_root->directory, ".", argc <= 1); 85997781Sgad 8601553Srgrimes#ifdef SERVER_SUPPORT 86131492Swollman /* We _always_ want to have a top-level admin 86297791Sgad directory. If we're running in client/server mode, 8631553Srgrimes send a "Clear-static-directory" command to make 86497791Sgad sure it is created on the client side. (See 5.10 86515648Sjoerg in cvsclient.dvi to convince yourself that this is 86697791Sgad OK.) If this is a duplicate command being sent, it 8671553Srgrimes will be ignored on the client side. */ 86815648Sjoerg 86997792Sgad if (server_active) 87097791Sgad server_clear_entstat (".", current_parsed_root->directory); 8711553Srgrimes#endif 8721553Srgrimes } 8731553Srgrimes 8741553Srgrimes 8751553Srgrimes /* Build dirs on the path if necessary and leave us in the 8761553Srgrimes bottom directory (where if where was specified) doesn't 8771553Srgrimes contain a CVS subdir yet, but all the others contain 8781553Srgrimes CVS and Entries.Static files */ 8791553Srgrimes 88078146Sgad if (build_dirs_and_chdir (head, argc <= 1) != 0) 8811553Srgrimes { 88295293Sgad error (0, 0, "ignoring module %s", omodule); 8831553Srgrimes err = 1; 8841553Srgrimes goto out; 8851553Srgrimes } 8861553Srgrimes } 8871553Srgrimes 8881553Srgrimes /* set up the repository (or make sure the old one matches) */ 88997791Sgad if (!isfile (CVSADM)) 89068253Sgad { 89168253Sgad FILE *fp; 89268253Sgad 89368253Sgad if (!noexec && argc > 1) 8941553Srgrimes { 8951553Srgrimes /* I'm not sure whether this check is redundant. */ 8961553Srgrimes if (!isdir (repository)) 8971553Srgrimes error (1, 0, "there is no repository %s", repository); 8981553Srgrimes 8991553Srgrimes Create_Admin (".", preload_update_dir, repository, 9001553Srgrimes (char *) NULL, (char *) NULL, 0, 0, 9011553Srgrimes m_type == CHECKOUT); 9021553Srgrimes fp = open_file (CVSADM_ENTSTAT, "w+"); 9031553Srgrimes if (fclose(fp) == EOF) 9041553Srgrimes error(1, errno, "cannot close %s", CVSADM_ENTSTAT); 9051553Srgrimes#ifdef SERVER_SUPPORT 9061553Srgrimes if (server_active) 9071553Srgrimes server_set_entstat (where, repository); 9081553Srgrimes#endif 90995293Sgad } 9101553Srgrimes else 9111553Srgrimes { 9121553Srgrimes /* I'm not sure whether this check is redundant. */ 9131553Srgrimes if (!isdir (repository)) 9141553Srgrimes error (1, 0, "there is no repository %s", repository); 9151553Srgrimes 9161553Srgrimes Create_Admin (".", preload_update_dir, repository, tag, date, 9171553Srgrimes 9181553Srgrimes /* FIXME? This is a guess. If it is important 9191553Srgrimes for nonbranch to be set correctly here I 9201553Srgrimes think we need to write it one way now and 9211553Srgrimes then rewrite it later via WriteTag, once 9221553Srgrimes we've had a chance to call RCS_nodeisbranch 92324831Sbrian on each file. */ 92478300Sgad 0, 0, m_type == CHECKOUT); 92568343Sgad } 92680133Sgad } 92768343Sgad else 92824831Sbrian { 92980133Sgad char *repos; 93031492Swollman 93124831Sbrian if (m_type == EXPORT) 93231492Swollman error (1, 0, "cannot export into working directory"); 93324831Sbrian 93424831Sbrian /* get the contents of the previously existing repository */ 93524831Sbrian repos = Name_Repository ((char *) NULL, preload_update_dir); 93624831Sbrian if (fncmp (repository, repos) != 0) 93724831Sbrian { 93880133Sgad error (0, 0, "existing repository %s does not match %s", 93924831Sbrian repos, repository); 94095293Sgad error (0, 0, "ignoring module %s", omodule); 9411553Srgrimes free (repos); 94295293Sgad err = 1; 94395293Sgad goto out; 9441553Srgrimes } 94595293Sgad free (repos); 94695293Sgad } 94795293Sgad } 9481553Srgrimes 9491553Srgrimes /* 9501553Srgrimes * If we are going to be updating to stdout, we need to cd to the 9511553Srgrimes * repository directory so the recursion processor can use the current 9521553Srgrimes * directory as the place to find repository information 9531553Srgrimes */ 95497791Sgad if (pipeout) 9551553Srgrimes { 95631492Swollman if ( CVS_CHDIR (repository) < 0) 9571553Srgrimes { 9581553Srgrimes error (0, errno, "cannot chdir to %s", repository); 9591553Srgrimes err = 1; 9601553Srgrimes goto out; 9611553Srgrimes } 9621553Srgrimes which = W_REPOS; 96395293Sgad if (tag != NULL && !tag_validated) 9641553Srgrimes { 96597791Sgad tag_check_valid (tag, argc - 1, argv + 1, 0, aflag, NULL); 9661553Srgrimes tag_validated = 1; 9671553Srgrimes } 9681553Srgrimes } 9691553Srgrimes else 9701553Srgrimes { 9711553Srgrimes which = W_LOCAL | W_REPOS; 97227748Simp if (tag != NULL && !tag_validated) 9731553Srgrimes { 9741553Srgrimes tag_check_valid (tag, argc - 1, argv + 1, 0, aflag, 9751553Srgrimes repository); 9761553Srgrimes tag_validated = 1; 9771553Srgrimes } 9781553Srgrimes } 97997791Sgad 9801553Srgrimes if (tag != NULL || date != NULL || join_rev1 != NULL) 9811553Srgrimes which |= W_ATTIC; 9821553Srgrimes 9831553Srgrimes if (! join_tags_validated) 9841553Srgrimes { 9851553Srgrimes if (join_rev1 != NULL) 9861553Srgrimes tag_check_valid_join (join_rev1, argc - 1, argv + 1, 0, aflag, 98795293Sgad repository); 9881553Srgrimes if (join_rev2 != NULL) 98994036Sgad tag_check_valid_join (join_rev2, argc - 1, argv + 1, 0, aflag, 9901553Srgrimes repository); 99194032Sgad join_tags_validated = 1; 99294032Sgad } 99395293Sgad 9941553Srgrimes /* 99574124Sgad * if we are going to be recursive (building dirs), go ahead and call the 99674124Sgad * update recursion processor. We will be recursive unless either local 99774124Sgad * only was specified, or we were passed arguments 99874124Sgad */ 99994036Sgad if (!(local_specified || argc > 1)) 100074124Sgad { 100194036Sgad if (m_type == CHECKOUT && !pipeout) 100294036Sgad history_write ('O', preload_update_dir, history_name, where, 100374124Sgad repository); 100474124Sgad else if (m_type == EXPORT && !pipeout) 100594036Sgad history_write ('E', preload_update_dir, tag ? tag : date, where, 100674124Sgad repository); 10071553Srgrimes err += do_update (0, (char **) NULL, options, tag, date, 10081553Srgrimes force_tag_match, 0 /* !local */ , 10091553Srgrimes 1 /* update -d */ , aflag, checkout_prune_dirs, 10101553Srgrimes pipeout, which, join_rev1, join_rev2, 10111553Srgrimes preload_update_dir, m_type == CHECKOUT); 101294036Sgad goto out; 101394036Sgad } 101494036Sgad 101594036Sgad if (!pipeout) 101694036Sgad { 101724831Sbrian int i; 101894032Sgad List *entries; 101994032Sgad 102024831Sbrian /* we are only doing files, so register them */ 102194032Sgad entries = Entries_Open (0, NULL); 102224831Sbrian for (i = 1; i < argc; i++) 102394032Sgad { 102494032Sgad char *line; 102594032Sgad Vers_TS *vers; 102694032Sgad struct file_info finfo; 102794032Sgad 102894032Sgad memset (&finfo, 0, sizeof finfo); 102994032Sgad finfo.file = argv[i]; 103094032Sgad /* Shouldn't be used, so set to arbitrary value. */ 103194032Sgad finfo.update_dir = NULL; 103294032Sgad finfo.fullname = argv[i]; 103394032Sgad finfo.repository = repository; 103494032Sgad finfo.entries = entries; 103594032Sgad /* The rcs slot is needed to get the options from the RCS 103694032Sgad file */ 103794032Sgad finfo.rcs = RCS_parse (finfo.file, repository); 103894032Sgad 103994032Sgad vers = Version_TS (&finfo, options, tag, date, 104094032Sgad force_tag_match, 0); 104194032Sgad if (vers->ts_user == NULL) 104294032Sgad { 104394032Sgad line = xmalloc (strlen (finfo.file) + 15); 104494032Sgad (void) sprintf (line, "Initial %s", finfo.file); 104531492Swollman Register (entries, finfo.file, 104694032Sgad vers->vn_rcs ? vers->vn_rcs : "0", 104794032Sgad line, vers->options, vers->tag, 104894032Sgad vers->date, (char *) 0); 104994032Sgad free (line); 105094032Sgad } 105194032Sgad freevers_ts (&vers); 105224831Sbrian freercsnode (&finfo.rcs); 105394032Sgad } 105494032Sgad 105594032Sgad Entries_Close (entries); 105694032Sgad } 105794032Sgad 105894032Sgad /* Don't log "export", just regular "checkouts" */ 105994032Sgad if (m_type == CHECKOUT && !pipeout) 106094032Sgad history_write ('O', preload_update_dir, history_name, where, 106194032Sgad repository); 106294032Sgad 106394032Sgad /* go ahead and call update now that everything is set */ 106494032Sgad err += do_update (argc - 1, argv + 1, options, tag, date, 106594032Sgad force_tag_match, local_specified, 1 /* update -d */, 106694032Sgad aflag, checkout_prune_dirs, pipeout, which, join_rev1, 106794032Sgad join_rev2, preload_update_dir, m_type == CHECKOUT); 106894032Sgadout: 106994032Sgad free (preload_update_dir); 107024831Sbrian preload_update_dir = oldupdate; 107124831Sbrian free (where); 107294032Sgad free (repository); 107394032Sgad return (err); 107494032Sgad} 107594032Sgad 107694032Sgadstatic char * 107794032Sgadfindslash (start, p) 107894032Sgad char *start; 107994032Sgad char *p; 108094032Sgad{ 108194032Sgad for (;;) 108294032Sgad { 108394032Sgad if (*p == '/') return p; 108494032Sgad if (p == start) break; 108594036Sgad --p; 108694036Sgad } 108794032Sgad return NULL; 108894036Sgad} 108924831Sbrian 109094032Sgad/* Return a newly malloc'd string containing a pathname for CVSNULLREPOS, 109194032Sgad and make sure that it exists. If there is an error creating the 109294032Sgad directory, give a fatal error. Otherwise, the directory is guaranteed 109394032Sgad to exist when we return. */ 109494032Sgadchar * 109594036Sgademptydir_name () 109694036Sgad{ 109794032Sgad char *repository; 109894036Sgad 109994036Sgad repository = xmalloc (strlen (current_parsed_root->directory) 110094032Sgad + sizeof (CVSROOTADM) 110194032Sgad + sizeof (CVSNULLREPOS) 110294032Sgad + 3); 110394032Sgad (void) sprintf (repository, "%s/%s/%s", current_parsed_root->directory, 110494036Sgad CVSROOTADM, CVSNULLREPOS); 110594036Sgad if (!isfile (repository)) 110694032Sgad { 110794032Sgad mode_t omask; 110894032Sgad omask = umask (cvsumask); 110994032Sgad if (CVS_MKDIR (repository, 0777) < 0) 111094032Sgad error (1, errno, "cannot create %s", repository); 111194032Sgad (void) umask (omask); 111294036Sgad } 111394036Sgad return repository; 111494032Sgad} 111594036Sgad 111694036Sgad/* Build all the dirs along the path to DIRS with CVS subdirs with appropriate 111794036Sgad repositories. If ->repository is NULL, do not create a CVSADM directory 111894032Sgad for that subdirectory; just CVS_CHDIR into it. */ 111994032Sgadstatic int 112095293Sgadbuild_dirs_and_chdir (dirs, sticky) 112195293Sgad struct dir_to_build *dirs; 112295293Sgad int sticky; 112395293Sgad{ 112495293Sgad int retval = 0; 112595293Sgad struct dir_to_build *nextdir; 112695293Sgad 112795293Sgad while (dirs != NULL) 112895293Sgad { 11291553Srgrimes char *dir = last_component (dirs->dirpath); 11301553Srgrimes 11311553Srgrimes if (!dirs->just_chdir) 113231492Swollman { 113394036Sgad mkdir_if_needed (dir); 113494036Sgad Subdir_Register (NULL, NULL, dir); 11351553Srgrimes } 11361553Srgrimes 11371553Srgrimes if (CVS_CHDIR (dir) < 0) 113831492Swollman { 113931492Swollman error (0, errno, "cannot chdir to %s", dir); 11401553Srgrimes retval = 1; 11411553Srgrimes goto out; 114297792Sgad } 11431553Srgrimes if (dirs->repository != NULL) 11441553Srgrimes { 11451553Srgrimes build_one_dir (dirs->repository, dirs->dirpath, sticky); 114631492Swollman free (dirs->repository); 114795293Sgad } 114895293Sgad nextdir = dirs->next; 114995293Sgad free (dirs->dirpath); 115095293Sgad free (dirs); 115168253Sgad dirs = nextdir; 115268253Sgad } 11531553Srgrimes 11541553Srgrimes out: 11551553Srgrimes while (dirs != NULL) 11561553Srgrimes { 115794036Sgad if (dirs->repository != NULL) 11581553Srgrimes free (dirs->repository); 11591553Srgrimes nextdir = dirs->next; 116094036Sgad free (dirs->dirpath); 116194036Sgad free (dirs); 11621553Srgrimes dirs = nextdir; 11631553Srgrimes } 11641553Srgrimes return retval; 11651553Srgrimes} 116631492Swollman