subr.c revision 17721
1186121Skmacy/*
2186121Skmacy * Copyright (c) 1992, Brian Berliner and Jeff Polk
3186121Skmacy * Copyright (c) 1989-1992, Brian Berliner
4186121Skmacy *
5186121Skmacy * You may distribute under the terms of the GNU General Public License as
6186121Skmacy * specified in the README file that comes with the CVS 1.4 kit.
7186121Skmacy *
8186121Skmacy * Various useful functions for the CVS support code.
9186121Skmacy */
10186121Skmacy
11186121Skmacy#include "cvs.h"
12186121Skmacy
13186121Skmacyextern char *getlogin ();
14186121Skmacy
15186121Skmacy/*
16186121Skmacy * malloc some data and die if it fails
17186121Skmacy */
18186121Skmacychar *
19186121Skmacyxmalloc (bytes)
20186121Skmacy    size_t bytes;
21186121Skmacy{
22186121Skmacy    char *cp;
23186121Skmacy
24186121Skmacy    /* Parts of CVS try to xmalloc zero bytes and then free it.  Some
25186121Skmacy       systems have a malloc which returns NULL for zero byte
26186121Skmacy       allocations but a free which can't handle NULL, so compensate. */
27186121Skmacy    if (bytes == 0)
28186121Skmacy	bytes = 1;
29186121Skmacy
30186121Skmacy    cp = malloc (bytes);
31186121Skmacy    if (cp == NULL)
32186121Skmacy	error (1, 0, "can not allocate %lu bytes", (unsigned long) bytes);
33186121Skmacy    return (cp);
34186121Skmacy}
35186121Skmacy
36186121Skmacy/*
37186121Skmacy * realloc data and die if it fails [I've always wanted to have "realloc" do
38186121Skmacy * a "malloc" if the argument is NULL, but you can't depend on it.  Here, I
39186121Skmacy * can *force* it.
40186121Skmacy */
41186121Skmacyvoid *
42186121Skmacyxrealloc (ptr, bytes)
43186121Skmacy    void *ptr;
44186121Skmacy    size_t bytes;
45186121Skmacy{
46186121Skmacy    char *cp;
47186121Skmacy
48186121Skmacy    if (!ptr)
49186121Skmacy	cp = malloc (bytes);
50186121Skmacy    else
51186121Skmacy	cp = realloc (ptr, bytes);
52186121Skmacy
53186121Skmacy    if (cp == NULL)
54186121Skmacy	error (1, 0, "can not reallocate %lu bytes", (unsigned long) bytes);
55186121Skmacy    return (cp);
56186121Skmacy}
57186121Skmacy
58186121Skmacy/*
59186121Skmacy * Duplicate a string, calling xmalloc to allocate some dynamic space
60186121Skmacy */
61186121Skmacychar *
62186121Skmacyxstrdup (str)
63186121Skmacy    const char *str;
64186121Skmacy{
65186121Skmacy    char *s;
66186121Skmacy
67186121Skmacy    if (str == NULL)
68186121Skmacy	return ((char *) NULL);
69186121Skmacy    s = xmalloc (strlen (str) + 1);
70186121Skmacy    (void) strcpy (s, str);
71186121Skmacy    return (s);
72186121Skmacy}
73186121Skmacy
74186121Skmacy/* Remove trailing newlines from STRING, destructively. */
75186121Skmacyvoid
76186121Skmacystrip_trailing_newlines (str)
77186121Skmacy     char *str;
78186121Skmacy{
79186121Skmacy  int len;
80186121Skmacy  len = strlen (str) - 1;
81186121Skmacy
82186121Skmacy  while (str[len] == '\n')
83186121Skmacy    str[len--] = '\0';
84186149Skmacy}
85186121Skmacy
86186121Skmacy/*
87186121Skmacy * Recover the space allocated by Find_Names() and line2argv()
88186121Skmacy */
89186121Skmacyvoid
90186121Skmacyfree_names (pargc, argv)
91186121Skmacy    int *pargc;
92186121Skmacy    char **argv;
93186121Skmacy{
94186121Skmacy    register int i;
95186121Skmacy
96186121Skmacy    for (i = 0; i < *pargc; i++)
97186121Skmacy    {					/* only do through *pargc */
98186121Skmacy	free (argv[i]);
99186121Skmacy    }
100186121Skmacy    *pargc = 0;				/* and set it to zero when done */
101186121Skmacy}
102186121Skmacy
103186121Skmacy/*
104186121Skmacy * Convert a line into argc/argv components and return the result in the
105186121Skmacy * arguments as passed.  Use free_names() to return the memory allocated here
106186121Skmacy * back to the free pool.
107186121Skmacy */
108186121Skmacyvoid
109186121Skmacyline2argv (pargc, argv, line)
110186121Skmacy    int *pargc;
111186121Skmacy    char **argv;
112186121Skmacy    char *line;
113186121Skmacy{
114186121Skmacy    char *cp;
115186121Skmacy
116186121Skmacy    *pargc = 0;
117186121Skmacy    for (cp = strtok (line, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
118186121Skmacy    {
119186121Skmacy	argv[*pargc] = xstrdup (cp);
120186121Skmacy	(*pargc)++;
121186121Skmacy    }
122186121Skmacy}
123186149Skmacy
124186121Skmacy/*
125186121Skmacy * Returns the number of dots ('.') found in an RCS revision number
126186121Skmacy */
127186121Skmacyint
128186121Skmacynumdots (s)
129186121Skmacy    const char *s;
130186121Skmacy{
131186121Skmacy    int dots = 0;
132186121Skmacy
133186121Skmacy    for (; *s; s++)
134186121Skmacy    {
135186121Skmacy	if (*s == '.')
136186121Skmacy	    dots++;
137186121Skmacy    }
138186121Skmacy    return (dots);
139186121Skmacy}
140186121Skmacy
141186121Skmacy/*
142186121Skmacy * Get the caller's login from his uid. If the real uid is "root" try LOGNAME
143186121Skmacy * USER or getlogin(). If getlogin() and getpwuid() both fail, return
144186121Skmacy * the uid as a string.
145186121Skmacy */
146186121Skmacychar *
147186121Skmacygetcaller ()
148186121Skmacy{
149186121Skmacy    static char uidname[20];
150186121Skmacy    struct passwd *pw;
151186121Skmacy    char *name;
152186121Skmacy    uid_t uid;
153186121Skmacy
154186121Skmacy    uid = getuid ();
155186121Skmacy    if (uid == (uid_t) 0)
156186121Skmacy    {
157186121Skmacy	/* super-user; try getlogin() to distinguish */
158186121Skmacy	if (((name = getlogin ()) || (name = getenv("LOGNAME")) ||
159186121Skmacy	     (name = getenv("USER"))) && *name)
160186121Skmacy	    return (name);
161186121Skmacy    }
162186121Skmacy    if ((pw = (struct passwd *) getpwuid (uid)) == NULL)
163186121Skmacy    {
164186121Skmacy	(void) sprintf (uidname, "uid%lu", (unsigned long) uid);
165186121Skmacy	return (uidname);
166186121Skmacy    }
167186121Skmacy    return (pw->pw_name);
168186121Skmacy}
169186121Skmacy
170186121Skmacy#ifdef lint
171186121Skmacy#ifndef __GNUC__
172186121Skmacy/* ARGSUSED */
173186121Skmacytime_t
174186121Skmacyget_date (date, now)
175186121Skmacy    char *date;
176186121Skmacy    struct timeb *now;
177186121Skmacy{
178186121Skmacy    time_t foo = 0;
179186121Skmacy
180186121Skmacy    return (foo);
181186121Skmacy}
182186121Skmacy#endif
183186121Skmacy#endif
184186121Skmacy
185186121Skmacy/* Given two revisions, find their greatest common ancestor.  If the
186186121Skmacy   two input revisions exist, then rcs guarantees that the gca will
187186121Skmacy   exist.  */
188186121Skmacy
189186121Skmacychar *
190186121Skmacygca (rev1, rev2)
191186121Skmacy    char *rev1;
192186121Skmacy    char *rev2;
193{
194    int dots;
195    char gca[PATH_MAX];
196    char *p[2];
197    int j[2];
198
199    if (rev1 == NULL || rev2 == NULL)
200    {
201	error (0, 0, "sanity failure in gca");
202	abort();
203    }
204
205    /* walk the strings, reading the common parts. */
206    gca[0] = '\0';
207    p[0] = rev1;
208    p[1] = rev2;
209    do
210    {
211	int i;
212	char c[2];
213	char *s[2];
214
215	for (i = 0; i < 2; ++i)
216	{
217	    /* swap out the dot */
218	    s[i] = strchr (p[i], '.');
219	    if (s[i] != NULL) {
220		c[i] = *s[i];
221	    }
222
223	    /* read an int */
224	    j[i] = atoi (p[i]);
225
226	    /* swap back the dot... */
227	    if (s[i] != NULL) {
228		*s[i] = c[i];
229		p[i] = s[i] + 1;
230	    }
231	    else
232	    {
233		/* or mark us at the end */
234		p[i] = NULL;
235	    }
236
237	}
238
239	/* use the lowest. */
240	(void) sprintf (gca + strlen (gca), "%d.",
241			j[0] < j[1] ? j[0] : j[1]);
242
243    } while (j[0] == j[1]
244	     && p[0] != NULL
245	     && p[1] != NULL);
246
247    /* back up over that last dot. */
248    gca[strlen(gca) - 1] = '\0';
249
250    /* numbers differ, or we ran out of strings.  we're done with the
251       common parts.  */
252
253    dots = numdots (gca);
254    if (dots == 0)
255    {
256	/* revisions differ in trunk major number.  */
257
258	char *q;
259	char *s;
260
261	s = (j[0] < j[1]) ? p[0] : p[1];
262
263	if (s == NULL)
264	{
265	    /* we only got one number.  this is strange.  */
266	    error (0, 0, "bad revisions %s or %s", rev1, rev2);
267	    abort();
268	}
269	else
270	{
271	    /* we have a minor number.  use it.  */
272	    q = gca + strlen (gca);
273
274	    *q++ = '.';
275	    for ( ; *s != '.' && *s != '\0'; )
276		*q++ = *s++;
277
278	    *q = '\0';
279	}
280    }
281    else if ((dots & 1) == 0)
282    {
283	/* if we have an even number of dots, then we have a branch.
284	   remove the last number in order to make it a revision.  */
285
286	char *s;
287
288	s = strrchr(gca, '.');
289	*s = '\0';
290    }
291
292    return (xstrdup (gca));
293}
294
295/*
296 *  Sanity checks and any required fix-up on message passed to RCS via '-m'.
297 *  RCS 5.7 requires that a non-total-whitespace, non-null message be provided
298 *  with '-m'.  Returns the original argument or a pointer to readonly
299 *  static storage.
300 */
301char *
302make_message_rcslegal (message)
303     char *message;
304{
305    if ((message == NULL) || (*message == '\0') || isspace (*message))
306    {
307        char *t;
308
309	if (message)
310	    for (t = message; *t; t++)
311	        if (!isspace (*t))
312		    return message;
313
314	return "*** empty log message ***\n";
315    }
316
317    return message;
318}
319