* 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 1.3 kit.
* Functions for accessing the modules file.
* The modules file supports basically three formats of lines:
* key [options] directory files... [ -x directory [files] ] ...
* key [options] directory [ -x directory [files] ] ...
* The -a option allows an aliasing step in the parsing of the modules
* file. The "aliases" listed on a line following the -a are
* processed one-by-one, as if they were specified as arguments on the
static char rcsid
[] = "@(#)modules.c 1.57 92/04/10";
static int sort_order (CONST PTR l
, CONST PTR r
);
static void save_d (char *k
, int ks
, char *d
, int ds
);
static int sort_order ();
* Open the modules file, and die if the CVSROOT environment variable
* was not set. If the modules file does not exist, that's fine, and
* a warning message is displayed and a NULL is returned.
"%s: must set the CVSROOT environment variable\n",
error (1, 0, "or specify the '-d' option to %s", program_name
);
(void) sprintf (mfile
, "%s/%s/%s", CVSroot
, CVSROOTADM
, CVSROOTADM_MODULES
);
return (dbm_open (mfile
, O_RDONLY
, 0666));
* Close the modules file, if the open succeeded, that is
* 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
do_module (db
, mname
, m_type
, msg
, callback_proc
, where
,
shorten
, local_specified
, run_module_prog
, extra_arg
)
char *checkin_prog
= NULL
;
char *checkout_prog
= NULL
;
char *update_prog
= NULL
;
char *xmodargv
[MAXFILEPERDIR
];
/* remember where we start */
error (1, 0, "cannot get current working directory: %s", cwd
);
/* strip extra stuff from the module name */
* 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
.dsize
= strlen (key
.dptr
);
val
= dbm_fetch (db
, key
);
/* null terminate the value XXX - is this space ours? */
val
.dptr
[val
.dsize
] = '\0';
/* If the line ends in a comment, strip it off */
if ((cp
= index (val
.dptr
, '#')) != NULL
)
mwhere
= xstrdup (mname
);
char attic_file
[PATH_MAX
];
/* check to see if mname is a directory or file */
(void) sprintf (file
, "%s/%s", CVSroot
, mname
);
if ((acp
= rindex (mname
, '/')) != NULL
)
(void) sprintf (attic_file
, "%s/%s/%s/%s%s", CVSroot
, mname
,
CVSATTIC
, acp
+ 1, RCSEXT
);
(void) sprintf (attic_file
, "%s/%s/%s%s", CVSroot
, CVSATTIC
,
(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
= rindex (mname
, '/')) != NULL
&& cp
!= mname
)
/* put the ' ' in a copy so we don't mess up the original */
value
= strcpy (xvalue
, mname
);
slashp
= rindex (value
, '/');
* 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 '.'
/* drop the leading / if specified */
value
= strcpy (xvalue
, ". ");
(void) strcat (xvalue
, mname
+ 1);
/* otherwise just copy it */
value
= strcpy (xvalue
, ". ");
(void) strcat (xvalue
, mname
);
/* look up everything to the first / as a module */
if (mname
[0] != '/' && (cp
= index (mname
, '/')) != NULL
)
/* Make the slash the new end of the string temporarily */
key
.dsize
= strlen (key
.dptr
);
val
= dbm_fetch (db
, key
);
/* if we found it, clean up the value and life is good */
/* null terminate the value XXX - is this space ours? */
val
.dptr
[val
.dsize
] = '\0';
/* If the line ends in a comment, strip it off */
if ((cp2
= index (val
.dptr
, '#')) != NULL
)
/* mwhere gets just the module name */
mwhere
= xstrdup (mname
);
/* put the / back in mname */
/* put the / back in mname */
/* if we got here, we couldn't find it using our search, so give up */
error (0, 0, "cannot find module `%s' - ignored", mname
);
* At this point, we found what we were looking for in one
* of the many different forms.
/* copy value to our own string since if we go recursive we'll be
really screwed if we do another dbm lookup */
zvalue
= xstrdup (value
);
/* search the value for the special delimiter and save for later */
if ((cp
= index (value
, CVSMODULE_SPEC
)) != NULL
)
*cp
= '\0'; /* null out the special char */
spec_opt
= cp
+ 1; /* save the options for later */
if (cp
!= value
) /* strip whitespace if necessary */
* we had nothing but special options, so skip arg
* parsing and regular stuff entirely
* If there were only special ones though, we must
* make the appropriate directory and cd to it
/* XXX - XXX - MAJOR HACK - DO NOT SHIP - this needs to
be !pipeout, but we don't know that here yet */
dir
= where
? where
: mname
;
/* XXX - think about making null repositories at each dir here
instead of just at the bottom */
error (0, errno
, "cannot chdir to %s", dir
);
if (!isfile (CVSADM
) && !isfile (OCVSADM
))
char nullrepos
[PATH_MAX
];
(void) sprintf (nullrepos
, "%s/%s/%s", CVSroot
,
CVSROOTADM
, CVSNULLREPOS
);
(void) mkdir (nullrepos
, 0777);
Create_Admin (".", nullrepos
, (char *) NULL
, (char *) NULL
);
fp
= open_file (CVSADM_ENTSTAT
, "w+");
error (1, errno
, "cannot close %s", CVSADM_ENTSTAT
);
/* don't do special options only part of a module was specified */
* value now contains one of the following:
* 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 */
(void) sprintf (line
, "%s %s", "XXX", value
);
/* turn the line into an argv[] array */
line2argv (&modargc
, xmodargv
, line
);
while ((c
= gnu_getopt (modargc
, modargv
, CVSMODULE_OPTS
)) != -1)
mwhere
= xstrdup (optarg
);
"modules file has invalid option for key %s value %s",
error (0, 0, "modules file missing directory for module %s", mname
);
/* if this was an alias, call ourselves recursively for each module */
for (i
= 0; i
< modargc
; i
++)
err
+= do_module (db
, modargv
[i
], m_type
, msg
, callback_proc
,
where
, shorten
, local_specified
,
run_module_prog
, extra_arg
);
/* otherwise, process this module */
err
+= callback_proc (&modargc
, modargv
, where
, mwhere
, mfile
, shorten
,
local_specified
, mname
, msg
);
free_names (&modargc
, modargv
);
/* if there were special include args, process them now */
/* blow off special options if -l was specified */
cp
= index (spec_opt
, CVSMODULE_SPEC
);
/* save the beginning of the next arg */
/* strip whitespace off the end */
/* strip whitespace from front */
while (isspace (*spec_opt
))
error (0, 0, "Mal-formed %c option for module %s - ignored",
err
+= do_module (db
, spec_opt
, m_type
, msg
, callback_proc
,
(char *) NULL
, 0, local_specified
,
run_module_prog
, extra_arg
);
/* write out the checkin/update prog files if necessary */
if (err
== 0 && !noexec
&& m_type
== CHECKOUT
&& run_module_prog
)
if (checkin_prog
!= NULL
)
fp
= open_file (CVSADM_CIPROG
, "w+");
(void) fprintf (fp
, "%s\n", checkin_prog
);
error (1, errno
, "cannot close %s", CVSADM_CIPROG
);
fp
= open_file (CVSADM_UPROG
, "w+");
(void) fprintf (fp
, "%s\n", update_prog
);
error (1, errno
, "cannot close %s", CVSADM_UPROG
);
/* cd back to where we started */
error (1, errno
, "failed chdir to %s!", cwd
);
/* run checkout or tag prog if appropriate */
if (err
== 0 && run_module_prog
)
if ((m_type
== TAG
&& tag_prog
!= NULL
) ||
(m_type
== CHECKOUT
&& checkout_prog
!= NULL
))
* If a relative pathname is specified as the checkout or
* tag proc, try to tack on the current "where" value.
* if we can't find a matching program, just punt and use
* whatever is specified in the modules file.
char real_prog
[PATH_MAX
];
char *prog
= (m_type
== TAG
? tag_prog
: checkout_prog
);
char *real_where
= (where
!= NULL
? where
: mwhere
);
if ((*prog
!= '/') && (*prog
!= '.'))
(void) sprintf (real_prog
, "%s/%s", real_where
, prog
);
run_setup ("%s %s", prog
, real_where
);
(void) printf ("%s %s: Executing '", program_name
,
err
+= run_exec (RUN_TTY
, RUN_TTY
, RUN_TTY
, RUN_NORMAL
);
/* - Read all the records from the modules database into an array.
- Sort the array depending on what format is desired.
- Print the array in the format desired.
Currently, there are only two "desires":
1. Sort by module name and format the whole entry including switches,
files and the comment field: (Including aliases)
modulename -s switches, one per line, even if
Directories and files involved, formatted
to cover multiple lines if necessary.
# Comment, also formatted to cover multiple
2. Sort by status field string and print: (*not* including aliases)
modulename STATUS Directories and files involved, formatted
to cover multiple lines if necessary.
# Comment, also formatted to cover multiple
static struct sortrec
*s_head
;
static int s_max
= 0; /* Number of elements allocated */
static int s_count
= 0; /* Number of elements used */
static char def_status
[] = "NONE";
/* Sort routine for qsort:
- If we want the "Status" field to be sorted, check it first.
- Then compare the "module name" fields. Since they are unique, we don't
CONST
struct sortrec
*left
= (CONST
struct sortrec
*) l
;
CONST
struct sortrec
*right
= (CONST
struct sortrec
*) r
;
/* If Sort by status field, compare them. */
if ((i
= strcmp (left
->status
, right
->status
)) != 0)
return (strcmp (left
->modname
, right
->modname
));
if (Status
&& *d
== '-' && *(d
+ 1) == 'a')
return; /* We want "cvs co -s" and it is an alias! */
s_head
= (struct sortrec
*) xrealloc ((char *) s_head
, s_max
* sizeof (*s_head
));
s_rec
= &s_head
[s_count
];
s_rec
->modname
= cp
= xmalloc (ks
+ 1);
(void) strncpy (cp
, k
, ks
);
s_rec
->rest
= cp2
= xmalloc (ds
+ 1);
*(cp
+ ds
) = '\0'; /* Assumes an extra byte at end of static dbm buffer */
/* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
/* Look for the "-s statusvalue" text */
s_rec
->status
= def_status
;
/* Minor kluge, but general enough to maintain */
for (cp
= s_rec
->rest
; (cp2
= index (cp
, '-')) != NULL
; cp
= ++cp2
)
if (*(cp2
+ 1) == 's' && *(cp2
+ 2) == ' ')
s_rec
->status
= (cp2
+= 3);
/* Find comment field, clean up on all three sides & compress blanks */
if ((cp2
= cp
= index (cp
, '#')) != NULL
)
int i
, c
, wid
, argc
, cols
= 80, indent
, fill
;
char line
[MAXLINELEN
], *moduleargv
[MAXFILEPERDIR
];
(void) ioctl (0, TIOCGSIZE
, &ts
);
(void) ioctl (0, TIOCGWINSZ
, &ws
);
/* Read the whole modules file into allocated records */
if (!(db
= open_module ()))
error (1, 0, "failed to open the modules file");
for (key
= dbm_firstkey (db
); key
.dptr
!= NULL
; key
= dbm_nextkey (db
))
val
= dbm_fetch (db
, key
);
save_d (key
.dptr
, key
.dsize
, val
.dptr
, val
.dsize
);
/* Sort the list as requested */
qsort ((PTR
) s_head
, s_count
, sizeof (struct sortrec
), sort_order
);
* Run through the sorted array and format the entries
* indent = space for modulename + space for status field
indent
= 12 + (status
* 12);
fill
= cols
- (indent
+ 2);
for (s_h
= s_head
, i
= 0; i
< s_count
; i
++, s_h
++)
/* Print module name (and status, if wanted) */
(void) printf ("%-12s", s_h
->modname
);
(void) printf (" %-11s", s_h
->status
);
if (s_h
->status
!= def_status
)
*(s_h
->status
+ strlen (s_h
->status
)) = ' ';
/* Parse module file entry as command line and print options */
(void) sprintf (line
, "%s %s", s_h
->modname
, s_h
->rest
);
line2argv (&moduleargc
, moduleargv
, line
);
while ((c
= gnu_getopt (argc
, argv
, CVSMODULE_OPTS
)) != -1)
wid
+= 3; /* Could just set it to 3 */
if (strlen (optarg
) + 4 + wid
> (unsigned) fill
)
(void) printf ("\n%*s", indent
, "");
(void) printf (" -%c %s", c
, optarg
);
wid
+= strlen (optarg
) + 4;
/* Format and Print all the files and directories */
if (strlen (*argv
) + wid
> (unsigned) fill
)
(void) printf ("\n%*s", indent
, "");
(void) printf (" %s", *argv
);
wid
+= strlen (*argv
) + 1;
/* Format the comment field -- save_d (), compressed spaces */
for (cp2
= cp
= s_h
->comment
; *cp
; cp2
= cp
)
(void) printf ("%*s # ", indent
, "");
if (strlen (cp2
) < (unsigned) (fill
- 2))
(void) printf ("%s\n", cp2
);
while (*cp
!= ' ' && cp
> cp2
)
(void) printf ("%s\n", cp2
);
(void) printf ("%s\n", cp2
);