1993-06-18 05:46:17 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
|
|
|
* Copyright (c) 1989-1992, Brian Berliner
|
1995-12-03 14:17:06 +00:00
|
|
|
*
|
1993-06-18 05:46:17 +00:00
|
|
|
* You may distribute under the terms of the GNU General Public License as
|
1995-03-31 07:45:33 +00:00
|
|
|
* specified in the README file that comes with the CVS 1.4 kit.
|
1995-12-03 14:17:06 +00:00
|
|
|
*
|
1993-06-18 05:46:17 +00:00
|
|
|
* Various useful functions for the CVS support code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cvs.h"
|
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
#ifndef lint
|
1995-12-10 23:10:40 +00:00
|
|
|
static const char rcsid[] = "$CVSid: @(#)subr.c 1.64 94/10/07 $";
|
|
|
|
USE(rcsid);
|
1995-03-31 07:45:33 +00:00
|
|
|
#endif
|
|
|
|
|
1993-06-18 05:46:17 +00:00
|
|
|
extern char *getlogin ();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* malloc some data and die if it fails
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
xmalloc (bytes)
|
1995-03-31 07:45:33 +00:00
|
|
|
size_t bytes;
|
1993-06-18 05:46:17 +00:00
|
|
|
{
|
|
|
|
char *cp;
|
|
|
|
|
1995-12-10 23:10:40 +00:00
|
|
|
/* Parts of CVS try to xmalloc zero bytes and then free it. Some
|
|
|
|
systems have a malloc which returns NULL for zero byte
|
|
|
|
allocations but a free which can't handle NULL, so compensate. */
|
|
|
|
if (bytes == 0)
|
|
|
|
bytes = 1;
|
|
|
|
|
|
|
|
cp = malloc (bytes);
|
|
|
|
if (cp == NULL)
|
1995-03-31 07:45:33 +00:00
|
|
|
error (1, 0, "can not allocate %lu bytes", (unsigned long) bytes);
|
1993-06-18 05:46:17 +00:00
|
|
|
return (cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* realloc data and die if it fails [I've always wanted to have "realloc" do
|
|
|
|
* a "malloc" if the argument is NULL, but you can't depend on it. Here, I
|
|
|
|
* can *force* it.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
xrealloc (ptr, bytes)
|
|
|
|
char *ptr;
|
1995-03-31 07:45:33 +00:00
|
|
|
size_t bytes;
|
1993-06-18 05:46:17 +00:00
|
|
|
{
|
|
|
|
char *cp;
|
|
|
|
|
|
|
|
if (!ptr)
|
1995-03-31 07:45:33 +00:00
|
|
|
cp = malloc (bytes);
|
|
|
|
else
|
|
|
|
cp = realloc (ptr, bytes);
|
1993-06-18 05:46:17 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
if (cp == NULL)
|
|
|
|
error (1, 0, "can not reallocate %lu bytes", (unsigned long) bytes);
|
1993-06-18 05:46:17 +00:00
|
|
|
return (cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Duplicate a string, calling xmalloc to allocate some dynamic space
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
xstrdup (str)
|
1995-12-10 23:10:40 +00:00
|
|
|
const char *str;
|
1993-06-18 05:46:17 +00:00
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
if (str == NULL)
|
|
|
|
return ((char *) NULL);
|
|
|
|
s = xmalloc (strlen (str) + 1);
|
|
|
|
(void) strcpy (s, str);
|
|
|
|
return (s);
|
|
|
|
}
|
|
|
|
|
1995-12-10 23:10:40 +00:00
|
|
|
/* Remove trailing newlines from STRING, destructively. */
|
1993-06-18 05:46:17 +00:00
|
|
|
void
|
1995-12-10 23:10:40 +00:00
|
|
|
strip_trailing_newlines (str)
|
|
|
|
char *str;
|
1993-06-18 05:46:17 +00:00
|
|
|
{
|
1995-12-10 23:10:40 +00:00
|
|
|
int len;
|
|
|
|
len = strlen (str) - 1;
|
1993-06-18 05:46:17 +00:00
|
|
|
|
1995-12-10 23:10:40 +00:00
|
|
|
while (str[len] == '\n')
|
|
|
|
str[len--] = '\0';
|
1993-06-18 05:46:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recover the space allocated by Find_Names() and line2argv()
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
free_names (pargc, argv)
|
|
|
|
int *pargc;
|
1995-12-10 23:10:40 +00:00
|
|
|
char **argv;
|
1993-06-18 05:46:17 +00:00
|
|
|
{
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
for (i = 0; i < *pargc; i++)
|
|
|
|
{ /* only do through *pargc */
|
|
|
|
free (argv[i]);
|
|
|
|
}
|
|
|
|
*pargc = 0; /* and set it to zero when done */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert a line into argc/argv components and return the result in the
|
|
|
|
* arguments as passed. Use free_names() to return the memory allocated here
|
|
|
|
* back to the free pool.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
line2argv (pargc, argv, line)
|
|
|
|
int *pargc;
|
1995-12-10 23:10:40 +00:00
|
|
|
char **argv;
|
1993-06-18 05:46:17 +00:00
|
|
|
char *line;
|
|
|
|
{
|
|
|
|
char *cp;
|
|
|
|
|
|
|
|
*pargc = 0;
|
|
|
|
for (cp = strtok (line, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
|
|
|
|
{
|
|
|
|
argv[*pargc] = xstrdup (cp);
|
|
|
|
(*pargc)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the number of dots ('.') found in an RCS revision number
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
numdots (s)
|
1995-12-10 23:10:40 +00:00
|
|
|
const char *s;
|
1993-06-18 05:46:17 +00:00
|
|
|
{
|
|
|
|
int dots = 0;
|
|
|
|
|
1995-12-10 23:10:40 +00:00
|
|
|
for (; *s; s++)
|
1993-06-18 05:46:17 +00:00
|
|
|
{
|
1995-12-10 23:10:40 +00:00
|
|
|
if (*s == '.')
|
1993-06-18 05:46:17 +00:00
|
|
|
dots++;
|
|
|
|
}
|
|
|
|
return (dots);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the caller's login from his uid. If the real uid is "root" try LOGNAME
|
|
|
|
* USER or getlogin(). If getlogin() and getpwuid() both fail, return
|
|
|
|
* the uid as a string.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
getcaller ()
|
|
|
|
{
|
|
|
|
static char uidname[20];
|
|
|
|
struct passwd *pw;
|
|
|
|
char *name;
|
1995-03-31 07:45:33 +00:00
|
|
|
uid_t uid;
|
1993-06-18 05:46:17 +00:00
|
|
|
|
|
|
|
uid = getuid ();
|
1995-03-31 07:45:33 +00:00
|
|
|
if (uid == (uid_t) 0)
|
1993-06-18 05:46:17 +00:00
|
|
|
{
|
|
|
|
/* super-user; try getlogin() to distinguish */
|
1995-12-10 23:10:40 +00:00
|
|
|
if (((name = getlogin ()) || (name = getenv("LOGNAME")) ||
|
|
|
|
(name = getenv("USER"))) && *name)
|
1993-06-18 05:46:17 +00:00
|
|
|
return (name);
|
|
|
|
}
|
|
|
|
if ((pw = (struct passwd *) getpwuid (uid)) == NULL)
|
|
|
|
{
|
1995-12-10 23:10:40 +00:00
|
|
|
(void) sprintf (uidname, "uid%lu", (unsigned long) uid);
|
1993-06-18 05:46:17 +00:00
|
|
|
return (uidname);
|
|
|
|
}
|
|
|
|
return (pw->pw_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef lint
|
|
|
|
#ifndef __GNUC__
|
|
|
|
/* ARGSUSED */
|
|
|
|
time_t
|
|
|
|
get_date (date, now)
|
|
|
|
char *date;
|
|
|
|
struct timeb *now;
|
|
|
|
{
|
|
|
|
time_t foo = 0;
|
|
|
|
|
|
|
|
return (foo);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
1995-03-31 07:45:33 +00:00
|
|
|
|
|
|
|
/* Given two revisions, find their greatest common ancestor. If the
|
|
|
|
two input revisions exist, then rcs guarantees that the gca will
|
|
|
|
exist. */
|
|
|
|
|
|
|
|
char *
|
|
|
|
gca (rev1, rev2)
|
|
|
|
char *rev1;
|
|
|
|
char *rev2;
|
|
|
|
{
|
|
|
|
int dots;
|
|
|
|
char gca[PATH_MAX];
|
|
|
|
char *p[2];
|
|
|
|
int j[2];
|
|
|
|
|
|
|
|
if (rev1 == NULL || rev2 == NULL)
|
|
|
|
{
|
|
|
|
error (0, 0, "sanity failure in gca");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* walk the strings, reading the common parts. */
|
|
|
|
gca[0] = '\0';
|
|
|
|
p[0] = rev1;
|
|
|
|
p[1] = rev2;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char c[2];
|
|
|
|
char *s[2];
|
1995-12-03 14:17:06 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
for (i = 0; i < 2; ++i)
|
|
|
|
{
|
|
|
|
/* swap out the dot */
|
|
|
|
s[i] = strchr (p[i], '.');
|
|
|
|
if (s[i] != NULL) {
|
|
|
|
c[i] = *s[i];
|
|
|
|
}
|
1995-12-03 14:17:06 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
/* read an int */
|
|
|
|
j[i] = atoi (p[i]);
|
1995-12-03 14:17:06 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
/* swap back the dot... */
|
|
|
|
if (s[i] != NULL) {
|
|
|
|
*s[i] = c[i];
|
|
|
|
p[i] = s[i] + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* or mark us at the end */
|
|
|
|
p[i] = NULL;
|
|
|
|
}
|
1995-12-03 14:17:06 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
}
|
1995-12-03 14:17:06 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
/* use the lowest. */
|
|
|
|
(void) sprintf (gca + strlen (gca), "%d.",
|
|
|
|
j[0] < j[1] ? j[0] : j[1]);
|
|
|
|
|
|
|
|
} while (j[0] == j[1]
|
|
|
|
&& p[0] != NULL
|
|
|
|
&& p[1] != NULL);
|
|
|
|
|
|
|
|
/* back up over that last dot. */
|
|
|
|
gca[strlen(gca) - 1] = '\0';
|
|
|
|
|
|
|
|
/* numbers differ, or we ran out of strings. we're done with the
|
|
|
|
common parts. */
|
|
|
|
|
|
|
|
dots = numdots (gca);
|
|
|
|
if (dots == 0)
|
|
|
|
{
|
|
|
|
/* revisions differ in trunk major number. */
|
|
|
|
|
|
|
|
char *q;
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
s = (j[0] < j[1]) ? p[0] : p[1];
|
|
|
|
|
|
|
|
if (s == NULL)
|
|
|
|
{
|
|
|
|
/* we only got one number. this is strange. */
|
|
|
|
error (0, 0, "bad revisions %s or %s", rev1, rev2);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* we have a minor number. use it. */
|
|
|
|
q = gca + strlen (gca);
|
1995-12-03 14:17:06 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
*q++ = '.';
|
|
|
|
for ( ; *s != '.' && *s != '\0'; )
|
|
|
|
*q++ = *s++;
|
1995-12-03 14:17:06 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
*q = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((dots & 1) == 0)
|
|
|
|
{
|
|
|
|
/* if we have an even number of dots, then we have a branch.
|
|
|
|
remove the last number in order to make it a revision. */
|
1995-12-03 14:17:06 +00:00
|
|
|
|
1995-03-31 07:45:33 +00:00
|
|
|
char *s;
|
|
|
|
|
|
|
|
s = strrchr(gca, '.');
|
|
|
|
*s = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
return (xstrdup (gca));
|
|
|
|
}
|
|
|
|
|
1995-12-10 23:10:40 +00:00
|
|
|
/*
|
|
|
|
* Sanity checks and any required fix-up on message passed to RCS via '-m'.
|
|
|
|
* RCS 5.7 requires that a non-total-whitespace, non-null message be provided
|
|
|
|
* with '-m'.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
make_message_rcslegal (message)
|
|
|
|
char *message;
|
1995-03-31 07:45:33 +00:00
|
|
|
{
|
1995-12-10 23:10:40 +00:00
|
|
|
if ((message == NULL) || (*message == '\0') || isspace (*message))
|
|
|
|
{
|
|
|
|
char *t;
|
|
|
|
|
|
|
|
if (message)
|
|
|
|
for (t = message; *t; t++)
|
|
|
|
if (!isspace (*t))
|
|
|
|
return message;
|
1995-03-31 07:45:33 +00:00
|
|
|
|
1995-12-10 23:10:40 +00:00
|
|
|
return "*** empty log message ***\n";
|
1995-03-31 07:45:33 +00:00
|
|
|
}
|
1995-12-10 23:10:40 +00:00
|
|
|
|
|
|
|
return message;
|
1995-03-31 07:45:33 +00:00
|
|
|
}
|