subr.c revision 1.1
1/*
2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
4 *
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS 1.4 kit.
7 *
8 * Various useful functions for the CVS support code.
9 */
10
11#include "cvs.h"
12
13#ifndef lint
14static const char rcsid[] = "$CVSid: @(#)subr.c 1.64 94/10/07 $";
15USE(rcsid);
16#endif
17
18extern char *getlogin ();
19
20/*
21 * malloc some data and die if it fails
22 */
23char *
24xmalloc (bytes)
25    size_t bytes;
26{
27    char *cp;
28
29    /* Parts of CVS try to xmalloc zero bytes and then free it.  Some
30       systems have a malloc which returns NULL for zero byte
31       allocations but a free which can't handle NULL, so compensate. */
32    if (bytes == 0)
33	bytes = 1;
34
35    cp = malloc (bytes);
36    if (cp == NULL)
37	error (1, 0, "can not allocate %lu bytes", (unsigned long) bytes);
38    return (cp);
39}
40
41/*
42 * realloc data and die if it fails [I've always wanted to have "realloc" do
43 * a "malloc" if the argument is NULL, but you can't depend on it.  Here, I
44 * can *force* it.
45 */
46char *
47xrealloc (ptr, bytes)
48    char *ptr;
49    size_t bytes;
50{
51    char *cp;
52
53    if (!ptr)
54	cp = malloc (bytes);
55    else
56	cp = realloc (ptr, bytes);
57
58    if (cp == NULL)
59	error (1, 0, "can not reallocate %lu bytes", (unsigned long) bytes);
60    return (cp);
61}
62
63/*
64 * Duplicate a string, calling xmalloc to allocate some dynamic space
65 */
66char *
67xstrdup (str)
68    const char *str;
69{
70    char *s;
71
72    if (str == NULL)
73	return ((char *) NULL);
74    s = xmalloc (strlen (str) + 1);
75    (void) strcpy (s, str);
76    return (s);
77}
78
79/*
80 * Recover the space allocated by Find_Names() and line2argv()
81 */
82void
83free_names (pargc, argv)
84    int *pargc;
85    char **argv;
86{
87    register int i;
88
89    for (i = 0; i < *pargc; i++)
90    {					/* only do through *pargc */
91	free (argv[i]);
92    }
93    *pargc = 0;				/* and set it to zero when done */
94}
95
96/*
97 * Convert a line into argc/argv components and return the result in the
98 * arguments as passed.  Use free_names() to return the memory allocated here
99 * back to the free pool.
100 */
101void
102line2argv (pargc, argv, line)
103    int *pargc;
104    char **argv;
105    char *line;
106{
107    char *cp;
108
109    *pargc = 0;
110    for (cp = strtok (line, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
111    {
112	argv[*pargc] = xstrdup (cp);
113	(*pargc)++;
114    }
115}
116
117/*
118 * Returns the number of dots ('.') found in an RCS revision number
119 */
120int
121numdots (s)
122    const char *s;
123{
124    int dots = 0;
125
126    for (; *s; s++)
127    {
128	if (*s == '.')
129	    dots++;
130    }
131    return (dots);
132}
133
134/*
135 * Get the caller's login from his uid. If the real uid is "root" try LOGNAME
136 * USER or getlogin(). If getlogin() and getpwuid() both fail, return
137 * the uid as a string.
138 */
139char *
140getcaller ()
141{
142    static char uidname[20];
143    struct passwd *pw;
144    char *name;
145    uid_t uid;
146
147    uid = getuid ();
148    if (uid == (uid_t) 0)
149    {
150	/* super-user; try getlogin() to distinguish */
151	if (((name = getenv("LOGNAME")) || (name = getenv("USER")) ||
152	     (name = getlogin ())) && *name)
153	    return (name);
154    }
155    if ((pw = (struct passwd *) getpwuid (uid)) == NULL)
156    {
157	(void) sprintf (uidname, "uid%lu", (unsigned long) uid);
158	return (uidname);
159    }
160    return (pw->pw_name);
161}
162
163#ifdef lint
164#ifndef __GNUC__
165/* ARGSUSED */
166time_t
167get_date (date, now)
168    char *date;
169    struct timeb *now;
170{
171    time_t foo = 0;
172
173    return (foo);
174}
175#endif
176#endif
177
178/* Given two revisions, find their greatest common ancestor.  If the
179   two input revisions exist, then rcs guarantees that the gca will
180   exist.  */
181
182char *
183gca (rev1, rev2)
184    char *rev1;
185    char *rev2;
186{
187    int dots;
188    char gca[PATH_MAX];
189    char *p[2];
190    int j[2];
191
192    if (rev1 == NULL || rev2 == NULL)
193    {
194	error (0, 0, "sanity failure in gca");
195	abort();
196    }
197
198    /* walk the strings, reading the common parts. */
199    gca[0] = '\0';
200    p[0] = rev1;
201    p[1] = rev2;
202    do
203    {
204	int i;
205	char c[2];
206	char *s[2];
207
208	for (i = 0; i < 2; ++i)
209	{
210	    /* swap out the dot */
211	    s[i] = strchr (p[i], '.');
212	    if (s[i] != NULL) {
213		c[i] = *s[i];
214	    }
215
216	    /* read an int */
217	    j[i] = atoi (p[i]);
218
219	    /* swap back the dot... */
220	    if (s[i] != NULL) {
221		*s[i] = c[i];
222		p[i] = s[i] + 1;
223	    }
224	    else
225	    {
226		/* or mark us at the end */
227		p[i] = NULL;
228	    }
229
230	}
231
232	/* use the lowest. */
233	(void) sprintf (gca + strlen (gca), "%d.",
234			j[0] < j[1] ? j[0] : j[1]);
235
236    } while (j[0] == j[1]
237	     && p[0] != NULL
238	     && p[1] != NULL);
239
240    /* back up over that last dot. */
241    gca[strlen(gca) - 1] = '\0';
242
243    /* numbers differ, or we ran out of strings.  we're done with the
244       common parts.  */
245
246    dots = numdots (gca);
247    if (dots == 0)
248    {
249	/* revisions differ in trunk major number.  */
250
251	char *q;
252	char *s;
253
254	s = (j[0] < j[1]) ? p[0] : p[1];
255
256	if (s == NULL)
257	{
258	    /* we only got one number.  this is strange.  */
259	    error (0, 0, "bad revisions %s or %s", rev1, rev2);
260	    abort();
261	}
262	else
263	{
264	    /* we have a minor number.  use it.  */
265	    q = gca + strlen (gca);
266
267	    *q++ = '.';
268	    for ( ; *s != '.' && *s != '\0'; )
269		*q++ = *s++;
270
271	    *q = '\0';
272	}
273    }
274    else if ((dots & 1) == 0)
275    {
276	/* if we have an even number of dots, then we have a branch.
277	   remove the last number in order to make it a revision.  */
278
279	char *s;
280
281	s = strrchr(gca, '.');
282	*s = '\0';
283    }
284
285    return (xstrdup (gca));
286}
287
288/*
289 *  Sanity checks and any required fix-up on message passed to RCS via '-m'.
290 *  RCS 5.7 requires that a non-total-whitespace, non-null message be provided
291 *  with '-m'.
292 */
293char *
294make_message_rcslegal (message)
295     char *message;
296{
297    if ((message == NULL) || (*message == '\0') || isspace (*message))
298    {
299        char *t;
300
301	if (message)
302	    for (t = message; *t; t++)
303	        if (!isspace (*t))
304		    return message;
305
306	return "*** empty log message ***\n";
307    }
308
309    return message;
310}
311