/* * 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*/ }