int ls (int argc, char **argv) { int c; int err = 0; is_rls = strcmp (cvs_cmd_name, "rls") == 0; if (argc == -1) usage (ls_usage); entries_format = false; long_format = false; show_tag = NULL; show_date = NULL; tag_validated = false; recurse = false; ls_prune_dirs = false; show_dead_revs = false; getoptreset (); while ((c = getopt (argc, argv, #ifdef SERVER_SUPPORT server_active ? "qdelr:D:PR" : #endif /* SERVER_SUPPORT */ "delr:D:RP" )) != -1) { switch (c) { #ifdef SERVER_SUPPORT case 'q': if (server_active) { error (0, 0, "`%s ls -q' is deprecated. Please use the global `-q' option instead.", program_name); quiet = true; } else usage (ls_usage); break; #endif /* SERVER_SUPPORT */ case 'd': show_dead_revs = true; break; case 'e': entries_format = true; break; case 'l': long_format = true; break; case 'r': parse_tagdate (&show_tag, &show_date, optarg); break; case 'D': if (show_date) free (show_date); show_date = Make_Date (optarg); break; case 'P': ls_prune_dirs = true; break; case 'R': recurse = true; break; case '?': default: usage (ls_usage); break; } } argc -= optind; argv += optind; if (entries_format && long_format) { error (0, 0, "`-e' & `-l' are mutually exclusive."); usage (ls_usage); } wrap_setup (); #ifdef CLIENT_SUPPORT if (current_parsed_root->isremote) { /* We're the local client. Fire up the remote server. */ start_server (); ign_setup (); if (is_rls ? !(supported_request ("rlist") || supported_request ("ls")) : !supported_request ("list")) error (1, 0, "server does not support %s", cvs_cmd_name); if (quiet && !supported_request ("global-list-quiet")) send_arg ("-q"); if (entries_format) send_arg ("-e"); if (long_format) send_arg ("-l"); if (ls_prune_dirs) send_arg ("-P"); if (recurse) send_arg ("-R"); if (show_dead_revs) send_arg ("-d"); if (show_tag) option_with_arg ("-r", show_tag); if (show_date) client_senddate (show_date); send_arg ("--"); if (is_rls) { int i; for (i = 0; i < argc; i++) send_arg (argv[i]); if (supported_request ("rlist")) send_to_server ("rlist\012", 0); else /* For backwards compatibility with CVSNT... */ send_to_server ("ls\012", 0); } else { /* Setting this means, I think, that any empty directories created * by the server will be deleted by the client. Since any dirs * created at all by ls should remain empty, this should cause any * dirs created by the server for the ls command to be deleted. */ client_prune_dirs = 1; /* I explicitly decide not to send contents here. We *could* let * the user pull status information with this command, but why * don't they just use update or status? */ send_files (argc, argv, !recurse, 0, SEND_NO_CONTENTS); send_file_names (argc, argv, SEND_EXPAND_WILD); send_to_server ("list\012", 0); } err = get_responses_and_close (); return err; } #endif if (is_rls) { DBM *db; int i; db = open_module (); if (argc) { for (i = 0; i < argc; i++) { char *mod = xstrdup (argv[i]); char *p; for (p=strchr (mod,'\\'); p; p=strchr (p,'\\')) *p='/'; p = strrchr (mod,'/'); if (p && (strchr (p,'?') || strchr (p,'*'))) { *p='\0'; regexp_match = p+1; } else regexp_match = NULL; /* Frontends like to do 'ls -q /', so we support it explicitly. */ if (!strcmp (mod,"/")) { *mod='\0'; } err += do_module (db, mod, MISC, "Listing", ls_proc, NULL, 0, 0, 0, 0, NULL); free (mod); } } else { /* should be ".", but do_recursion() fails this: assert ( strstr ( repository, "/./" ) == NULL ); */ char *topmod = xstrdup (""); err += do_module (db, topmod, MISC, "Listing", ls_proc, NULL, 0, 0, 0, 0, NULL); free (topmod); } close_module (db); } else ls_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, 0, NULL, NULL); return err; }
int history (int argc, char **argv) { int i, c; const char *fname = NULL; List *flist; if (argc == -1) usage (history_usg); since_rev = xstrdup (""); since_tag = xstrdup (""); backto = xstrdup (""); rec_types = xstrdup (""); getoptreset (); while ((c = getopt (argc, argv, "+Tacelow?D:b:f:m:n:p:r:t:u:x:X:z:")) != -1) { switch (c) { case 'T': /* Tag list */ report_count++; tag_report++; break; case 'a': /* For all usernames */ all_users++; break; case 'c': report_count++; modified = 1; break; case 'e': report_count++; extract_all++; free (rec_types); rec_types = xstrdup (ALL_HISTORY_REC_TYPES); break; case 'l': /* Find Last file record */ last_entry = 1; break; case 'o': report_count++; v_checkout = 1; break; case 'w': /* Match Working Dir (CurDir) fields */ working = 1; break; case 'X': /* Undocumented debugging flag */ #ifdef DEBUG fname = optarg; #endif break; case 'D': /* Since specified date */ if (*since_rev || *since_tag || *backto) { error (0, 0, "date overriding rev/tag/backto"); *since_rev = *since_tag = *backto = '\0'; } since_date = Make_Date (optarg); break; case 'b': /* Since specified file/Repos */ if (since_date || *since_rev || *since_tag) { error (0, 0, "backto overriding date/rev/tag"); *since_rev = *since_tag = '\0'; if (since_date != NULL) free (since_date); since_date = NULL; } free (backto); backto = xstrdup (optarg); break; case 'f': /* For specified file */ save_file (NULL, optarg, NULL); break; case 'm': /* Full module report */ if (!module_report++) report_count++; /* fall through */ case 'n': /* Look for specified module */ save_module (optarg); break; case 'p': /* For specified directory */ save_file (optarg, NULL, NULL); break; case 'r': /* Since specified Tag/Rev */ if (since_date || *since_tag || *backto) { error (0, 0, "rev overriding date/tag/backto"); *since_tag = *backto = '\0'; if (since_date != NULL) free (since_date); since_date = NULL; } free (since_rev); since_rev = xstrdup (optarg); break; case 't': /* Since specified Tag/Rev */ if (since_date || *since_rev || *backto) { error (0, 0, "tag overriding date/marker/file/repos"); *since_rev = *backto = '\0'; if (since_date != NULL) free (since_date); since_date = NULL; } free (since_tag); since_tag = xstrdup (optarg); break; case 'u': /* For specified username */ save_user (optarg); break; case 'x': report_count++; extract++; { char *cp; for (cp = optarg; *cp; cp++) if (!strchr (ALL_HISTORY_REC_TYPES, *cp)) error (1, 0, "%c is not a valid report type", *cp); } free (rec_types); rec_types = xstrdup (optarg); break; case 'z': tz_local = (optarg[0] == 'l' || optarg[0] == 'L') && (optarg[1] == 't' || optarg[1] == 'T') && !optarg[2]; if (tz_local) tz_name = optarg; else { /* * Convert a known time with the given timezone to time_t. * Use the epoch + 23 hours, so timezones east of GMT work. */ struct timespec t; char *buf = Xasprintf ("1/1/1970 23:00 %s", optarg); if (get_date (&t, buf, NULL)) { /* * Convert to seconds east of GMT, removing the * 23-hour offset mentioned above. */ tz_seconds_east_of_GMT = (time_t)23 * 60 * 60 - t.tv_sec; tz_name = optarg; } else error (0, 0, "%s is not a known time zone", optarg); free (buf); } break; case '?': default: usage (history_usg); break; } } argc -= optind; argv += optind; for (i = 0; i < argc; i++) save_file (NULL, argv[i], NULL); /* ================ Now analyze the arguments a bit */ if (!report_count) v_checkout++; else if (report_count > 1) error (1, 0, "Only one report type allowed from: \"-Tcomxe\"."); #ifdef CLIENT_SUPPORT if (current_parsed_root->isremote) { struct file_list_str *f1; char **mod; /* We're the client side. Fire up the remote server. */ start_server (); ign_setup (); if (tag_report) send_arg ("-T"); if (all_users) send_arg ("-a"); if (modified) send_arg ("-c"); if (last_entry) send_arg ("-l"); if (v_checkout) send_arg ("-o"); if (working) send_arg ("-w"); if (fname) option_with_arg ("-X", fname); if (since_date) client_senddate (since_date); if (backto[0] != '\0') option_with_arg ("-b", backto); for (f1 = file_list; f1 < &file_list[file_count]; ++f1) { if (f1->l_file[0] == '*') option_with_arg ("-p", f1->l_file + 1); else option_with_arg ("-f", f1->l_file); } if (module_report) send_arg ("-m"); for (mod = mod_list; mod < &mod_list[mod_count]; ++mod) option_with_arg ("-n", *mod); if (*since_rev) option_with_arg ("-r", since_rev); if (*since_tag) option_with_arg ("-t", since_tag); for (mod = user_list; mod < &user_list[user_count]; ++mod) option_with_arg ("-u", *mod); if (extract_all) send_arg ("-e"); if (extract) option_with_arg ("-x", rec_types); option_with_arg ("-z", tz_name); send_to_server ("history\012", 0); return get_responses_and_close (); } #endif if (all_users) save_user (""); if (mod_list) expand_modules (); if (tag_report) { if (!strchr (rec_types, 'T')) { rec_types = xrealloc (rec_types, strlen (rec_types) + 5); (void) strcat (rec_types, "T"); } } else if (extract || extract_all) { if (user_list) user_sort++; } else if (modified) { free (rec_types); rec_types = xstrdup ("MAR"); /* * If the user has not specified a date oriented flag ("Since"), sort * by Repository/file before date. Default is "just" date. */ if (last_entry || (!since_date && !*since_rev && !*since_tag && !*backto)) { repos_sort++; file_sort++; /* * If we are not looking for last_modified and the user specified * one or more users to look at, sort by user before filename. */ if (!last_entry && user_list) user_sort++; } } else if (module_report) { free (rec_types); rec_types = xstrdup (last_entry ? "OMAR" : ALL_HISTORY_REC_TYPES); module_sort++; repos_sort++; file_sort++; working = 0; /* User's workdir doesn't count here */ } else /* Must be "checkout" or default */ { free (rec_types); rec_types = xstrdup ("OF"); /* See comments in "modified" above */ if (!last_entry && user_list) user_sort++; if (last_entry || (!since_date && !*since_rev && !*since_tag && !*backto)) file_sort++; } /* If no users were specified, use self (-a saves a universal ("") user) */ if (!user_list) save_user (getcaller ()); /* If we're looking back to a Tag value, must consider "Tag" records */ if (*since_tag && !strchr (rec_types, 'T')) { rec_types = xrealloc (rec_types, strlen (rec_types) + 5); (void) strcat (rec_types, "T"); } if (fname) { Node *p; flist = getlist (); p = getnode (); p->type = FILES; p->key = Xasprintf ("%s/%s/%s", current_parsed_root->directory, CVSROOTADM, fname); addnode (flist, p); } else { char *pat; if (config->HistorySearchPath) pat = config->HistorySearchPath; else pat = Xasprintf ("%s/%s/%s", current_parsed_root->directory, CVSROOTADM, CVSROOTADM_HISTORY); flist = find_files (NULL, pat); if (pat != config->HistorySearchPath) free (pat); } read_hrecs (flist); if (hrec_count > 0) qsort (hrec_head, hrec_count, sizeof (struct hrec), sort_order); report_hrecs (); if (since_date != NULL) free (since_date); free (since_rev); free (since_tag); free (backto); free (rec_types); return 0; }
/* * This is the recursive function that processes a module name. * It calls back the passed routine for each directory of a module * It runs the post checkout or post tag proc from the modules file */ int my_module (DBM *db, char *mname, enum mtype m_type, char *msg, CALLBACKPROC callback_proc, char *where, int shorten, int local_specified, int run_module_prog, int build_dirs, char *extra_arg, List *stack) { char *checkout_prog = NULL; char *export_prog = NULL; char *tag_prog = NULL; struct saved_cwd cwd; int cwd_saved = 0; char *line; int modargc; int xmodargc; char **modargv = NULL; char **xmodargv = NULL; /* Found entry from modules file, including options and such. */ char *value = NULL; char *mwhere = NULL; char *mfile = NULL; char *spec_opt = NULL; char *xvalue = NULL; int alias = 0; datum key, val; char *cp; int c, err = 0; int nonalias_opt = 0; #ifdef SERVER_SUPPORT int restore_server_dir = 0; char *server_dir_to_restore = NULL; #endif TRACE (TRACE_FUNCTION, "my_module (%s, %s, %s, %s)", mname ? mname : "(null)", msg ? msg : "(null)", where ? where : "NULL", extra_arg ? extra_arg : "NULL"); /* Don't process absolute directories. Anything else could be a security * problem. Before this check was put in place: * * $ cvs -d:fork:/cvsroot co /foo * cvs server: warning: cannot make directory CVS in /: Permission denied * cvs [server aborted]: cannot make directory /foo: Permission denied * $ */ if (ISABSOLUTE (mname)) error (1, 0, "Absolute module reference invalid: `%s'", mname); /* Similarly for directories that attempt to step above the root of the * repository. */ if (pathname_levels (mname) > 0) error (1, 0, "up-level in module reference (`..') invalid: `%s'.", mname); /* if this is a directory to ignore, add it to that list */ if (mname[0] == '!' && mname[1] != '\0') { ign_dir_add (mname+1); goto do_module_return; } /* strip extra stuff from the module name */ strip_trailing_slashes (mname); /* * Look up the module using the following scheme: * 1) look for mname as a module name * 2) look for mname as a directory * 3) look for mname as a file * 4) take mname up to the first slash and look it up as a module name * (this is for checking out only part of a module) */ /* look it up as a module name */ key.dptr = mname; key.dsize = strlen (key.dptr); if (db != NULL) val = dbm_fetch (db, key); else val.dptr = NULL; if (val.dptr != NULL) { /* copy and null terminate the value */ value = xmalloc (val.dsize + 1); memcpy (value, val.dptr, val.dsize); value[val.dsize] = '\0'; /* If the line ends in a comment, strip it off */ if ((cp = strchr (value, '#')) != NULL) *cp = '\0'; else cp = value + val.dsize; /* Always strip trailing spaces */ while (cp > value && isspace ((unsigned char) *--cp)) *cp = '\0'; mwhere = xstrdup (mname); goto found; } else { char *file; char *attic_file; char *acp; int is_found = 0; /* check to see if mname is a directory or file */ file = xmalloc (strlen (current_parsed_root->directory) + strlen (mname) + sizeof(RCSEXT) + 2); (void) sprintf (file, "%s/%s", current_parsed_root->directory, mname); attic_file = xmalloc (strlen (current_parsed_root->directory) + strlen (mname) + sizeof (CVSATTIC) + sizeof (RCSEXT) + 3); if ((acp = strrchr (mname, '/')) != NULL) { *acp = '\0'; (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory, mname, CVSATTIC, acp + 1, RCSEXT); *acp = '/'; } else (void) sprintf (attic_file, "%s/%s/%s%s", current_parsed_root->directory, CVSATTIC, mname, RCSEXT); if (isdir (file)) { modargv = xmalloc (sizeof (*modargv)); modargv[0] = xstrdup (mname); modargc = 1; is_found = 1; } else { (void) strcat (file, RCSEXT); if (isfile (file) || isfile (attic_file)) { /* if mname was a file, we have to split it into "dir file" */ if ((cp = strrchr (mname, '/')) != NULL && cp != mname) { modargv = xnmalloc (2, sizeof (*modargv)); modargv[0] = xmalloc (strlen (mname) + 2); strncpy (modargv[0], mname, cp - mname); modargv[0][cp - mname] = '\0'; modargv[1] = xstrdup (cp + 1); modargc = 2; } else { /* * the only '/' at the beginning or no '/' at all * means the file we are interested in is in CVSROOT * itself so the directory should be '.' */ if (cp == mname) { /* drop the leading / if specified */ modargv = xnmalloc (2, sizeof (*modargv)); modargv[0] = xstrdup ("."); modargv[1] = xstrdup (mname + 1); modargc = 2; } else { /* otherwise just copy it */ modargv = xnmalloc (2, sizeof (*modargv)); modargv[0] = xstrdup ("."); modargv[1] = xstrdup (mname); modargc = 2; } } is_found = 1; } } free (attic_file); free (file); if (is_found) { assert (value == NULL); /* OK, we have now set up modargv with the actual file/directory we want to work on. We duplicate a small amount of code here because the vast majority of the code after the "found" label does not pertain to the case where we found a file/directory rather than finding an entry in the modules file. */ if (save_cwd (&cwd)) error (1, errno, "Failed to save current directory."); cwd_saved = 1; err += callback_proc (modargc, modargv, where, mwhere, mfile, shorten, local_specified, mname, msg); free_names (&modargc, modargv); /* cd back to where we started. */ if (restore_cwd (&cwd)) error (1, errno, "Failed to restore current directory, `%s'.", cwd.name); free_cwd (&cwd); cwd_saved = 0; goto do_module_return; } } /* look up everything to the first / as a module */ if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL) { /* Make the slash the new end of the string temporarily */ *cp = '\0'; key.dptr = mname; key.dsize = strlen (key.dptr); /* do the lookup */ if (db != NULL) val = dbm_fetch (db, key); else val.dptr = NULL; /* if we found it, clean up the value and life is good */ if (val.dptr != NULL) { char *cp2; /* copy and null terminate the value */ value = xmalloc (val.dsize + 1); memcpy (value, val.dptr, val.dsize); value[val.dsize] = '\0'; /* If the line ends in a comment, strip it off */ if ((cp2 = strchr (value, '#')) != NULL) *cp2 = '\0'; else cp2 = value + val.dsize; /* Always strip trailing spaces */ while (cp2 > value && isspace ((unsigned char) *--cp2)) *cp2 = '\0'; /* mwhere gets just the module name */ mwhere = xstrdup (mname); mfile = cp + 1; assert (strlen (mfile)); /* put the / back in mname */ *cp = '/'; goto found; } /* put the / back in mname */ *cp = '/'; } /* if we got here, we couldn't find it using our search, so give up */ error (0, 0, "cannot find module `%s' - ignored", mname); err++; goto do_module_return; /* * At this point, we found what we were looking for in one * of the many different forms. */ found: /* remember where we start */ if (save_cwd (&cwd)) error (1, errno, "Failed to save current directory."); cwd_saved = 1; assert (value != NULL); /* search the value for the special delimiter and save for later */ if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL) { *cp = '\0'; /* null out the special char */ spec_opt = cp + 1; /* save the options for later */ /* strip whitespace if necessary */ while (cp > value && isspace ((unsigned char) *--cp)) *cp = '\0'; } /* don't do special options only part of a module was specified */ if (mfile != NULL) spec_opt = NULL; /* * value now contains one of the following: * 1) dir * 2) dir file * 3) the value from modules without any special args * [ args ] dir [file] [file] ... * or -a module [ module ] ... */ /* Put the value on a line with XXX prepended for getopt to eat */ line = Xasprintf ("XXX %s", value); /* turn the line into an argv[] array */ line2argv (&xmodargc, &xmodargv, line, " \t"); free (line); modargc = xmodargc; modargv = xmodargv; /* parse the args */ getoptreset (); while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1) { switch (c) { case 'a': alias = 1; break; case 'd': if (mwhere) free (mwhere); mwhere = xstrdup (optarg); nonalias_opt = 1; break; case 'l': local_specified = 1; nonalias_opt = 1; break; case 'o': if (checkout_prog) free (checkout_prog); checkout_prog = xstrdup (optarg); nonalias_opt = 1; break; case 'e': if (export_prog) free (export_prog); export_prog = xstrdup (optarg); nonalias_opt = 1; break; case 't': if (tag_prog) free (tag_prog); tag_prog = xstrdup (optarg); nonalias_opt = 1; break; case '?': error (0, 0, "modules file has invalid option for key %s value %s", key.dptr, value); err++; goto do_module_return; } } modargc -= optind; modargv += optind; if (modargc == 0 && spec_opt == NULL) { error (0, 0, "modules file missing directory for module %s", mname); ++err; goto do_module_return; } if (alias && nonalias_opt) { /* The documentation has never said it is valid to specify -a along with another option. And I believe that in the past CVS has ignored the options other than -a, more or less, in this situation. */ error (0, 0, "\ -a cannot be specified in the modules file along with other options"); ++err; goto do_module_return; }