AMD64 - Fix format conversions and other warnings.
[dragonfly.git] / contrib / cvs-1.12 / src / ls.c
blob0184bdd1dfd92f42c9e395cb3eaeed5c1e5665a6
1 /*
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
13 #include "cvs.h"
14 #include <stdbool.h>
16 static int ls_proc (int argc, char **argv, char *xwhere, char *mwhere,
17 char *mfile, int shorten, int local, char *mname,
18 char *msg);
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",
31 NULL
34 static bool entries_format;
35 static bool long_format;
36 static char *show_tag;
37 static char *show_date;
38 static bool set_tag;
39 static char *created_dir;
40 static bool tag_validated;
41 static bool recurse;
42 static bool ls_prune_dirs;
43 static char *regexp_match;
44 static bool is_rls;
45 static bool show_dead_revs;
49 int
50 ls (int argc, char **argv)
52 int c;
53 int err = 0;
55 is_rls = strcmp (cvs_cmd_name, "rls") == 0;
57 if (argc == -1)
58 usage (ls_usage);
60 entries_format = false;
61 long_format = false;
62 show_tag = NULL;
63 show_date = NULL;
64 tag_validated = false;
65 recurse = false;
66 ls_prune_dirs = false;
67 show_dead_revs = false;
69 optind = 0;
71 while ((c = getopt (argc, argv,
72 #ifdef SERVER_SUPPORT
73 server_active ? "qdelr:D:PR" :
74 #endif /* SERVER_SUPPORT */
75 "delr:D:RP"
76 )) != -1)
78 switch (c)
80 #ifdef SERVER_SUPPORT
81 case 'q':
82 if (server_active)
84 error (0, 0,
85 "`%s ls -q' is deprecated. Please use the global `-q' option instead.",
86 program_name);
87 quiet = true;
89 else
90 usage (ls_usage);
91 break;
92 #endif /* SERVER_SUPPORT */
93 case 'd':
94 show_dead_revs = true;
95 break;
96 case 'e':
97 entries_format = true;
98 break;
99 case 'l':
100 long_format = true;
101 break;
102 case 'r':
103 parse_tagdate (&show_tag, &show_date, optarg);
104 break;
105 case 'D':
106 if (show_date) free (show_date);
107 show_date = Make_Date (optarg);
108 break;
109 case 'P':
110 ls_prune_dirs = true;
111 break;
112 case 'R':
113 recurse = true;
114 break;
115 case '?':
116 default:
117 usage (ls_usage);
118 break;
121 argc -= optind;
122 argv += optind;
124 if (entries_format && long_format)
126 error (0, 0, "`-e' & `-l' are mutually exclusive.");
127 usage (ls_usage);
130 wrap_setup ();
132 #ifdef CLIENT_SUPPORT
133 if (current_parsed_root->isremote)
135 /* We're the local client. Fire up the remote server. */
136 start_server ();
138 ign_setup ();
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"))
145 send_arg ("-q");
146 if (entries_format)
147 send_arg ("-e");
148 if (long_format)
149 send_arg ("-l");
150 if (ls_prune_dirs)
151 send_arg ("-P");
152 if (recurse)
153 send_arg ("-R");
154 if (show_dead_revs)
155 send_arg ("-d");
156 if (show_tag)
157 option_with_arg ("-r", show_tag);
158 if (show_date)
159 client_senddate (show_date);
161 send_arg ("--");
163 if (is_rls)
165 int i;
166 for (i = 0; i < argc; i++)
167 send_arg (argv[i]);
168 if (supported_request ("rlist"))
169 send_to_server ("rlist\012", 0);
170 else
171 /* For backwards compatibility with CVSNT... */
172 send_to_server ("ls\012", 0);
174 else
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 ();
193 return err;
195 #endif
197 if (is_rls)
199 DBM *db;
200 int i;
201 db = open_module ();
202 if (argc)
204 for (i = 0; i < argc; i++)
206 char *mod = xstrdup (argv[i]);
207 char *p;
209 for (p=strchr (mod,'\\'); p; p=strchr (p,'\\'))
210 *p='/';
212 p = strrchr (mod,'/');
213 if (p && (strchr (p,'?') || strchr (p,'*')))
215 *p='\0';
216 regexp_match = p+1;
218 else
219 regexp_match = NULL;
221 /* Frontends like to do 'ls -q /', so we support it explicitly.
223 if (!strcmp (mod,"/"))
225 *mod='\0';
228 err += do_module (db, mod, MISC, "Listing",
229 ls_proc, NULL, 0, 0, 0, 0, NULL);
231 free (mod);
234 else
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);
241 free (topmod);
243 close_module (db);
245 else
246 ls_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, 0, NULL, NULL);
248 return err;
253 struct long_format_data
255 char *header;
256 char *time;
257 char *footer;
260 static int
261 ls_print (Node *p, void *closure)
263 if (long_format)
265 struct long_format_data *data = p->data;
266 cvs_output_tagged ("text", data->header);
267 if (data->time)
268 cvs_output_tagged ("date", data->time);
269 if (data->footer)
270 cvs_output_tagged ("text", data->footer);
271 cvs_output_tagged ("newline", NULL);
273 else
274 cvs_output (p->data, 0);
275 return 0;
280 static int
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.
291 if (printed)
292 cvs_output ("\n", 1);
293 else
294 printed = true;
296 if (!strcmp (p->key, ""))
297 cvs_output (".", 1);
298 else
299 cvs_output (p->key, 0);
300 cvs_output (":\n", 2);
302 walklist (p->data, ls_print, NULL);
303 return 0;
309 * Delproc for a node containing a struct long_format_data as data.
311 static void
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);
318 free (data);
323 /* A delproc for a List Node containing a List *. */
324 static void
325 ls_delproc (Node *p)
327 dellist ((List **)&p->data);
333 * Add a file to our list of data to print for a directory.
335 /* ARGSUSED */
336 static int
337 ls_fileproc (void *callerdat, struct file_info *finfo)
339 Vers_TS *vers;
340 char *regex_err;
341 Node *p, *n;
342 bool isdead;
343 const char *filename;
345 if (regexp_match)
347 #ifdef FILENAMES_CASE_INSENSITIVE
348 re_set_syntax (REG_ICASE|RE_SYNTAX_EGREP);
349 #else
350 re_set_syntax (RE_SYNTAX_EGREP);
351 #endif
352 if ((regex_err = re_comp (regexp_match)) != NULL)
354 error (1, 0, "bad regular expression passed to 'ls': %s",
355 regex_err);
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);
367 else
368 isdead = false;
369 if (!vers->vn_rcs || (!show_dead_revs && isdead))
371 freevers_ts (&vers);
372 return 0;
375 p = findnode (callerdat, finfo->update_dir);
376 if (!p)
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, ".");
385 if (!p)
387 p = getnode ();
388 p->key = xstrdup (".");
389 p->data = getlist ();
390 p->delproc = ls_delproc;
391 addnode (callerdat, p);
394 else
395 filename = finfo->file;
397 n = getnode();
398 if (entries_format)
400 char *outdate = entries_time (RCS_getrevtime (finfo->rcs, vers->vn_rcs,
401 0, 0));
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 : "");
406 free (outdate);
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
414 : "----");
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,
417 0, 0));
418 out->footer = Xasprintf (" %-9.9s%s %s%s", vers->vn_rcs,
419 strlen (vers->vn_rcs) > 9 ? "+" : " ",
420 show_dead_revs ? (isdead ? "dead " : " ")
421 : "",
422 filename);
423 n->data = out;
424 n->delproc = long_format_data_delproc;
426 else
427 n->data = Xasprintf ("%s\n", filename);
429 addnode (p->data, n);
431 freevers_ts (&vers);
432 return 0;
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
440 * recursing or not.
442 static Dtype
443 ls_direntproc (void *callerdat, const char *dir, const char *repos,
444 const char *update_dir, List *entries)
446 Dtype retval;
447 Node *p;
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. */
457 char *parent;
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);
464 else
465 p = NULL;
467 if (p)
469 /* Push this dir onto our parent directory's listing. */
470 Node *n = getnode();
472 if (entries_format)
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);
482 n->data = out;
483 n->delproc = long_format_data_delproc;
485 else
486 n->data = Xasprintf ("%s\n", dir);
488 addnode (p->data, n);
491 if (!p || recurse)
493 /* Create a new list for this directory. */
494 p = getnode ();
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 &
502 * checkout.
504 if (!isdir (dir))
506 int nonbranch;
507 if (show_tag == NULL && show_date == NULL)
509 ParseTag (&show_tag, &show_date, &nonbranch);
510 set_tag = true;
513 if (!created_dir)
514 created_dir = xstrdup (update_dir);
516 make_directory (dir);
517 Create_Admin (dir, update_dir, repos, show_tag, show_date,
518 nonbranch, 0, 0);
519 Subdir_Register (entries, NULL, dir);
522 /* Tell do_recursion to keep going. */
523 retval = R_PROCESS;
525 else
526 retval = R_SKIP_ALL;
528 return retval;
533 /* Clean up tags, dates, and dirs if we created this directory.
535 static int
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))
541 if (set_tag)
543 if (show_tag) free (show_tag);
544 if (show_date) free (show_date);
545 show_tag = show_date = NULL;
546 set_tag = false;
549 (void)CVS_CHDIR ("..");
550 if (unlink_file_dir (dir))
551 error (0, errno, "Failed to remove directory `%s'",
552 created_dir);
553 Subdir_Deregister (entries, NULL, dir);
555 free (created_dir);
556 created_dir = NULL;
558 return err;
563 static int
564 ls_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
565 int shorten, int local, char *mname, char *msg)
567 char *repository;
568 int err = 0;
569 int which;
570 char *where;
571 int i;
573 if (is_rls)
575 char *myargv[2];
577 if (!quiet)
578 error (0, 0, "Listing module: `%s'",
579 strcmp (mname, "") ? mname : ".");
581 repository = xmalloc (strlen (current_parsed_root->directory)
582 + strlen (argv[0])
583 + (mfile == NULL ? 0 : strlen (mfile) + 1)
584 + 2);
585 (void)sprintf (repository, "%s/%s", current_parsed_root->directory,
586 argv[0]);
587 where = xmalloc (strlen (argv[0])
588 + (mfile == NULL ? 0 : strlen (mfile) + 1)
589 + 1);
590 (void)strcpy (where, argv[0]);
592 /* If mfile isn't null, we need to set up to do only part of the
593 * module.
595 if (mfile != NULL)
597 char *cp;
598 char *path;
600 /* If the portion of the module is a path, put the dir part on
601 * repos.
603 if ((cp = strrchr (mfile, '/')) != NULL)
605 *cp = '\0';
606 (void)strcat (repository, "/");
607 (void)strcat (repository, mfile);
608 (void)strcat (where, "/");
609 (void)strcat (where, mfile);
610 mfile = cp + 1;
613 /* take care of the rest */
614 path = Xasprintf ("%s/%s", repository, mfile);
615 if (isdir (path))
617 /* directory means repository gets the dir tacked on */
618 (void)strcpy (repository, path);
619 (void)strcat (where, "/");
620 (void)strcat (where, mfile);
622 else
624 myargv[1] = mfile;
625 argc = 2;
626 argv = myargv;
628 free (path);
631 /* cd to the starting repository */
632 if (CVS_CHDIR (repository) < 0)
634 error (0, errno, "cannot chdir to %s", repository);
635 free (repository);
636 free (where);
637 return 1;
640 which = W_REPOS;
642 else /* !is_rls */
644 repository = NULL;
645 where = NULL;
646 which = W_LOCAL | W_REPOS;
649 if (show_tag || show_date || show_dead_revs)
650 which |= W_ATTIC;
652 if (show_tag != NULL && !tag_validated)
654 tag_check_valid (show_tag, argc - 1, argv + 1, local, 0, repository,
655 false);
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.
662 if (argc == 1)
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);
669 dellist (&dirs);
671 else
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);
680 dellist (&dirs);
684 if (!(which & W_LOCAL)) free (repository);
685 if (where) free (where);
687 return err;