1/* 2 * FreeBSD install - a package for the installation and maintenance 3 * of non-core utilities. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * Jordan K. Hubbard 15 * 18 July 1993 16 * 17 * Miscellaneous file access utilities. 18 * 19 */ 20 21#include <sys/cdefs.h> 22__FBSDID("$FreeBSD$"); 23 24#include "lib.h" 25#include <err.h> 26#include <pwd.h> 27#include <time.h> 28#include <sys/wait.h> 29 30/* Quick check to see if a file exists */ 31Boolean 32fexists(const char *fname) 33{ 34 int fd; 35 36 if ((fd = open(fname, O_RDONLY)) == -1) 37 return FALSE; 38 39 close(fd); 40 return TRUE; 41} 42 43/* Quick check to see if something is a directory or symlink to a directory */ 44Boolean 45isdir(const char *fname) 46{ 47 struct stat sb; 48 49 if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) 50 return TRUE; 51 else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode)) 52 return TRUE; 53 else 54 return FALSE; 55} 56 57/* Check to see if file is a dir or symlink to a dir, and is empty */ 58Boolean 59isemptydir(const char *fname) 60{ 61 if (isdir(fname)) { 62 DIR *dirp; 63 struct dirent *dp; 64 65 dirp = opendir(fname); 66 if (!dirp) 67 return FALSE; /* no perms, leave it alone */ 68 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 69 if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { 70 closedir(dirp); 71 return FALSE; 72 } 73 } 74 (void)closedir(dirp); 75 return TRUE; 76 } 77 return FALSE; 78} 79 80/* 81 * Returns TRUE if file is a regular file or symlink pointing to a regular 82 * file 83 */ 84Boolean 85isfile(const char *fname) 86{ 87 struct stat sb; 88 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) 89 return TRUE; 90 return FALSE; 91} 92 93/* 94 * Check to see if file is a file or symlink pointing to a file and is empty. 95 * If nonexistent or not a file, say "it's empty", otherwise return TRUE if 96 * zero sized. 97 */ 98Boolean 99isemptyfile(const char *fname) 100{ 101 struct stat sb; 102 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) { 103 if (sb.st_size != 0) 104 return FALSE; 105 } 106 return TRUE; 107} 108 109/* Returns TRUE if file is a symbolic link. */ 110Boolean 111issymlink(const char *fname) 112{ 113 struct stat sb; 114 if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) 115 return TRUE; 116 return FALSE; 117} 118 119/* Returns TRUE if file is a URL specification */ 120Boolean 121isURL(const char *fname) 122{ 123 /* 124 * I'm sure there are other types of URL specifications that I could 125 * also be looking for here, but for now I'll just be happy to get ftp 126 * and http working. 127 */ 128 if (!fname) 129 return FALSE; 130 while (isspace(*fname)) 131 ++fname; 132 if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7) || 133 !strncmp(fname, "https://", 8) || !strncmp(fname, "file://", 7)) 134 return TRUE; 135 return FALSE; 136} 137 138char * 139fileFindByPath(const char *base, const char *fname) 140{ 141 static char tmp[FILENAME_MAX]; 142 char *cp; 143 const char *suffixes[] = {".tbz", ".tgz", ".tar", ".txz", NULL}; 144 int i; 145 146 if (fexists(fname) && isfile(fname)) { 147 strcpy(tmp, fname); 148 return tmp; 149 } 150 if (base) { 151 strcpy(tmp, base); 152 153 cp = strrchr(tmp, '/'); 154 if (cp) { 155 *cp = '\0'; /* chop name */ 156 cp = strrchr(tmp, '/'); 157 } 158 if (cp) 159 for (i = 0; suffixes[i] != NULL; i++) { 160 *(cp + 1) = '\0'; 161 strcat(cp, "All/"); 162 strcat(cp, fname); 163 strcat(cp, suffixes[i]); 164 if (fexists(tmp)) 165 return tmp; 166 } 167 } 168 169 cp = getenv("PKG_PATH"); 170 while (cp) { 171 char *cp2 = strsep(&cp, ":"); 172 173 for (i = 0; suffixes[i] != NULL; i++) { 174 snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]); 175 if (fexists(tmp) && isfile(tmp)) 176 return tmp; 177 } 178 } 179 return NULL; 180} 181 182char * 183fileGetContents(const char *fname) 184{ 185 char *contents; 186 struct stat sb; 187 int fd; 188 189 if (stat(fname, &sb) == FAIL) { 190 cleanup(0); 191 errx(2, "%s: can't stat '%s'", __func__, fname); 192 } 193 194 contents = (char *)malloc(sb.st_size + 1); 195 fd = open(fname, O_RDONLY, 0); 196 if (fd == FAIL) { 197 cleanup(0); 198 errx(2, "%s: unable to open '%s' for reading", __func__, fname); 199 } 200 if (read(fd, contents, sb.st_size) != sb.st_size) { 201 cleanup(0); 202 errx(2, "%s: short read on '%s' - did not get %lld bytes", __func__, 203 fname, (long long)sb.st_size); 204 } 205 close(fd); 206 contents[sb.st_size] = '\0'; 207 return contents; 208} 209 210/* 211 * Takes a filename and package name, returning (in "try") the 212 * canonical "preserve" name for it. 213 */ 214Boolean 215make_preserve_name(char *try, int max, const char *name, const char *file) 216{ 217 int len, i; 218 219 if ((len = strlen(file)) == 0) 220 return FALSE; 221 else 222 i = len - 1; 223 strncpy(try, file, max); 224 if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */ 225 --i; 226 for (; i; i--) { 227 if (try[i] == '/') { 228 try[i + 1]= '.'; 229 strncpy(&try[i + 2], &file[i + 1], max - i - 2); 230 break; 231 } 232 } 233 if (!i) { 234 try[0] = '.'; 235 strncpy(try + 1, file, max - 1); 236 } 237 /* I should probably be called rude names for these inline assignments */ 238 strncat(try, ".", max -= strlen(try)); 239 strncat(try, name, max -= strlen(name)); 240 strncat(try, ".", max--); 241 strncat(try, "backup", max -= 6); 242 return TRUE; 243} 244 245/* Write the contents of "str" to a file */ 246void 247write_file(const char *name, const char *str) 248{ 249 FILE *fp; 250 size_t len; 251 252 fp = fopen(name, "w"); 253 if (!fp) { 254 cleanup(0); 255 errx(2, "%s: cannot fopen '%s' for writing", __func__, name); 256 } 257 len = strlen(str); 258 if (fwrite(str, 1, len, fp) != len) { 259 cleanup(0); 260 errx(2, "%s: short fwrite on '%s', tried to write %ld bytes", 261 __func__, name, (long)len); 262 } 263 if (fclose(fp)) { 264 cleanup(0); 265 errx(2, "%s: failure to fclose '%s'", __func__, name); 266 } 267} 268 269void 270copy_file(const char *dir, const char *fname, const char *to) 271{ 272 char cmd[FILENAME_MAX]; 273 274 if (fname[0] == '/') 275 snprintf(cmd, FILENAME_MAX, "/bin/cp -r %s %s", fname, to); 276 else 277 snprintf(cmd, FILENAME_MAX, "/bin/cp -r %s/%s %s", dir, fname, to); 278 if (vsystem(cmd)) { 279 cleanup(0); 280 errx(2, "%s: could not perform '%s'", __func__, cmd); 281 } 282} 283 284void 285move_file(const char *dir, const char *fname, const char *tdir) 286{ 287 char from[FILENAME_MAX]; 288 char to[FILENAME_MAX]; 289 290 if (fname[0] == '/') 291 strncpy(from, fname, FILENAME_MAX); 292 else 293 snprintf(from, FILENAME_MAX, "%s/%s", dir, fname); 294 295 snprintf(to, FILENAME_MAX, "%s/%s", tdir, fname); 296 297 if (rename(from, to) == -1) { 298 if (vsystem("/bin/mv %s %s", from, to)) { 299 cleanup(0); 300 errx(2, "%s: could not move '%s' to '%s'", __func__, from, to); 301 } 302 } 303} 304 305/* 306 * Copy a hierarchy (possibly from dir) to the current directory, or 307 * if "to" is TRUE, from the current directory to a location someplace 308 * else. 309 * 310 * Though slower, using tar to copy preserves symlinks and everything 311 * without me having to write some big hairy routine to do it. 312 */ 313void 314copy_hierarchy(const char *dir, const char *fname, Boolean to) 315{ 316 char cmd[FILENAME_MAX * 3]; 317 318 if (!to) { 319 /* If absolute path, use it */ 320 if (*fname == '/') 321 dir = "/"; 322 snprintf(cmd, FILENAME_MAX * 3, "/usr/bin/tar cf - -C %s %s | /usr/bin/tar xpf -", 323 dir, fname); 324 } 325 else 326 snprintf(cmd, FILENAME_MAX * 3, "/usr/bin/tar cf - %s | /usr/bin/tar xpf - -C %s", 327 fname, dir); 328#ifdef DEBUG 329 printf("Using '%s' to copy trees.\n", cmd); 330#endif 331 if (system(cmd)) { 332 cleanup(0); 333 errx(2, "%s: could not perform '%s'", __func__, cmd); 334 } 335} 336 337/* Unpack a tar file */ 338int 339unpack(const char *pkg, const char *flist) 340{ 341 const char *comp, *cp; 342 char suff[80]; 343 344 comp = ""; 345 /* 346 * Figure out by a crude heuristic whether this or not this is probably 347 * compressed and whichever compression utility was used (gzip or bzip2). 348 */ 349 if (strcmp(pkg, "-")) { 350 cp = strrchr(pkg, '.'); 351 if (cp) { 352 strcpy(suff, cp + 1); 353 if (strchr(suff, 'z') || strchr(suff, 'Z')) { 354 if (strchr(suff, 'b')) 355 comp = "-j"; 356 else 357 comp = "-z"; 358 } 359 } 360 } 361 else 362 comp = "-j"; 363 if (vsystem("/usr/bin/tar -xp %s -f '%s' %s", comp, pkg, flist ? flist : "")) { 364 warnx("tar extract of %s failed!", pkg); 365 return 1; 366 } 367 return 0; 368} 369 370/* 371 * Using fmt, replace all instances of: 372 * 373 * %F With the parameter "name" 374 * %D With the parameter "dir" 375 * %B Return the directory part ("base") of %D/%F 376 * %f Return the filename part of %D/%F 377 * 378 * Does not check for overflow - caution! 379 * 380 */ 381void 382format_cmd(char *buf, int max, const char *fmt, const char *dir, const char *name) 383{ 384 char *cp, scratch[FILENAME_MAX * 2]; 385 int l; 386 387 while (*fmt && max > 0) { 388 if (*fmt == '%') { 389 switch (*++fmt) { 390 case 'F': 391 strncpy(buf, name, max); 392 l = strlen(name); 393 buf += l, max -= l; 394 break; 395 396 case 'D': 397 strncpy(buf, dir, max); 398 l = strlen(dir); 399 buf += l, max -= l; 400 break; 401 402 case 'B': 403 snprintf(scratch, FILENAME_MAX * 2, "%s/%s", dir, name); 404 cp = &scratch[strlen(scratch) - 1]; 405 while (cp != scratch && *cp != '/') 406 --cp; 407 *cp = '\0'; 408 strncpy(buf, scratch, max); 409 l = strlen(scratch); 410 buf += l, max -= l; 411 break; 412 413 case 'f': 414 snprintf(scratch, FILENAME_MAX * 2, "%s/%s", dir, name); 415 cp = &scratch[strlen(scratch) - 1]; 416 while (cp != scratch && *(cp - 1) != '/') 417 --cp; 418 strncpy(buf, cp, max); 419 l = strlen(cp); 420 buf += l, max -= l; 421 break; 422 423 default: 424 *buf++ = *fmt; 425 --max; 426 break; 427 } 428 ++fmt; 429 } 430 else { 431 *buf++ = *fmt++; 432 --max; 433 } 434 } 435 *buf = '\0'; 436} 437