int chacl_fileproc(void *callerdat, struct file_info *finfo) { CXmlNodePtr acl; /* If someone has specified 'chacl foo' and foo is a directory, you'll get a dirent plus every file in the directory. We only want to set the directory in this case */ if(acl_directory_set && !strcmp(finfo->repository, acl_directory_set)) return 0; Vers_TS *vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0, 0); if(!vers->vn_user && !vers->vn_rcs) { if (!really_quiet) error (0, 0, "nothing known about %s", fn_root(finfo->fullname)); freevers_ts(&vers); return 0; } freevers_ts(&vers); if(!quiet) printf("%sing ACL for file %s\n",parms.del?"delet":"sett",finfo->file); acl = fileattr_getroot(); acl->xpathVariable("name",finfo->file); if(!acl->Lookup("file[cvs:filename(@name,$name)]") || !acl->XPathResultNext()) { acl = fileattr_getroot(); acl->NewNode("file"); acl->NewAttribute("name",finfo->file); } set_acl(acl); return 0; }
/* * Classify the state of a file */ Ctype Classify_File (struct file_info *finfo, const char *tag, const char *date, const char *options, int force_tag_match, int aflag, Vers_TS **versp, int pipeout, int force_time_mismatch, int ignore_keywords) /* Keyword expansion options can be either NULL or "" to indicate none are specified here. */ { Vers_TS *vers; Ctype ret; kflag kf,kf_ent; TRACE(3,"Classify_File (%s)",PATCH_NULL(finfo->file)); /* get all kinds of good data about the file */ vers = Version_TS (finfo, options, tag, date, force_tag_match, 0, 0); TRACE(3,"vn_rcs=%s",PATCH_NULL(vers->vn_rcs)); RCS_get_kflags(vers->options,false,kf); if(vers->entdata) RCS_get_kflags(vers->entdata->options,false,kf_ent); if(force_time_mismatch) { xfree(vers->ts_user); vers->ts_user=xstrdup("modified"); } if (vers->vn_user == NULL) { /* No entry available, ts_rcs is invalid */ if (vers->vn_rcs == NULL) { /* there is no RCS file either */ if (vers->ts_user == NULL) { /* there is no user file */ /* FIXME: Why do we skip this message if vers->tag or vers->date is set? It causes "cvs update -r tag98 foo" to silently do nothing, which is seriously confusing behavior. "cvs update foo" gives this message, which is what I would expect. */ if (!force_tag_match || !(vers->tag || vers->date)) { if (!really_quiet) error (0, 0, "nothing known about %s", fn_root(finfo->fullname)); } ret = T_UNKNOWN; } else { /* there is a user file */ /* FIXME: Why do we skip this message if vers->tag or vers->date is set? It causes "cvs update -r tag98 foo" to silently do nothing, which is seriously confusing behavior. "cvs update foo" gives this message, which is what I would expect. */ if (!force_tag_match || !(vers->tag || vers->date)) if (!really_quiet) error (0, 0, "use `%s add' to create an entry for %s", program_name, fn_root(finfo->fullname)); ret = T_UNKNOWN; } } else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) { /* there is an RCS file, but it's dead */ if (vers->ts_user == NULL) ret = T_UPTODATE; else { error (0, 0, "use `%s add' to create an entry for %s", program_name, fn_root(finfo->fullname)); ret = T_UNKNOWN; } } else if (vers->ts_user && !strcmp(vers->ts_user,"0") && !strcmp(finfo->file,RCSREPOVERSION)) { /* It's a directory entry, but there isn't one in the repository */ ret = T_UNKNOWN; } else if (!pipeout && vers->ts_user && No_Difference(finfo, vers, (kf.flags&KFLAG_STATIC || kf_ent.flags&KFLAG_STATIC), ignore_keywords)) { /* the files were different so it is a conflict */ if (!really_quiet) error (0, 0, "move away %s; it is in the way", fn_root(finfo->fullname)); ret = T_CONFLICT; } else { /* no user file or no difference, just checkout */ ret = T_CHECKOUT; } } else if (strcmp (vers->vn_user, "0") == 0) { /* An entry for a new-born file; ts_rcs is dummy */ if (vers->ts_user == NULL) { /* * There is no user file, but there should be one; remove the * entry */ if(!really_quiet) error (0, 0, "warning: new-born %s has disappeared", fn_root(finfo->fullname)); ret = T_REMOVE_ENTRY; } else if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) /* No RCS file or RCS file revision is dead */ ret = T_ADDED; else { if (vers->srcfile->flags & VALID) { /* This file has been added on some branch other than the one we are looking at. In the branch we are looking at, the file was already valid. */ if (!really_quiet) error (0, 0, "conflict: %s has been added, but already exists", fn_root(finfo->fullname)); } else { /* * There is an RCS file, so someone else must have checked * one in behind our back; conflict */ if (!really_quiet) error (0, 0, "conflict: %s created independently by second party", fn_root(finfo->fullname)); } ret = T_CONFLICT; } } else if (vers->vn_user[0] == '-') { /* An entry for a removed file, ts_rcs is invalid */ if (vers->ts_user == NULL) { /* There is no user file (as it should be) */ if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) { /* * There is no RCS file; this is all-right, but it has been * removed independently by a second party; remove the entry */ ret = T_REMOVE_ENTRY; } else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0) /* * The RCS file is the same version as the user file was, and * that's OK; remove it */ ret = T_REMOVED; else if (pipeout) /* * The RCS file doesn't match the user's file, but it doesn't * matter in this case */ ret = T_NEEDS_MERGE; else { /* * The RCS file is a newer version than the removed user file * and this is definitely not OK; make it a conflict. */ if (!really_quiet) error (0, 0, "conflict: removed %s was modified by second party", fn_root(finfo->fullname)); ret = T_CONFLICT; } } else { /* The user file shouldn't be there */ if (!really_quiet) error (0, 0, "%s should be removed and is still there", fn_root(finfo->fullname)); ret = T_REMOVED; } } else { /* A normal entry, TS_Rcs is valid */ if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) { /* There is no RCS file */ if (vers->ts_user == NULL) { /* There is no user file, so just remove the entry */ if (!really_quiet) error (0, 0, "warning: %s is not (any longer) pertinent", fn_root(finfo->fullname)); ret = T_REMOVE_ENTRY; } else if (!strcmp (vers->ts_user, vers->ts_rcs)) { /* * The user file is still unmodified, so just remove it from * the entry list */ if (!really_quiet) error (0, 0, "%s is no longer in the repository", fn_root(finfo->fullname)); ret = T_REMOVE_ENTRY; } else if (No_Difference (finfo, vers, (kf.flags&KFLAG_STATIC || kf_ent.flags&KFLAG_STATIC), ignore_keywords)) { /* they are different -> conflict */ if (!really_quiet) error (0, 0, "conflict: %s is modified but no longer in the repository", fn_root(finfo->fullname)); ret = T_CONFLICT; } else { /* they weren't really different */ if (!really_quiet) error (0, 0, "warning: %s is not (any longer) pertinent", fn_root(finfo->fullname)); ret = T_REMOVE_ENTRY; } } else if (strcmp (vers->vn_rcs, vers->vn_user) == 0) { /* The RCS file is the same version as the user file */ if (vers->ts_user == NULL) { /* * There is no user file, so note that it was lost and * extract a new version */ /* Comparing the command_name against "update", in addition to being an ugly way to operate, means that this message does not get printed by the server. That might be considered just a straight bug, although there is one subtlety: that case also gets hit when a patch fails and the client fetches a file. I'm not sure there is currently any way for the server to distinguish those two cases. */ if (strcmp (command_name, "update") == 0) if (!really_quiet) error (0, 0, "warning: %s was lost", fn_root(finfo->fullname)); ret = T_CHECKOUT; } else if (!strcmp(vers->ts_user,"0") && !strcmp(finfo->file,RCSREPOVERSION)) { /* This is a directory... The only important entry is the version*/ ret = T_UPTODATE; } else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) { /* Check for expansion option changes */ if (options && !options_same(vers->entdata->options,vers->options)) ret = T_CHECKOUT; else { if(aflag && !options_same(vers->entdata->options, vers->options)) ret = T_CHECKOUT; else ret = T_UPTODATE; /* Changing the expansion option now requires a commit -f, which makes sense (since you might have a sandbox specially checked out as -kb for example) */ sticky_ck (finfo, aflag, vers); } } else if (vers->entdata->merge_from_tag_1[0]) { ret = T_MODIFIED; } else if (No_Difference (finfo, vers, (kf.flags&KFLAG_STATIC || kf_ent.flags&KFLAG_STATIC), ignore_keywords)) { ret = T_MODIFIED; sticky_ck (finfo, aflag, vers); } else if (!options_same(vers->entdata->options,vers->options)) { /* file has not changed; check out if -k changed */ ret = T_CHECKOUT; } else { /* * else -> note that No_Difference will Register the * file already for us, using the new tag/date. This * is the desired behaviour */ ret = T_UPTODATE; } } else { /* The RCS file is a newer version than the user file */ if (vers->ts_user == NULL) { /* There is no user file, so just get it */ /* See comment at other "update" compare, for more thoughts on this comparison. */ if (strcmp (command_name, "update") == 0) if (!really_quiet) error (0, 0, "warning: %s was lost", fn_root(finfo->fullname)); ret = T_CHECKOUT; } else if (!strcmp (vers->ts_user, vers->ts_rcs)) { /* * The user file is still unmodified, so just get it as well */ if (!options_same(vers->entdata->options,vers->options)) ret = T_CHECKOUT; else ret = T_PATCH; } else if (No_Difference (finfo, vers, (kf.flags&KFLAG_STATIC || kf_ent.flags&KFLAG_STATIC), ignore_keywords)) /* really modified, needs to merge */ ret = T_NEEDS_MERGE; else if(!options_same(vers->entdata->options,vers->options)) /* not really modified, check it out */ ret = T_CHECKOUT; else ret = T_PATCH; } } /* free up the vers struct, or just return it */ if (versp != (Vers_TS **) NULL) *versp = vers; else freevers_ts (&vers); /* return the status of the file */ return (ret); }
/* The purpose of "select_hrec" is to apply the selection criteria based on * the command arguments and defaults and return a flag indicating whether * this record should be remembered for printing. */ static int select_hrec (struct hrec *hr) { char **cpp, *cp, *cp2; struct file_list_str *fl; int count; /* basic validity checking */ if (!hr->type || !hr->user || !hr->dir || !hr->repos || !hr->rev || !hr->file || !hr->end) { error (0, 0, "warning: history line %ld invalid", hr->idx); return 0; } /* "Since" checking: The argument parser guarantees that only one of the * following four choices is set: * * 1. If "since_date" is set, it contains the date specified on the * command line. hr->date fields earlier than "since_date" are ignored. * 2. If "since_rev" is set, it contains either an RCS "dotted" revision * number (which is of limited use) or a symbolic TAG. Each RCS file * is examined and the date on the specified revision (or the revision * corresponding to the TAG) in the RCS file (CVSROOT/repos/file) is * compared against hr->date as in 1. above. * 3. If "since_tag" is set, matching tag records are saved. The field * "last_since_tag" is set to the last one of these. Since we don't * know where the last one will be, all records are saved from the * first occurrence of the TAG. Later, at the end of "select_hrec" * records before the last occurrence of "since_tag" are skipped. * 4. If "backto" is set, all records with a module name or file name * matching "backto" are saved. In addition, all records with a * repository field with a *prefix* matching "backto" are saved. * The field "last_backto" is set to the last one of these. As in * 3. above, "select_hrec" adjusts to include the last one later on. */ if (since_date) { char *ourdate = date_from_time_t (hr->date); count = RCS_datecmp (ourdate, since_date); free (ourdate); if (count < 0) return 0; } else if (*since_rev) { Vers_TS *vers; time_t t; struct file_info finfo; memset (&finfo, 0, sizeof finfo); finfo.file = hr->file; /* Not used, so don't worry about it. */ finfo.update_dir = NULL; finfo.fullname = finfo.file; finfo.repository = hr->repos; finfo.entries = NULL; finfo.rcs = NULL; vers = Version_TS (&finfo, NULL, since_rev, NULL, 1, 0); if (vers->vn_rcs) { if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, NULL, 0)) != (time_t) 0) { if (hr->date < t) { freevers_ts (&vers); return 0; } } } freevers_ts (&vers); } else if (*since_tag) { if (*(hr->type) == 'T') { /* * A 'T'ag record, the "rev" field holds the tag to be set, * while the "repos" field holds "D"elete, "A"dd or a rev. */ if (within (since_tag, hr->rev)) { last_since_tag = hr; return 1; } else return 0; } if (!last_since_tag) return 0; } else if (*backto) { if (within (backto, hr->file) || within (backto, hr->mod) || within (backto, hr->repos)) last_backto = hr; else return 0; } /* User checking: * * Run down "user_list", match username ("" matches anything) * If "" is not there and actual username is not there, return failure. */ if (user_list && hr->user) { for (cpp = user_list, count = user_count; count; cpp++, count--) { if (!**cpp) break; /* null user == accept */ if (!strcmp (hr->user, *cpp)) /* found listed user */ break; } if (!count) return 0; /* Not this user */ } /* Record type checking: * * 1. If Record type is not in rec_types field, skip it. * 2. If mod_list is null, keep everything. Otherwise keep only modules * on mod_list. * 3. If neither a 'T', 'F' nor 'O' record, run through "file_list". If * file_list is null, keep everything. Otherwise, keep only files on * file_list, matched appropriately. */ if (!strchr (rec_types, *(hr->type))) return 0; if (!strchr ("TFOE", *(hr->type))) /* Don't bother with "file" if "TFOE" */ { if (file_list) /* If file_list is null, accept all */ { for (fl = file_list, count = file_count; count; fl++, count--) { /* 1. If file_list entry starts with '*', skip the '*' and * compare it against the repository in the hrec. * 2. If file_list entry has a '/' in it, compare it against * the concatenation of the repository and file from hrec. * 3. Else compare the file_list entry against the hrec file. */ char *cmpfile = NULL; if (*(cp = fl->l_file) == '*') { cp++; /* if argument to -p is a prefix of repository */ if (!strncmp (cp, hr->repos, strlen (cp))) { hr->mod = fl->l_module; break; } } else { if (strchr (cp, '/')) { cmpfile = Xasprintf ("%s/%s", hr->repos, hr->file); cp2 = cmpfile; } else { cp2 = hr->file; } /* if requested file is found within {repos}/file fields */ if (within (cp, cp2)) { hr->mod = fl->l_module; if (cmpfile != NULL) free (cmpfile); break; } if (cmpfile != NULL) free (cmpfile); } } if (!count) return 0; /* String specified and no match */ } } if (mod_list) { for (cpp = mod_list, count = mod_count; count; cpp++, count--) { if (hr->mod && !strcmp (hr->mod, *cpp)) /* found module */ break; } if (!count) return 0; /* Module specified & this record is not one of them. */ } return 1; /* Select this record unless rejected above. */ }
/* ARGSUSED */ static int diff_fileproc (void *callerdat, struct file_info *finfo) { int status, err = 2; /* 2 == trouble, like rcsdiff */ Vers_TS *vers; enum diff_file empty_file = DIFF_DIFFERENT; char *tmp; char *fname; char *label1; char *label2; char *default_branch; /* Initialize these solely to avoid warnings from gcc -Wall about variables that might be used uninitialized. */ tmp = NULL; fname = NULL; user_file_rev = 0; vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0, 0); if(vers->tag && RCS_isbranch(finfo->rcs, vers->tag)) default_branch = vers->tag; else default_branch = NULL; if (diff_rev2 != NULL || diff_date2 != NULL) { /* Skip all the following checks regarding the user file; we're not using it. */ } else if (vers->vn_user == NULL) { /* The file does not exist in the working directory. */ if ((diff_rev1 != NULL || diff_date1 != NULL) && vers->srcfile != NULL) { /* The file does exist in the repository. */ if (empty_files) empty_file = DIFF_REMOVED; else { int exists; exists = 0; /* special handling for TAG_HEAD */ if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0) { char *head = (vers->vn_rcs == NULL ? NULL : RCS_branch_head (vers->srcfile, vers->vn_rcs)); exists = head != NULL; if (head != NULL) xfree (head); } else { Vers_TS *xvers; xvers = Version_TS (finfo, NULL, diff_rev1?diff_rev1:default_branch, diff_date1, 1, 0, 0); exists = xvers->vn_rcs != NULL; freevers_ts (&xvers); } if (exists) error (0, 0, "%s no longer exists, no comparison available", fn_root(finfo->fullname)); freevers_ts (&vers); diff_mark_errors (err); return (err); } } else { error (0, 0, "I know nothing about %s", fn_root(finfo->fullname)); freevers_ts (&vers); diff_mark_errors (err); return (err); } } else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') { if (empty_files) empty_file = DIFF_ADDED; else { error (0, 0, "%s is a new entry, no comparison available", fn_root(finfo->fullname)); freevers_ts (&vers); diff_mark_errors (err); return (err); } } else if (vers->vn_user[0] == '-') { if (empty_files) empty_file = DIFF_REMOVED; else { error (0, 0, "%s was removed, no comparison available", fn_root(finfo->fullname)); freevers_ts (&vers); diff_mark_errors (err); return (err); } } else { if (vers->vn_rcs == NULL && vers->srcfile == NULL) { error (0, 0, "cannot find revision control file for %s", fn_root(finfo->fullname)); freevers_ts (&vers); diff_mark_errors (err); return (err); } else { if (vers->ts_user == NULL) { error (0, 0, "cannot find %s", fn_root(finfo->fullname)); freevers_ts (&vers); diff_mark_errors (err); return (err); } else if (!strcmp (vers->ts_user, vers->ts_rcs)) { /* The user file matches some revision in the repository Diff against the repository (for remote CVS, we might not have a copy of the user file around). */ user_file_rev = vers->vn_user; } } } empty_file = diff_file_nodiff (finfo, vers, empty_file, default_branch); if (empty_file == DIFF_SAME || empty_file == DIFF_ERROR) { freevers_ts (&vers); if (empty_file == DIFF_SAME) { /* In the server case, would be nice to send a "Checked-in" response, so that the client can rewrite its timestamp. server_checked_in by itself isn't the right thing (it needs a server_register), but I'm not sure what is. It isn't clear to me how "cvs status" handles this (that is, for a client which sends Modified not Is-modified to "cvs status"), but it does. */ return (0); } else { diff_mark_errors (err); return (err); } } if (empty_file == DIFF_DIFFERENT) { int dead1, dead2; if (use_rev1 == NULL) dead1 = 0; else dead1 = RCS_isdead (vers->srcfile, use_rev1); if (use_rev2 == NULL) dead2 = 0; else dead2 = RCS_isdead (vers->srcfile, use_rev2); if (dead1 && dead2) { freevers_ts (&vers); return (0); } else if (dead1) { if (empty_files) empty_file = DIFF_ADDED; else { error (0, 0, "%s is a new entry, no comparison available", fn_root(finfo->fullname)); freevers_ts (&vers); diff_mark_errors (err); return (err); } } else if (dead2) { if (empty_files) empty_file = DIFF_REMOVED; else { error (0, 0, "%s was removed, no comparison available", fn_root(finfo->fullname)); freevers_ts (&vers); diff_mark_errors (err); return (err); } } } /* Output an "Index:" line for patch to use */ if(!is_rcs) { cvs_output ("Index: ", 0); cvs_output (fn_root(finfo->fullname), 0); cvs_output ("\n", 1); } /* Set up file labels appropriate for compatibility with the Larry Wall * implementation of patch if the user didn't specify. This is irrelevant * according to the POSIX.2 specification. */ label1 = NULL; label2 = NULL; if (!have_rev1_label) { if (empty_file == DIFF_ADDED) { TRACE(3,"diff_fileproc(); !have_rev1_label; empty_file == DIFF_ADDED make_file_label(is_rcs=%d)",is_rcs); label1 = make_file_label (DEVNULL, NULL, NULL, is_rcs); } else { TRACE(3,"diff_fileproc(); !have_rev1_label; empty_file != DIFF_ADDED make_file_label(is_rcs=%d)",is_rcs); label1 = make_file_label (is_rcs?fn_root(finfo->file):fn_root(finfo->fullname), use_rev1, vers ? vers->srcfile : NULL, is_rcs); } } if (!have_rev2_label) { if (empty_file == DIFF_REMOVED) { TRACE(3,"diff_fileproc(); !have_rev2_label; empty_file == DIFF_REMOVED make_file_label(is_rcs=%d)",is_rcs); label2 = make_file_label (DEVNULL, NULL, NULL, is_rcs); } else { TRACE(3,"diff_fileproc(); !have_rev2_label; empty_file != DIFF_REMOVED make_file_label(is_rcs=%d)",is_rcs); label2 = make_file_label (is_rcs?fn_root(finfo->file):fn_root(finfo->fullname), use_rev2, vers ? vers->srcfile : NULL, is_rcs); } } if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED) { /* This is fullname, not file, possibly despite the POSIX.2 * specification, because that's the way all the Larry Wall * implementations of patch (are there other implementations?) want * things and the POSIX.2 spec appears to leave room for this. */ cvs_output ("\ ===================================================================\n\ RCS file: ", 0); cvs_output (fn_root(finfo->fullname), 0); cvs_output ("\n", 1); cvs_output ("diff -N ", 0); cvs_output (fn_root(finfo->fullname), 0); cvs_output ("\n", 1); if (empty_file == DIFF_ADDED) { if (use_rev2 == NULL) status = diff_exec (DEVNULL, finfo->file, label1, label2, opts, RUN_TTY); else { int retcode; tmp = cvs_temp_name (); retcode = RCS_checkout (vers->srcfile, (char *) NULL, use_rev2, (char *) NULL, vers->options, tmp, (RCSCHECKOUTPROC) NULL, (void *) NULL, NULL); if (retcode != 0) { diff_mark_errors (err); return err; } status = diff_exec (DEVNULL, tmp, label1, label2, opts, RUN_TTY); } } else { int retcode; tmp = cvs_temp_name (); retcode = RCS_checkout (vers->srcfile, (char *) NULL, use_rev1, (char *) NULL, *options ? options : vers->options, tmp, (RCSCHECKOUTPROC) NULL, (void *) NULL, NULL); if (retcode != 0) { diff_mark_errors (err); return err; } status = diff_exec (tmp, DEVNULL, label1, label2, opts, RUN_TTY); } }
/* ARGSUSED */ static int ls_fileproc (void *callerdat, struct file_info *finfo) { Vers_TS *vers; char *regex_err; Node *p, *n; bool isdead; const char *filename; if (regexp_match) { #ifdef FILENAMES_CASE_INSENSITIVE re_set_syntax (REG_ICASE|RE_SYNTAX_EGREP); #else re_set_syntax (RE_SYNTAX_EGREP); #endif if ((regex_err = re_comp (regexp_match)) != NULL) { error (1, 0, "bad regular expression passed to 'ls': %s", regex_err); } if (re_exec (finfo->file) == 0) return 0; /* no match */ } vers = Version_TS (finfo, NULL, show_tag, show_date, 1, 0); /* Skip dead revisions unless specifically requested to do otherwise. * We also bother to check for long_format so we can print the state. */ if (vers->vn_rcs && (!show_dead_revs || long_format)) isdead = RCS_isdead (finfo->rcs, vers->vn_rcs); else isdead = false; if (!vers->vn_rcs || (!show_dead_revs && isdead)) { freevers_ts (&vers); return 0; } p = findnode (callerdat, finfo->update_dir); if (!p) { /* This only occurs when a complete path to a file is specified on the * command line. Put the file in the root list. */ filename = finfo->fullname; /* Add update_dir node. */ p = findnode (callerdat, "."); if (!p) { p = getnode (); p->key = xstrdup ("."); p->data = getlist (); p->delproc = ls_delproc; addnode (callerdat, p); } } else filename = finfo->file; n = getnode(); if (entries_format) { char *outdate = entries_time (RCS_getrevtime (finfo->rcs, vers->vn_rcs, 0, 0)); n->data = Xasprintf ("/%s/%s/%s/%s/%s%s\n", filename, vers->vn_rcs, outdate, vers->options, show_tag ? "T" : "", show_tag ? show_tag : ""); free (outdate); } else if (long_format) { struct long_format_data *out = xmalloc (sizeof (struct long_format_data)); out->header = Xasprintf ("%-5.5s", vers->options[0] != '\0' ? vers->options : "----"); /* FIXME: Do we want to mimc the real `ls' command's date format? */ out->time = gmformat_time_t (RCS_getrevtime (finfo->rcs, vers->vn_rcs, 0, 0)); out->footer = Xasprintf (" %-9.9s%s %s%s", vers->vn_rcs, strlen (vers->vn_rcs) > 9 ? "+" : " ", show_dead_revs ? (isdead ? "dead " : " ") : "", filename); n->data = out; n->delproc = long_format_data_delproc; } else n->data = Xasprintf ("%s\n", filename); addnode (p->data, n); freevers_ts (&vers); return 0; }
/* ARGSUSED */ static int remove_fileproc (void *callerdat, struct file_info *finfo) { Vers_TS *vers; if (force) { if (!noexec) { if ( CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno)) { error (0, errno, "unable to remove %s", fn_root(finfo->fullname)); } } /* else FIXME should probably act as if the file doesn't exist in doing the following checks. */ } vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0, 0); if (vers->ts_user && (!vers->vn_user || strcmp(vers->vn_user,"0"))) { existing_files++; if (!quiet) error (0, 0, "file `%s' still in working directory", fn_root(finfo->fullname)); } else if (vers->vn_user == NULL) { if (!quiet) error (0, 0, "nothing known about `%s'", fn_root(finfo->fullname)); } else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') { char *fname; /* * It's a file that has been added, but not commited yet. So, * remove the ,t file for it and scratch it from the * entries file. */ Scratch_Entry (finfo->entries, finfo->file); fname = (char*)xmalloc (strlen (finfo->file) + sizeof (CVSADM) + sizeof (CVSEXT_LOG) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); if (unlink_file (fname) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", CVSEXT_LOG); if (!quiet) error (0, 0, "removed `%s'", fn_root(finfo->fullname)); #ifdef SERVER_SUPPORT if (server_active) server_checked_in (finfo->file, finfo->update_dir, finfo->repository); #endif xfree (fname); } else if (vers->vn_user[0] == '-') { if (!quiet) error (0, 0, "file `%s' already scheduled for removal", fn_root(finfo->fullname)); } else if (vers->tag != NULL && ((isdigit ((unsigned char) *vers->tag)) || !RCS_isbranch(finfo->rcs, vers->tag))) { /* Commit will just give an error, and so there seems to be little reason to allow the remove. I mean, conflicts that arise out of parallel development are one thing, but conflicts that arise from sticky tags are quite another. */ error (0, 0, "\ cannot remove file `%s' which has a sticky tag of `%s'", fn_root(finfo->fullname), vers->tag); bad_files++; } else { char *fname; /* Re-register it with a negative version number. */ fname = (char*)xmalloc (strlen (vers->vn_user) + 5); (void) strcpy (fname, "-"); (void) strcat (fname, vers->vn_user); Register (finfo->entries, finfo->file, fname, vers->ts_rcs, vers->options, vers->tag, vers->date, vers->ts_conflict, NULL, NULL, vers->tt_rcs, vers->edit_revision, vers->edit_tag, vers->edit_bugid, NULL); if (!quiet) error (0, 0, "scheduling `%s' for removal", fn_root(finfo->fullname)); removed_files++; #ifdef SERVER_SUPPORT if (server_active) server_checked_in (finfo->file, finfo->update_dir, finfo->repository); #endif xfree (fname); } freevers_ts (&vers); return (0); }