I started on a patch to add a list option to cvs. It came about because
I wanted to run things like bonsai and cvsweb on remote repositories. I
guess you could say "I scratched an itch", as Eric S. Raymond has put
it.
It is based on patch.c, which I belatedly discovered might not have been
the best idea, as it uses the do_module function which refers to trigger
scripts in the comments. It currently lists all the files, as expected,
but I am not sure how to get it to list all the directories. If someone
could help with this is would be appreciated.
The are XX options available:
-r to get a list of files based on tag/branch
-D to get a list of files based on date
-l to turn off recursion
-R to force recursion
-s to not show state, author, version, and date for each file
-a to list all files, even if they have state == dead
-b to not show directory name for each file
-? to get help
The default output has state, version, author, date, directory, and
filename listed for each file in the module. The author field has a
width of 35, which is fairly large, but is the longest length username
on mozilla.org's CVS repository.
I have not implemented any functionality to list the top level
directories, but rather require the user to specify the module or
directory to list. I chose to do this because some repository
administrators use the fact that you can't easily get a list of
modules/top level directories as a feature.
I would appreciate any assistance with this option, and would hope that
this will eventually make it into a release of cvs.
Thanks.
Peter Bowen
Index: cvs.h
===================================================================
RCS file: /home2/cvsroot/ccvs/src/cvs.h,v
retrieving revision 1.206
diff -u -r1.206 cvs.h
--- cvs.h 2000/10/12 15:33:21 1.206
+++ cvs.h 2000/10/16 21:40:41
@@ -314,7 +314,7 @@
/* The type of request that is being done in do_module() */
enum mtype
{
- CHECKOUT, TAG, PATCH, EXPORT
+ CHECKOUT, TAG, PATCH, EXPORT, CVSLIST
};
/*
@@ -856,6 +856,7 @@
extern int cvsstatus PROTO((int argc, char **argv));
extern int cvstag PROTO((int argc, char **argv));
extern int version PROTO((int argc, char **argv));
+extern int cvslist PROTO((int argc, char **argv));
extern unsigned long int lookup_command_attribute PROTO((char *));
Index: main.c
===================================================================
RCS file: /home2/cvsroot/ccvs/src/main.c,v
retrieving revision 1.154
diff -u -r1.154 main.c
--- main.c 2000/10/11 16:17:53 1.154
+++ main.c 2000/10/16 21:40:41
@@ -114,6 +114,7 @@
#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
{ "kserver", NULL, NULL, server }, /* placeholder */
#endif
+ { "list", "ls", NULL, cvslist },
{ "log", "lo", "rlog", cvslog },
#ifdef AUTH_CLIENT_SUPPORT
{ "login", "logon", "lgn", login },
@@ -329,6 +330,7 @@
something doesn't use the WD. */
if ((strcmp (cmd_name, "checkout") != 0) &&
(strcmp (cmd_name, "init") != 0) &&
+ (strcmp (cmd_name, "list") != 0) &&
(strcmp (cmd_name, "login") != 0) &&
(strcmp (cmd_name, "logout") != 0) &&
(strcmp (cmd_name, "rdiff") != 0) &&
@@ -350,6 +352,7 @@
(strcmp (cmd_name, "editors") != 0) &&
(strcmp (cmd_name, "export") != 0) &&
(strcmp (cmd_name, "history") != 0) &&
+ (strcmp (cmd_name, "list") != 0) &&
(strcmp (cmd_name, "log") != 0) &&
(strcmp (cmd_name, "noop") != 0) &&
(strcmp (cmd_name, "watchers") != 0) &&
/*
* Copyright (c) 1992, Brian Berliner and Jeff Polk
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS source distribution.
*
* List
*
* Print a list of file under version control
*/
#include "cvs.h"
#include "getline.h"
static RETSIGTYPE list_cleanup PROTO((void));
static Dtype list_dirproc PROTO ((void *callerdat, char *dir,
char *repos, char *update_dir,
List *entries));
static int list_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int list_proc PROTO((int argc, char **argv, char *xwhere,
char *mwhere, char *mfile, int shorten,
int local_specified, char *mname, char *msg));
static int force_tag_match = 1;
static int list_short = 0;
static int list_all = 0;
static int list_bare = 0;
static int local = 0;
static char *options = NULL;
static char *rev = NULL;
static int rev_validated = 0;
static char *date = NULL;
static const char *const list_usage[] =
{
"Usage: %s %s [-flR] [-s|-t] \n",
" [-r rev | -D date] modules...\n",
"\t-f\tForce a head revision match if tag/date not found.\n",
"\t-l\tLocal directory only, not recursive\n",
"\t-R\tProcess directories recursively.\n",
"\t-s\tShort list - only list filename.\n",
"\t-a\tAll files - list all files, including dead ones.\n",
"\t-b\tBare - don't show directory name with each file.\n",
"\t-D date\tDate.\n",
"\t-r rev\tRevision - symbolic or numeric.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
int
cvslist (argc, argv)
int argc;
char **argv;
{
register int i;
int c;
int err = 0;
DBM *db;
if (argc == -1)
usage (list_usage);
optind = 0;
while ((c = getopt (argc, argv, "+V:k:cuibftsaQqlRD:r:")) != -1)
{
switch (c)
{
case 'Q':
case 'q':
#ifdef SERVER_SUPPORT
/* The CVS 1.5 client sends these options (in addition to
Global_option requests), so we must ignore them. */
if (!server_active)
#endif
error (1, 0,
"-q or -Q must be specified before \"%s\"",
command_name);
break;
case 'f':
force_tag_match = 0;
break;
case 'l':
local = 1;
break;
case 'R':
local = 0;
break;
case 's':
list_short = 1;
break;
case 'a':
list_all = 1;
break;
case 'b':
list_bare = 1;
break;
case 'D':
date = Make_Date (optarg);
break;
case 'r':
rev = optarg;
break;
case '?':
default:
usage (list_usage);
break;
}
}
argc -= optind;
argv += optind;
/* Sanity checks */
if (argc < 1)
usage (list_usage);
/* if options is NULL, make it a NULL string */
if (options == NULL)
options = xstrdup ("");
#ifdef CLIENT_SUPPORT
if (client_active)
{
/* We're the client side. Fire up the remote server. */
start_server ();
ign_setup ();
if (local)
send_arg("-l");
if (!force_tag_match)
send_arg("-f");
if (list_short)
send_arg("-s");
if (list_all)
send_arg("-a");
if (list_bare)
send_arg("-b");
if (rev)
option_with_arg ("-r", rev);
if (date)
client_senddate (date);
if (options[0] != '\0')
send_arg (options);
{
int i;
for (i = 0; i < argc; ++i)
send_arg (argv[i]);
}
send_to_server ("list\012", 0);
return get_responses_and_close ();
}
#endif
/* turn this off, as we nolonger have tempfiles */
#if 0
/* clean up if we get a signal */
#ifdef SIGABRT
(void) SIG_register (SIGABRT, list_cleanup);
#endif
#ifdef SIGHUP
(void) SIG_register (SIGHUP, list_cleanup);
#endif
#ifdef SIGINT
(void) SIG_register (SIGINT, list_cleanup);
#endif
#ifdef SIGQUIT
(void) SIG_register (SIGQUIT, list_cleanup);
#endif
#ifdef SIGPIPE
(void) SIG_register (SIGPIPE, list_cleanup);
#endif
#ifdef SIGTERM
(void) SIG_register (SIGTERM, list_cleanup);
#endif
#endif
db = open_module ();
for (i = 0; i < argc; i++)
err += do_module (db, argv[i], CVSLIST, "Listing", list_proc,
(char *) NULL, 0, 0, 0, (char *) NULL);
close_module (db);
free (options);
list_cleanup ();
return (err);
}
/*
* callback proc for doing the real work of listing
*/
/* ARGSUSED */
static int
list_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
mname, msg)
int argc;
char **argv;
char *xwhere;
char *mwhere;
char *mfile;
int shorten;
int local_specified;
char *mname;
char *msg;
{
char *myargv[2];
int err = 0;
int which;
char *repository;
char *where;
repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0])
+ (mfile == NULL ? 0 : strlen (mfile)) + 30);
(void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]);
where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile))
+ 10);
(void) strcpy (where, argv[0]);
/* if mfile isn't null, we need to set up to do only part of the module */
if (mfile != NULL)
{
char *cp;
char *path;
/* if the portion of the module is a path, put the dir part on repos */
if ((cp = strrchr (mfile, '/')) != NULL)
{
*cp = '\0';
(void) strcat (repository, "/");
(void) strcat (repository, mfile);
(void) strcat (where, "/");
(void) strcat (where, mfile);
mfile = cp + 1;
}
/* take care of the rest */
path = xmalloc (strlen (repository) + strlen (mfile) + 5);
(void) sprintf (path, "%s/%s", repository, mfile);
if (isdir (path))
{
/* directory means repository gets the dir tacked on */
(void) strcpy (repository, path);
(void) strcat (where, "/");
(void) strcat (where, mfile);
}
else
{
myargv[0] = argv[0];
myargv[1] = mfile;
argc = 2;
argv = myargv;
}
free (path);
}
/* cd to the starting repository */
if ( CVS_CHDIR (repository) < 0)
{
error (0, errno, "cannot chdir to %s", repository);
free (repository);
return (1);
}
free (repository);
if (1)
which = W_REPOS | W_ATTIC;
else
which = W_REPOS;
if (rev != NULL && !rev_validated)
{
tag_check_valid (rev, argc - 1, argv + 1, local, 0, NULL);
rev_validated = 1;
}
/* start the recursion processor */
err = start_recursion (list_fileproc, (FILESDONEPROC) NULL, list_dirproc,
(DIRLEAVEPROC) NULL, NULL,
argc - 1, argv + 1, local,
which, 0, 1, where, 1);
free (where);
return (err);
}
/*
* Called to examine a particular RCS file, as appropriate with the options
* that were set above.
*/
/* ARGSUSED */
static int
list_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
struct utimbuf t;
char *vers_tag, *vers_head;
char *rcs = NULL;
RCSNode *rcsfile;
FILE *fp1, *fp2, *fp3;
int ret = 0;
int isattic = 0;
int retcode = 0;
char *file1;
char *file2;
char *strippath;
char *line1, *line2;
size_t line1_chars_allocated;
size_t line2_chars_allocated;
char *cp1, *cp2;
FILE *fp;
int line_length;
line1 = NULL;
line1_chars_allocated = 0;
line2 = NULL;
line2_chars_allocated = 0;
/* find the parsed rcs file */
if ((rcsfile = finfo->rcs) == NULL)
{
ret = 1;
goto out2;
}
if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
isattic = 1;
rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5);
(void) sprintf (rcs, "%s%s", finfo->file, RCSEXT);
vers_tag = RCS_getversion (rcsfile, rev, date, force_tag_match,
(int *) NULL);
if (list_all == 0 && vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
{
free (vers_tag);
vers_tag = NULL;
}
if (vers_tag == NULL)
{
/* Nothing known about specified revs. */
ret = 0;
goto out2;
}
if (!list_short) {
Node *p;
RCSVers *vers;
char buf[100];
int year, mon, mday, hour, min, sec;
p = findnode (rcsfile->versions, vers_tag);
vers = (RCSVers *) p->data;
(void) sscanf (vers->date, SDATEFORM, &year, &mon, &mday, &hour, &min, &sec);
if (year < 1900)
year += 1900;
sprintf(buf, "%-10s %-15s %-35s %04d/%02d/%02d %02d:%02d:%02d ",
vers->state, vers->version, vers->author, year, mon,
mday, hour, min, sec);
cvs_output(buf, 0);
}
if (list_bare) {
cvs_output(finfo->file, 0);
} else {
cvs_output(finfo->fullname, 0);
}
cvs_output("\n", 1);
ret=0;
out2:
if (vers_tag != NULL)
free (vers_tag);
if (rcs != NULL)
free (rcs);
return (ret);
}
/*
* Print a warm fuzzy message
*/
/* ARGSUSED */
static Dtype
list_dirproc (callerdat, dir, repos, update_dir, entries)
void *callerdat;
char *dir;
char *repos;
char *update_dir;
List *entries;
{
if (!quiet)
error (0, 0, "Listing %s", update_dir);
return (R_PROCESS);
}
static RETSIGTYPE
list_cleanup ()
{
/* Placeholder function*/
}