2 * FreeBSD install - a package for the installation and maintainance
3 * of non-core utilities.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
17 * Miscellaneous file access utilities.
21 #include <sys/cdefs.h>
22 __FBSDID("$FreeBSD: src/usr.sbin/pkg_install/lib/file.c,v 1.40.2.14 2002/09/25 23:22:14 bmah Exp $");
31 /* Quick check to see if a file exists */
33 fexists(const char *fname)
36 if (!lstat(fname, &dummy))
41 /* Quick check to see if something is a directory or symlink to a directory */
43 isdir(const char *fname)
47 if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
49 else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode))
55 /* Check to see if file is a dir or symlink to a dir, and is empty */
57 isemptydir(const char *fname)
63 dirp = opendir(fname);
65 return FALSE; /* no perms, leave it alone */
66 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
67 if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
79 * Returns TRUE if file is a regular file or symlink pointing to a regular
83 isfile(const char *fname)
86 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
92 * Check to see if file is a file or symlink pointing to a file and is empty.
93 * If nonexistent or not a file, say "it's empty", otherwise return TRUE if
97 isemptyfile(const char *fname)
100 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
107 /* Returns TRUE if file is a symbolic link. */
109 issymlink(const char *fname)
112 if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode))
117 /* Returns TRUE if file is a URL specification */
119 isURL(const char *fname)
122 * I'm sure there are other types of URL specifications that I could
123 * also be looking for here, but for now I'll just be happy to get ftp
128 while (isspace(*fname))
130 if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7))
135 #define HOSTNAME_MAX 64
137 * Try and fetch a file by URL, returning the directory name for where
138 * it's unpacked, if successful.
141 fileGetURL(const char *base, const char *spec)
144 char fname[FILENAME_MAX];
145 char pen[FILENAME_MAX];
149 int pfd[2], pstat, r, w;
154 /* Special tip that sysinstall left for us */
155 hint = getenv("PKG_ADD_BASE");
160 * We've been given an existing URL (that's known-good) and now we need
161 * to construct a composite one out of that and the basename we were
162 * handed as a dependency.
167 * Advance back two slashes to get to the root of the package
170 cp = strrchr(fname, '/');
172 *cp = '\0'; /* chop name */
173 cp = strrchr(fname, '/');
186 * Otherwise, we've been given an environment variable hinting
187 * at the right location from sysinstall
191 strcat(fname, ".tgz");
197 if ((ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) {
198 printf("Error: FTP Unable to get %s: %s\n",
199 fname, fetchLastErrString);
203 if (isatty(0) || Verbose)
204 printf("Fetching %s...", fname), fflush(stdout);
206 if ((rp = make_playpen(pen, 0)) == NULL) {
207 printf("Error: Unable to construct a new playpen for FTP!\n");
211 if (pipe(pfd) == -1) {
216 if ((tpid = fork()) == -1) {
223 for (fd = getdtablesize() - 1; fd >= 3; --fd)
225 execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-",
231 if ((r = fread(buf, 1, sizeof buf, ftp)) < 1)
233 if ((w = write(pfd[1], buf, r)) != r)
237 warn("warning: error reading from server");
241 warn("warning: error writing to tar");
242 tpid = waitpid(tpid, &pstat, 0);
244 printf("tar command returns %d status\n", WEXITSTATUS(pstat));
245 if (rp && (isatty(0) || Verbose))
251 fileFindByPath(const char *base, const char *fname)
253 static char tmp[FILENAME_MAX];
255 const char *suffixes[] = {".tbz", ".tgz", ".tar", NULL};
258 if (fexists(fname) && isfile(fname)) {
265 cp = strrchr(tmp, '/');
267 *cp = '\0'; /* chop name */
268 cp = strrchr(tmp, '/');
271 for (i = 0; suffixes[i] != NULL; i++) {
275 strcat(cp, suffixes[i]);
281 cp = getenv("PKG_PATH");
283 char *cp2 = strsep(&cp, ":");
285 for (i = 0; suffixes[i] != NULL; i++) {
286 snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]);
287 if (fexists(tmp) && isfile(tmp))
295 fileGetContents(const char *fname)
301 if (stat(fname, &sb) == FAIL) {
303 errx(2, "%s: can't stat '%s'", __func__, fname);
306 contents = (char *)malloc(sb.st_size + 1);
307 fd = open(fname, O_RDONLY, 0);
310 errx(2, "%s: unable to open '%s' for reading", __func__, fname);
312 if (read(fd, contents, sb.st_size) != sb.st_size) {
314 errx(2, "%s: short read on '%s' - did not get %qd bytes", __func__,
315 fname, (long long)sb.st_size);
318 contents[sb.st_size] = '\0';
323 * Takes a filename and package name, returning (in "try") the
324 * canonical "preserve" name for it.
327 make_preserve_name(char *try, int max, const char *name, const char *file)
331 if ((len = strlen(file)) == 0)
335 strncpy(try, file, max);
336 if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */
341 strncpy(&try[i + 2], &file[i + 1], max - i - 2);
347 strncpy(try + 1, file, max - 1);
349 /* I should probably be called rude names for these inline assignments */
350 strncat(try, ".", max -= strlen(try));
351 strncat(try, name, max -= strlen(name));
352 strncat(try, ".", max--);
353 strncat(try, "backup", max -= 6);
357 /* Write the contents of "str" to a file */
359 write_file(const char *name, const char *str)
364 fp = fopen(name, "w");
367 errx(2, "%s: cannot fopen '%s' for writing", __func__, name);
370 if (fwrite(str, 1, len, fp) != len) {
372 errx(2, "%s: short fwrite on '%s', tried to write %ld bytes",
373 __func__, name, (long)len);
377 errx(2, "%s: failure to fclose '%s'", __func__, name);
382 copy_file(const char *dir, const char *fname, const char *to)
384 char cmd[FILENAME_MAX];
387 snprintf(cmd, FILENAME_MAX, "cp -r %s %s", fname, to);
389 snprintf(cmd, FILENAME_MAX, "cp -r %s/%s %s", dir, fname, to);
392 errx(2, "%s: could not perform '%s'", __func__, cmd);
397 move_file(const char *dir, const char *fname, const char *to)
399 char cmd[FILENAME_MAX];
402 snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to);
404 snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to);
407 errx(2, "%s: could not perform '%s'", __func__, cmd);
412 * Copy a hierarchy (possibly from dir) to the current directory, or
413 * if "to" is TRUE, from the current directory to a location someplace
416 * Though slower, using tar to copy preserves symlinks and everything
417 * without me having to write some big hairy routine to do it.
420 copy_hierarchy(const char *dir, const char *fname, Boolean to)
422 char cmd[FILENAME_MAX * 3];
425 /* If absolute path, use it */
428 snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -",
432 snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s",
435 printf("Using '%s' to copy trees.\n", cmd);
439 errx(2, "%s: could not perform '%s'", __func__, cmd);
443 /* Unpack a tar file */
445 unpack(const char *pkg, const char *flist)
447 char args[10], suff[80], *cp;
451 * Figure out by a crude heuristic whether this or not this is probably
452 * compressed and whichever compression utility was used (gzip or bzip2).
454 if (strcmp(pkg, "-")) {
455 cp = strrchr(pkg, '.');
457 strcpy(suff, cp + 1);
458 if (strchr(suff, 'z') || strchr(suff, 'Z')) {
459 if (strchr(suff, 'b'))
468 strcat(args, " -xpf");
469 if (vsystem("tar %s '%s' %s", args, pkg, flist ? flist : "")) {
470 warnx("tar extract of %s failed!", pkg);
477 * Using fmt, replace all instances of:
479 * %F With the parameter "name"
480 * %D With the parameter "dir"
481 * %B Return the directory part ("base") of %D/%F
482 * %f Return the filename part of %D/%F
484 * Does not check for overflow - caution!
488 format_cmd(char *buf, const char *fmt, const char *dir, const char *name)
490 char *cp, scratch[FILENAME_MAX * 2];
506 sprintf(scratch, "%s/%s", dir, name);
507 cp = &scratch[strlen(scratch) - 1];
508 while (cp != scratch && *cp != '/')
511 strcpy(buf, scratch);
512 buf += strlen(scratch);
516 sprintf(scratch, "%s/%s", dir, name);
517 cp = &scratch[strlen(scratch) - 1];
518 while (cp != scratch && *(cp - 1) != '/')