1327Sjkh/* 2327Sjkh * FreeBSD install - a package for the installation and maintainance 3327Sjkh * of non-core utilities. 4327Sjkh * 5327Sjkh * Redistribution and use in source and binary forms, with or without 6327Sjkh * modification, are permitted provided that the following conditions 7327Sjkh * are met: 8327Sjkh * 1. Redistributions of source code must retain the above copyright 9327Sjkh * notice, this list of conditions and the following disclaimer. 10327Sjkh * 2. Redistributions in binary form must reproduce the above copyright 11327Sjkh * notice, this list of conditions and the following disclaimer in the 12327Sjkh * documentation and/or other materials provided with the distribution. 13327Sjkh * 14327Sjkh * Jordan K. Hubbard 15327Sjkh * 18 July 1993 16327Sjkh * 17327Sjkh * Miscellaneous file access utilities. 18327Sjkh * 19327Sjkh */ 20327Sjkh 2193520Sobrien#include <sys/cdefs.h> 2293520Sobrien__FBSDID("$FreeBSD$"); 2393520Sobrien 24327Sjkh#include "lib.h" 2530221Scharnier#include <err.h> 268087Sjkh#include <pwd.h> 279838Sjkh#include <time.h> 2816549Sjkh#include <sys/wait.h> 29327Sjkh 30327Sjkh/* Quick check to see if a file exists */ 31327SjkhBoolean 3284745Ssobomaxfexists(const char *fname) 33327Sjkh{ 34206043Sflz int fd; 35206043Sflz 36206043Sflz if ((fd = open(fname, O_RDONLY)) == -1) 37206043Sflz return FALSE; 38206043Sflz 39206043Sflz close(fd); 40206043Sflz return TRUE; 41327Sjkh} 42327Sjkh 4366021Ssobomax/* Quick check to see if something is a directory or symlink to a directory */ 44327SjkhBoolean 4584745Ssobomaxisdir(const char *fname) 46327Sjkh{ 47327Sjkh struct stat sb; 48327Sjkh 4957034Sobrien if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) 50327Sjkh return TRUE; 5181058Sjon else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode)) 5257331Sjkh return TRUE; 53327Sjkh else 54327Sjkh return FALSE; 55327Sjkh} 56327Sjkh 5766021Ssobomax/* Check to see if file is a dir or symlink to a dir, and is empty */ 58327SjkhBoolean 5984745Ssobomaxisemptydir(const char *fname) 60327Sjkh{ 61327Sjkh if (isdir(fname)) { 62327Sjkh DIR *dirp; 63327Sjkh struct dirent *dp; 64327Sjkh 65327Sjkh dirp = opendir(fname); 66327Sjkh if (!dirp) 67327Sjkh return FALSE; /* no perms, leave it alone */ 68327Sjkh for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 69327Sjkh if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { 70327Sjkh closedir(dirp); 71327Sjkh return FALSE; 72327Sjkh } 73327Sjkh } 74327Sjkh (void)closedir(dirp); 75327Sjkh return TRUE; 76327Sjkh } 77327Sjkh return FALSE; 78327Sjkh} 79327Sjkh 8066021Ssobomax/* 8166021Ssobomax * Returns TRUE if file is a regular file or symlink pointing to a regular 8266021Ssobomax * file 8366021Ssobomax */ 849782SacheBoolean 8584745Ssobomaxisfile(const char *fname) 869782Sache{ 879782Sache struct stat sb; 889782Sache if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) 899782Sache return TRUE; 909782Sache return FALSE; 919782Sache} 929782Sache 93106136Sdes/* 9466021Ssobomax * Check to see if file is a file or symlink pointing to a file and is empty. 9566021Ssobomax * If nonexistent or not a file, say "it's empty", otherwise return TRUE if 9666021Ssobomax * zero sized. 9766021Ssobomax */ 984996SjkhBoolean 9984745Ssobomaxisemptyfile(const char *fname) 1004996Sjkh{ 1014996Sjkh struct stat sb; 1024996Sjkh if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) { 1034996Sjkh if (sb.st_size != 0) 1044996Sjkh return FALSE; 1054996Sjkh } 1064996Sjkh return TRUE; 1074996Sjkh} 1084996Sjkh 10966021Ssobomax/* Returns TRUE if file is a symbolic link. */ 11066021SsobomaxBoolean 11184745Ssobomaxissymlink(const char *fname) 11266021Ssobomax{ 11366021Ssobomax struct stat sb; 11466021Ssobomax if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) 11566021Ssobomax return TRUE; 11666021Ssobomax return FALSE; 11766021Ssobomax} 11866021Ssobomax 1198087Sjkh/* Returns TRUE if file is a URL specification */ 1208087SjkhBoolean 12184745SsobomaxisURL(const char *fname) 1228087Sjkh{ 1238087Sjkh /* 1248087Sjkh * I'm sure there are other types of URL specifications that I could 1258087Sjkh * also be looking for here, but for now I'll just be happy to get ftp 12662154Sdes * and http working. 1278087Sjkh */ 12811780Sjkh if (!fname) 12911780Sjkh return FALSE; 1308087Sjkh while (isspace(*fname)) 1318087Sjkh ++fname; 132131280Seik if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7) || 133131280Seik !strncmp(fname, "https://", 8) || !strncmp(fname, "file://", 7)) 1348087Sjkh return TRUE; 1358087Sjkh return FALSE; 1368087Sjkh} 1378087Sjkh 1388087Sjkhchar * 13984745SsobomaxfileFindByPath(const char *base, const char *fname) 1408087Sjkh{ 1418087Sjkh static char tmp[FILENAME_MAX]; 1428087Sjkh char *cp; 143236462Sjpaetzel const char *suffixes[] = {".tbz", ".tgz", ".tar", ".txz", NULL}; 14471373Ssobomax int i; 1458087Sjkh 1469782Sache if (fexists(fname) && isfile(fname)) { 1478424Sjkh strcpy(tmp, fname); 1488424Sjkh return tmp; 1498424Sjkh } 15011780Sjkh if (base) { 15111780Sjkh strcpy(tmp, base); 15211780Sjkh 15330533Sjkh cp = strrchr(tmp, '/'); 15411780Sjkh if (cp) { 15512219Sjkh *cp = '\0'; /* chop name */ 15630533Sjkh cp = strrchr(tmp, '/'); 15711780Sjkh } 15871373Ssobomax if (cp) 15971373Ssobomax for (i = 0; suffixes[i] != NULL; i++) { 16071373Ssobomax *(cp + 1) = '\0'; 16171373Ssobomax strcat(cp, "All/"); 16271373Ssobomax strcat(cp, fname); 16371373Ssobomax strcat(cp, suffixes[i]); 16471373Ssobomax if (fexists(tmp)) 16571373Ssobomax return tmp; 16671373Ssobomax } 16711780Sjkh } 16811780Sjkh 1698087Sjkh cp = getenv("PKG_PATH"); 1708087Sjkh while (cp) { 1718087Sjkh char *cp2 = strsep(&cp, ":"); 1728857Srgrimes 17371373Ssobomax for (i = 0; suffixes[i] != NULL; i++) { 17471373Ssobomax snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]); 17571373Ssobomax if (fexists(tmp) && isfile(tmp)) 17671373Ssobomax return tmp; 17771373Ssobomax } 1788087Sjkh } 1798424Sjkh return NULL; 1808087Sjkh} 1818087Sjkh 1828087Sjkhchar * 18384745SsobomaxfileGetContents(const char *fname) 1848087Sjkh{ 185327Sjkh char *contents; 186327Sjkh struct stat sb; 187327Sjkh int fd; 188327Sjkh 18939068Sjkh if (stat(fname, &sb) == FAIL) { 19039068Sjkh cleanup(0); 19196392Salfred errx(2, "%s: can't stat '%s'", __func__, fname); 19239068Sjkh } 193327Sjkh 194327Sjkh contents = (char *)malloc(sb.st_size + 1); 195327Sjkh fd = open(fname, O_RDONLY, 0); 19639068Sjkh if (fd == FAIL) { 19739068Sjkh cleanup(0); 19896392Salfred errx(2, "%s: unable to open '%s' for reading", __func__, fname); 19939068Sjkh } 20039068Sjkh if (read(fd, contents, sb.st_size) != sb.st_size) { 20139068Sjkh cleanup(0); 202132799Sstefanf errx(2, "%s: short read on '%s' - did not get %lld bytes", __func__, 20384745Ssobomax fname, (long long)sb.st_size); 20439068Sjkh } 205327Sjkh close(fd); 206327Sjkh contents[sb.st_size] = '\0'; 207327Sjkh return contents; 208327Sjkh} 209327Sjkh 21076739Ssobomax/* 21176739Ssobomax * Takes a filename and package name, returning (in "try") the 21276739Ssobomax * canonical "preserve" name for it. 21327137Sjkh */ 21427137SjkhBoolean 21584745Ssobomaxmake_preserve_name(char *try, int max, const char *name, const char *file) 21627137Sjkh{ 21727137Sjkh int len, i; 21827137Sjkh 21927137Sjkh if ((len = strlen(file)) == 0) 22027137Sjkh return FALSE; 22127137Sjkh else 22227137Sjkh i = len - 1; 22327137Sjkh strncpy(try, file, max); 22427137Sjkh if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */ 22527137Sjkh --i; 22627137Sjkh for (; i; i--) { 22727137Sjkh if (try[i] == '/') { 22827137Sjkh try[i + 1]= '.'; 22927137Sjkh strncpy(&try[i + 2], &file[i + 1], max - i - 2); 23027137Sjkh break; 23127137Sjkh } 23227137Sjkh } 23327137Sjkh if (!i) { 23427137Sjkh try[0] = '.'; 23527137Sjkh strncpy(try + 1, file, max - 1); 23627137Sjkh } 23727137Sjkh /* I should probably be called rude names for these inline assignments */ 23827137Sjkh strncat(try, ".", max -= strlen(try)); 23927137Sjkh strncat(try, name, max -= strlen(name)); 24027137Sjkh strncat(try, ".", max--); 24127137Sjkh strncat(try, "backup", max -= 6); 24227137Sjkh return TRUE; 24327137Sjkh} 24427137Sjkh 245327Sjkh/* Write the contents of "str" to a file */ 246327Sjkhvoid 24784745Ssobomaxwrite_file(const char *name, const char *str) 248327Sjkh{ 249327Sjkh FILE *fp; 25084745Ssobomax size_t len; 251327Sjkh 252327Sjkh fp = fopen(name, "w"); 25339068Sjkh if (!fp) { 25439068Sjkh cleanup(0); 25596392Salfred errx(2, "%s: cannot fopen '%s' for writing", __func__, name); 25639068Sjkh } 257327Sjkh len = strlen(str); 25839068Sjkh if (fwrite(str, 1, len, fp) != len) { 25939068Sjkh cleanup(0); 26096388Salfred errx(2, "%s: short fwrite on '%s', tried to write %ld bytes", 26196392Salfred __func__, name, (long)len); 26239068Sjkh } 26339068Sjkh if (fclose(fp)) { 26439068Sjkh cleanup(0); 26596392Salfred errx(2, "%s: failure to fclose '%s'", __func__, name); 26639068Sjkh } 267327Sjkh} 268327Sjkh 269327Sjkhvoid 27084745Ssobomaxcopy_file(const char *dir, const char *fname, const char *to) 271327Sjkh{ 272404Sjkh char cmd[FILENAME_MAX]; 273404Sjkh 274404Sjkh if (fname[0] == '/') 275131285Seik snprintf(cmd, FILENAME_MAX, "/bin/cp -r %s %s", fname, to); 276404Sjkh else 277131285Seik snprintf(cmd, FILENAME_MAX, "/bin/cp -r %s/%s %s", dir, fname, to); 27839068Sjkh if (vsystem(cmd)) { 27939068Sjkh cleanup(0); 28096392Salfred errx(2, "%s: could not perform '%s'", __func__, cmd); 28139068Sjkh } 282327Sjkh} 283327Sjkh 2847999Sjkhvoid 285206043Sflzmove_file(const char *dir, const char *fname, const char *tdir) 2867999Sjkh{ 287206043Sflz char from[FILENAME_MAX]; 288206043Sflz char to[FILENAME_MAX]; 2897999Sjkh 2907999Sjkh if (fname[0] == '/') 291206043Sflz strncpy(from, fname, FILENAME_MAX); 2927999Sjkh else 293206043Sflz snprintf(from, FILENAME_MAX, "%s/%s", dir, fname); 294206043Sflz 295206043Sflz snprintf(to, FILENAME_MAX, "%s/%s", tdir, fname); 296206043Sflz 297206043Sflz if (rename(from, to) == -1) { 298206043Sflz if (vsystem("/bin/mv %s %s", from, to)) { 299206043Sflz cleanup(0); 300206043Sflz errx(2, "%s: could not move '%s' to '%s'", __func__, from, to); 301206043Sflz } 30239068Sjkh } 3037999Sjkh} 3047999Sjkh 305327Sjkh/* 306327Sjkh * Copy a hierarchy (possibly from dir) to the current directory, or 307327Sjkh * if "to" is TRUE, from the current directory to a location someplace 308327Sjkh * else. 309327Sjkh * 310327Sjkh * Though slower, using tar to copy preserves symlinks and everything 311327Sjkh * without me having to write some big hairy routine to do it. 312327Sjkh */ 313327Sjkhvoid 31484745Ssobomaxcopy_hierarchy(const char *dir, const char *fname, Boolean to) 315327Sjkh{ 316327Sjkh char cmd[FILENAME_MAX * 3]; 317327Sjkh 318327Sjkh if (!to) { 319327Sjkh /* If absolute path, use it */ 320327Sjkh if (*fname == '/') 321327Sjkh dir = "/"; 322131285Seik snprintf(cmd, FILENAME_MAX * 3, "/usr/bin/tar cf - -C %s %s | /usr/bin/tar xpf -", 323106136Sdes dir, fname); 324327Sjkh } 325327Sjkh else 326131285Seik snprintf(cmd, FILENAME_MAX * 3, "/usr/bin/tar cf - %s | /usr/bin/tar xpf - -C %s", 327106136Sdes fname, dir); 328383Sjkh#ifdef DEBUG 329383Sjkh printf("Using '%s' to copy trees.\n", cmd); 330383Sjkh#endif 33139068Sjkh if (system(cmd)) { 33239068Sjkh cleanup(0); 33396392Salfred errx(2, "%s: could not perform '%s'", __func__, cmd); 33439068Sjkh } 335327Sjkh} 336327Sjkh 337327Sjkh/* Unpack a tar file */ 338327Sjkhint 33984745Ssobomaxunpack(const char *pkg, const char *flist) 340327Sjkh{ 341131280Seik const char *comp, *cp; 342131280Seik char suff[80]; 343327Sjkh 344128026Skientzle comp = ""; 345327Sjkh /* 346327Sjkh * Figure out by a crude heuristic whether this or not this is probably 34771373Ssobomax * compressed and whichever compression utility was used (gzip or bzip2). 348327Sjkh */ 34914582Sjkh if (strcmp(pkg, "-")) { 35067429Sjkh cp = strrchr(pkg, '.'); 35114582Sjkh if (cp) { 35284745Ssobomax strcpy(suff, cp + 1); 35384745Ssobomax if (strchr(suff, 'z') || strchr(suff, 'Z')) { 35484745Ssobomax if (strchr(suff, 'b')) 355128026Skientzle comp = "-j"; 35671373Ssobomax else 357128026Skientzle comp = "-z"; 35871373Ssobomax } 35914582Sjkh } 360327Sjkh } 36114582Sjkh else 362128026Skientzle comp = "-j"; 363131285Seik if (vsystem("/usr/bin/tar -xp %s -f '%s' %s", comp, pkg, flist ? flist : "")) { 36430221Scharnier warnx("tar extract of %s failed!", pkg); 365327Sjkh return 1; 366327Sjkh } 367327Sjkh return 0; 368327Sjkh} 369479Sjkh 37076739Ssobomax/* 37176739Ssobomax * Using fmt, replace all instances of: 3728857Srgrimes * 373479Sjkh * %F With the parameter "name" 374479Sjkh * %D With the parameter "dir" 375479Sjkh * %B Return the directory part ("base") of %D/%F 376479Sjkh * %f Return the filename part of %D/%F 377479Sjkh * 378479Sjkh * Does not check for overflow - caution! 379479Sjkh * 380479Sjkh */ 381479Sjkhvoid 382108778Sjkhformat_cmd(char *buf, int max, const char *fmt, const char *dir, const char *name) 383479Sjkh{ 384479Sjkh char *cp, scratch[FILENAME_MAX * 2]; 385108778Sjkh int l; 386479Sjkh 387108778Sjkh while (*fmt && max > 0) { 388479Sjkh if (*fmt == '%') { 389479Sjkh switch (*++fmt) { 390479Sjkh case 'F': 391108778Sjkh strncpy(buf, name, max); 392108778Sjkh l = strlen(name); 393108778Sjkh buf += l, max -= l; 394479Sjkh break; 395479Sjkh 396479Sjkh case 'D': 397108778Sjkh strncpy(buf, dir, max); 398108778Sjkh l = strlen(dir); 399108778Sjkh buf += l, max -= l; 400479Sjkh break; 401479Sjkh 402479Sjkh case 'B': 403108778Sjkh snprintf(scratch, FILENAME_MAX * 2, "%s/%s", dir, name); 404479Sjkh cp = &scratch[strlen(scratch) - 1]; 405479Sjkh while (cp != scratch && *cp != '/') 406479Sjkh --cp; 407479Sjkh *cp = '\0'; 408108778Sjkh strncpy(buf, scratch, max); 409108778Sjkh l = strlen(scratch); 410108778Sjkh buf += l, max -= l; 411479Sjkh break; 412479Sjkh 413479Sjkh case 'f': 414108778Sjkh snprintf(scratch, FILENAME_MAX * 2, "%s/%s", dir, name); 415479Sjkh cp = &scratch[strlen(scratch) - 1]; 416479Sjkh while (cp != scratch && *(cp - 1) != '/') 417479Sjkh --cp; 418108778Sjkh strncpy(buf, cp, max); 419108778Sjkh l = strlen(cp); 420108778Sjkh buf += l, max -= l; 421479Sjkh break; 422479Sjkh 423479Sjkh default: 424479Sjkh *buf++ = *fmt; 425108778Sjkh --max; 426479Sjkh break; 427479Sjkh } 428479Sjkh ++fmt; 429479Sjkh } 430108778Sjkh else { 431479Sjkh *buf++ = *fmt++; 432108778Sjkh --max; 433108778Sjkh } 434479Sjkh } 435479Sjkh *buf = '\0'; 436479Sjkh} 437