2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
4 * Copyright (c) 2001, Tony Hoyle
5 * Copyright (c) 2004, Derek R. Price & Ximbiot <http://ximbiot.com>
7 * You may distribute under the terms of the GNU General Public License as
8 * specified in the README file that comes with the CVS source distribution.
10 * Query CVS/Entries from server
16 static int ls_proc (int argc
, char **argv
, char *xwhere
, char *mwhere
,
17 char *mfile
, int shorten
, int local
, char *mname
,
20 static const char *const ls_usage
[] =
22 "Usage: %s %s [-e | -l] [-RP] [-r rev] [-D date] [path...]\n",
23 "\t-d\tShow dead revisions (with tag when specified).\n",
24 "\t-e\tDisplay in CVS/Entries format.\n",
25 "\t-l\tDisplay all details.\n",
26 "\t-P\tPrune empty directories.\n",
27 "\t-R\tList recursively.\n",
28 "\t-r rev\tShow files with revision or tag.\n",
29 "\t-D date\tShow files from date.\n",
30 "(Specify the --help global option for a list of other help options)\n",
34 static bool entries_format
;
35 static bool long_format
;
36 static char *show_tag
;
37 static char *show_date
;
39 static char *created_dir
;
40 static bool tag_validated
;
42 static bool ls_prune_dirs
;
43 static char *regexp_match
;
45 static bool show_dead_revs
;
50 ls (int argc
, char **argv
)
55 is_rls
= strcmp (cvs_cmd_name
, "rls") == 0;
60 entries_format
= false;
64 tag_validated
= false;
66 ls_prune_dirs
= false;
67 show_dead_revs
= false;
71 while ((c
= getopt (argc
, argv
,
73 server_active
? "qdelr:D:PR" :
74 #endif /* SERVER_SUPPORT */
85 "`%s ls -q' is deprecated. Please use the global `-q' option instead.",
92 #endif /* SERVER_SUPPORT */
94 show_dead_revs
= true;
97 entries_format
= true;
103 parse_tagdate (&show_tag
, &show_date
, optarg
);
106 if (show_date
) free (show_date
);
107 show_date
= Make_Date (optarg
);
110 ls_prune_dirs
= true;
124 if (entries_format
&& long_format
)
126 error (0, 0, "`-e' & `-l' are mutually exclusive.");
132 #ifdef CLIENT_SUPPORT
133 if (current_parsed_root
->isremote
)
135 /* We're the local client. Fire up the remote server. */
140 if (is_rls
? !(supported_request ("rlist") || supported_request ("ls"))
141 : !supported_request ("list"))
142 error (1, 0, "server does not support %s", cvs_cmd_name
);
144 if (quiet
&& !supported_request ("global-list-quiet"))
157 option_with_arg ("-r", show_tag
);
159 client_senddate (show_date
);
166 for (i
= 0; i
< argc
; i
++)
168 if (supported_request ("rlist"))
169 send_to_server ("rlist\012", 0);
171 /* For backwards compatibility with CVSNT... */
172 send_to_server ("ls\012", 0);
176 /* Setting this means, I think, that any empty directories created
177 * by the server will be deleted by the client. Since any dirs
178 * created at all by ls should remain empty, this should cause any
179 * dirs created by the server for the ls command to be deleted.
181 client_prune_dirs
= 1;
183 /* I explicitly decide not to send contents here. We *could* let
184 * the user pull status information with this command, but why
185 * don't they just use update or status?
187 send_files (argc
, argv
, !recurse
, 0, SEND_NO_CONTENTS
);
188 send_file_names (argc
, argv
, SEND_EXPAND_WILD
);
189 send_to_server ("list\012", 0);
192 err
= get_responses_and_close ();
204 for (i
= 0; i
< argc
; i
++)
206 char *mod
= xstrdup (argv
[i
]);
209 for (p
=strchr (mod
,'\\'); p
; p
=strchr (p
,'\\'))
212 p
= strrchr (mod
,'/');
213 if (p
&& (strchr (p
,'?') || strchr (p
,'*')))
221 /* Frontends like to do 'ls -q /', so we support it explicitly.
223 if (!strcmp (mod
,"/"))
228 err
+= do_module (db
, mod
, MISC
, "Listing",
229 ls_proc
, NULL
, 0, 0, 0, 0, NULL
);
236 /* should be ".", but do_recursion()
237 fails this: assert ( strstr ( repository, "/./" ) == NULL ); */
238 char *topmod
= xstrdup ("");
239 err
+= do_module (db
, topmod
, MISC
, "Listing",
240 ls_proc
, NULL
, 0, 0, 0, 0, NULL
);
246 ls_proc (argc
+ 1, argv
- 1, NULL
, NULL
, NULL
, 0, 0, NULL
, NULL
);
253 struct long_format_data
261 ls_print (Node
*p
, void *closure
)
265 struct long_format_data
*data
= p
->data
;
266 cvs_output_tagged ("text", data
->header
);
268 cvs_output_tagged ("date", data
->time
);
270 cvs_output_tagged ("text", data
->footer
);
271 cvs_output_tagged ("newline", NULL
);
274 cvs_output (p
->data
, 0);
281 ls_print_dir (Node
*p
, void *closure
)
283 static bool printed
= false;
285 if (recurse
&& !(ls_prune_dirs
&& list_isempty (p
->data
)))
287 /* Keep track of whether we've printed. If we have, then put a blank
288 * line before directory headers, to separate the header from the
289 * listing of the previous directory.
292 cvs_output ("\n", 1);
296 if (!strcmp (p
->key
, ""))
299 cvs_output (p
->key
, 0);
300 cvs_output (":\n", 2);
302 walklist (p
->data
, ls_print
, NULL
);
309 * Delproc for a node containing a struct long_format_data as data.
312 long_format_data_delproc (Node
*n
)
314 struct long_format_data
*data
= (struct long_format_data
*)n
->data
;
315 if (data
->header
) free (data
->header
);
316 if (data
->time
) free (data
->time
);
317 if (data
->footer
) free (data
->footer
);
323 /* A delproc for a List Node containing a List *. */
327 dellist ((List
**)&p
->data
);
333 * Add a file to our list of data to print for a directory.
337 ls_fileproc (void *callerdat
, struct file_info
*finfo
)
343 const char *filename
;
347 #ifdef FILENAMES_CASE_INSENSITIVE
348 re_set_syntax (REG_ICASE
|RE_SYNTAX_EGREP
);
350 re_set_syntax (RE_SYNTAX_EGREP
);
352 if ((regex_err
= re_comp (regexp_match
)) != NULL
)
354 error (1, 0, "bad regular expression passed to 'ls': %s",
357 if (re_exec (finfo
->file
) == 0)
358 return 0; /* no match */
361 vers
= Version_TS (finfo
, NULL
, show_tag
, show_date
, 1, 0);
362 /* Skip dead revisions unless specifically requested to do otherwise.
363 * We also bother to check for long_format so we can print the state.
365 if (vers
->vn_rcs
&& (!show_dead_revs
|| long_format
))
366 isdead
= RCS_isdead (finfo
->rcs
, vers
->vn_rcs
);
369 if (!vers
->vn_rcs
|| (!show_dead_revs
&& isdead
))
375 p
= findnode (callerdat
, finfo
->update_dir
);
378 /* This only occurs when a complete path to a file is specified on the
379 * command line. Put the file in the root list.
381 filename
= finfo
->fullname
;
383 /* Add update_dir node. */
384 p
= findnode (callerdat
, ".");
388 p
->key
= xstrdup (".");
389 p
->data
= getlist ();
390 p
->delproc
= ls_delproc
;
391 addnode (callerdat
, p
);
395 filename
= finfo
->file
;
400 char *outdate
= entries_time (RCS_getrevtime (finfo
->rcs
, vers
->vn_rcs
,
402 n
->data
= Xasprintf ("/%s/%s/%s/%s/%s%s\n",
403 filename
, vers
->vn_rcs
,
404 outdate
, vers
->options
,
405 show_tag
? "T" : "", show_tag
? show_tag
: "");
408 else if (long_format
)
410 struct long_format_data
*out
=
411 xmalloc (sizeof (struct long_format_data
));
412 out
->header
= Xasprintf ("%-5.5s",
413 vers
->options
[0] != '\0' ? vers
->options
415 /* FIXME: Do we want to mimc the real `ls' command's date format? */
416 out
->time
= gmformat_time_t (RCS_getrevtime (finfo
->rcs
, vers
->vn_rcs
,
418 out
->footer
= Xasprintf (" %-9.9s%s %s%s", vers
->vn_rcs
,
419 strlen (vers
->vn_rcs
) > 9 ? "+" : " ",
420 show_dead_revs
? (isdead
? "dead " : " ")
424 n
->delproc
= long_format_data_delproc
;
427 n
->data
= Xasprintf ("%s\n", filename
);
429 addnode (p
->data
, n
);
438 * Add this directory to the list of data to be printed for a directory and
439 * decide whether to tell the recursion processor whether to continue
443 ls_direntproc (void *callerdat
, const char *dir
, const char *repos
,
444 const char *update_dir
, List
*entries
)
449 /* Due to the way we called start_recursion() from ls_proc() with a single
450 * argument at a time, we can assume that if we don't yet have a parent
451 * directory in DIRS then this directory should be processed.
454 if (strcmp (dir
, "."))
456 /* Search for our parent directory. */
458 parent
= xmalloc (strlen (update_dir
) - strlen (dir
) + 1);
459 strncpy (parent
, update_dir
, strlen (update_dir
) - strlen (dir
));
460 parent
[strlen (update_dir
) - strlen (dir
)] = '\0';
461 strip_trailing_slashes (parent
);
462 p
= findnode (callerdat
, parent
);
469 /* Push this dir onto our parent directory's listing. */
473 n
->data
= Xasprintf ("D/%s////\n", dir
);
474 else if (long_format
)
476 struct long_format_data
*out
=
477 xmalloc (sizeof (struct long_format_data
));
478 out
->header
= xstrdup ("d--- ");
479 out
->time
= gmformat_time_t (unix_time_stamp (repos
));
480 out
->footer
= Xasprintf ("%12s%s%s", "",
481 show_dead_revs
? " " : "", dir
);
483 n
->delproc
= long_format_data_delproc
;
486 n
->data
= Xasprintf ("%s\n", dir
);
488 addnode (p
->data
, n
);
493 /* Create a new list for this directory. */
495 p
->key
= xstrdup (strcmp (update_dir
, ".") ? update_dir
: "");
496 p
->data
= getlist ();
497 p
->delproc
= ls_delproc
;
498 addnode (callerdat
, p
);
500 /* Create a local directory and mark it as needing deletion. This is
501 * the behavior the recursion processor relies upon, a la update &
507 if (show_tag
== NULL
&& show_date
== NULL
)
509 ParseTag (&show_tag
, &show_date
, &nonbranch
);
514 created_dir
= xstrdup (update_dir
);
516 make_directory (dir
);
517 Create_Admin (dir
, update_dir
, repos
, show_tag
, show_date
,
519 Subdir_Register (entries
, NULL
, dir
);
522 /* Tell do_recursion to keep going. */
533 /* Clean up tags, dates, and dirs if we created this directory.
536 ls_dirleaveproc (void *callerdat
, const char *dir
, int err
,
537 const char *update_dir
, List
*entries
)
539 if (created_dir
&& !strcmp (created_dir
, update_dir
))
543 if (show_tag
) free (show_tag
);
544 if (show_date
) free (show_date
);
545 show_tag
= show_date
= NULL
;
549 (void)CVS_CHDIR ("..");
550 if (unlink_file_dir (dir
))
551 error (0, errno
, "Failed to remove directory `%s'",
553 Subdir_Deregister (entries
, NULL
, dir
);
564 ls_proc (int argc
, char **argv
, char *xwhere
, char *mwhere
, char *mfile
,
565 int shorten
, int local
, char *mname
, char *msg
)
578 error (0, 0, "Listing module: `%s'",
579 strcmp (mname
, "") ? mname
: ".");
581 repository
= xmalloc (strlen (current_parsed_root
->directory
)
583 + (mfile
== NULL
? 0 : strlen (mfile
) + 1)
585 (void)sprintf (repository
, "%s/%s", current_parsed_root
->directory
,
587 where
= xmalloc (strlen (argv
[0])
588 + (mfile
== NULL
? 0 : strlen (mfile
) + 1)
590 (void)strcpy (where
, argv
[0]);
592 /* If mfile isn't null, we need to set up to do only part of the
600 /* If the portion of the module is a path, put the dir part on
603 if ((cp
= strrchr (mfile
, '/')) != NULL
)
606 (void)strcat (repository
, "/");
607 (void)strcat (repository
, mfile
);
608 (void)strcat (where
, "/");
609 (void)strcat (where
, mfile
);
613 /* take care of the rest */
614 path
= Xasprintf ("%s/%s", repository
, mfile
);
617 /* directory means repository gets the dir tacked on */
618 (void)strcpy (repository
, path
);
619 (void)strcat (where
, "/");
620 (void)strcat (where
, mfile
);
631 /* cd to the starting repository */
632 if (CVS_CHDIR (repository
) < 0)
634 error (0, errno
, "cannot chdir to %s", repository
);
646 which
= W_LOCAL
| W_REPOS
;
649 if (show_tag
|| show_date
|| show_dead_revs
)
652 if (show_tag
!= NULL
&& !tag_validated
)
654 tag_check_valid (show_tag
, argc
- 1, argv
+ 1, local
, 0, repository
,
656 tag_validated
= true;
659 /* Loop on argc so that we are guaranteed that any directory passed to
660 * ls_direntproc should be processed if its parent is not yet in DIRS.
664 List
*dirs
= getlist ();
665 err
= start_recursion (ls_fileproc
, NULL
, ls_direntproc
,
666 ls_dirleaveproc
, dirs
, 0, NULL
, local
, which
, 0,
667 CVS_LOCK_READ
, where
, 1, repository
);
668 walklist (dirs
, ls_print_dir
, NULL
);
673 for (i
= 1; i
< argc
; i
++)
675 List
*dirs
= getlist ();
676 err
= start_recursion (ls_fileproc
, NULL
, ls_direntproc
,
677 NULL
, dirs
, 1, argv
+ i
, local
, which
, 0,
678 CVS_LOCK_READ
, where
, 1, repository
);
679 walklist (dirs
, ls_print_dir
, NULL
);
684 if (!(which
& W_LOCAL
)) free (repository
);
685 if (where
) free (where
);