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