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