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.
19 * $FreeBSD: src/usr.sbin/pkg_install/lib/file.c,v 1.40.2.14 2002/09/25 23:22:14 bmah Exp $
20 * $DragonFly: src/usr.sbin/pkg_install/lib/Attic/file.c,v 1.2 2003/06/17 04:29:59 dillon Exp $
30 /* Quick check to see if a file exists */
32 fexists(const char *fname)
35 if (!lstat(fname, &dummy))
40 /* Quick check to see if something is a directory or symlink to a directory */
42 isdir(const char *fname)
46 if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
48 else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode))
54 /* Check to see if file is a dir or symlink to a dir, and is empty */
56 isemptydir(const char *fname)
62 dirp = opendir(fname);
64 return FALSE; /* no perms, leave it alone */
65 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
66 if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
78 * Returns TRUE if file is a regular file or symlink pointing to a regular
82 isfile(const char *fname)
85 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
91 * Check to see if file is a file or symlink pointing to a file and is empty.
92 * If nonexistent or not a file, say "it's empty", otherwise return TRUE if
96 isemptyfile(const char *fname)
99 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
106 /* Returns TRUE if file is a symbolic link. */
108 issymlink(const char *fname)
111 if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode))
116 /* Returns TRUE if file is a URL specification */
118 isURL(const char *fname)
121 * I'm sure there are other types of URL specifications that I could
122 * also be looking for here, but for now I'll just be happy to get ftp
127 while (isspace(*fname))
129 if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7))
134 #define HOSTNAME_MAX 64
136 * Try and fetch a file by URL, returning the directory name for where
137 * it's unpacked, if successful.
140 fileGetURL(const char *base, const char *spec)
143 char fname[FILENAME_MAX];
144 char pen[FILENAME_MAX];
148 int pfd[2], pstat, r, w;
153 /* Special tip that sysinstall left for us */
154 hint = getenv("PKG_ADD_BASE");
159 * We've been given an existing URL (that's known-good) and now we need
160 * to construct a composite one out of that and the basename we were
161 * handed as a dependency.
166 * Advance back two slashes to get to the root of the package
169 cp = strrchr(fname, '/');
171 *cp = '\0'; /* chop name */
172 cp = strrchr(fname, '/');
185 * Otherwise, we've been given an environment variable hinting
186 * at the right location from sysinstall
190 strcat(fname, ".tgz");
196 if ((ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) {
197 printf("Error: FTP Unable to get %s: %s\n",
198 fname, fetchLastErrString);
202 if (isatty(0) || Verbose)
203 printf("Fetching %s...", fname), fflush(stdout);
205 if ((rp = make_playpen(pen, 0)) == NULL) {
206 printf("Error: Unable to construct a new playpen for FTP!\n");
210 if (pipe(pfd) == -1) {
215 if ((tpid = fork()) == -1) {
222 for (fd = getdtablesize() - 1; fd >= 3; --fd)
224 execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-",
230 if ((r = fread(buf, 1, sizeof buf, ftp)) < 1)
232 if ((w = write(pfd[1], buf, r)) != r)
236 warn("warning: error reading from server");
240 warn("warning: error writing to tar");
241 tpid = waitpid(tpid, &pstat, 0);
243 printf("tar command returns %d status\n", WEXITSTATUS(pstat));
244 if (rp && (isatty(0) || Verbose))
250 fileFindByPath(const char *base, const char *fname)
252 static char tmp[FILENAME_MAX];
254 const char *suffixes[] = {".tbz", ".tgz", ".tar", NULL};
257 if (fexists(fname) && isfile(fname)) {
264 cp = strrchr(tmp, '/');
266 *cp = '\0'; /* chop name */
267 cp = strrchr(tmp, '/');
270 for (i = 0; suffixes[i] != NULL; i++) {
274 strcat(cp, suffixes[i]);
280 cp = getenv("PKG_PATH");
282 char *cp2 = strsep(&cp, ":");
284 for (i = 0; suffixes[i] != NULL; i++) {
285 snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]);
286 if (fexists(tmp) && isfile(tmp))
294 fileGetContents(const char *fname)
300 if (stat(fname, &sb) == FAIL) {
302 errx(2, "%s: can't stat '%s'", __func__, fname);
305 contents = (char *)malloc(sb.st_size + 1);
306 fd = open(fname, O_RDONLY, 0);
309 errx(2, "%s: unable to open '%s' for reading", __func__, fname);
311 if (read(fd, contents, sb.st_size) != sb.st_size) {
313 errx(2, "%s: short read on '%s' - did not get %qd bytes", __func__,
314 fname, (long long)sb.st_size);
317 contents[sb.st_size] = '\0';
322 * Takes a filename and package name, returning (in "try") the
323 * canonical "preserve" name for it.
326 make_preserve_name(char *try, int max, const char *name, const char *file)
330 if ((len = strlen(file)) == 0)
334 strncpy(try, file, max);
335 if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */
340 strncpy(&try[i + 2], &file[i + 1], max - i - 2);
346 strncpy(try + 1, file, max - 1);
348 /* I should probably be called rude names for these inline assignments */
349 strncat(try, ".", max -= strlen(try));
350 strncat(try, name, max -= strlen(name));
351 strncat(try, ".", max--);
352 strncat(try, "backup", max -= 6);
356 /* Write the contents of "str" to a file */
358 write_file(const char *name, const char *str)
363 fp = fopen(name, "w");
366 errx(2, "%s: cannot fopen '%s' for writing", __func__, name);
369 if (fwrite(str, 1, len, fp) != len) {
371 errx(2, "%s: short fwrite on '%s', tried to write %ld bytes",
372 __func__, name, (long)len);
376 errx(2, "%s: failure to fclose '%s'", __func__, name);
381 copy_file(const char *dir, const char *fname, const char *to)
383 char cmd[FILENAME_MAX];
386 snprintf(cmd, FILENAME_MAX, "cp -r %s %s", fname, to);
388 snprintf(cmd, FILENAME_MAX, "cp -r %s/%s %s", dir, fname, to);
391 errx(2, "%s: could not perform '%s'", __func__, cmd);
396 move_file(const char *dir, const char *fname, const char *to)
398 char cmd[FILENAME_MAX];
401 snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to);
403 snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to);
406 errx(2, "%s: could not perform '%s'", __func__, cmd);
411 * Copy a hierarchy (possibly from dir) to the current directory, or
412 * if "to" is TRUE, from the current directory to a location someplace
415 * Though slower, using tar to copy preserves symlinks and everything
416 * without me having to write some big hairy routine to do it.
419 copy_hierarchy(const char *dir, const char *fname, Boolean to)
421 char cmd[FILENAME_MAX * 3];
424 /* If absolute path, use it */
427 snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -",
431 snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s",
434 printf("Using '%s' to copy trees.\n", cmd);
438 errx(2, "%s: could not perform '%s'", __func__, cmd);
442 /* Unpack a tar file */
444 unpack(const char *pkg, const char *flist)
446 char args[10], suff[80], *cp;
450 * Figure out by a crude heuristic whether this or not this is probably
451 * compressed and whichever compression utility was used (gzip or bzip2).
453 if (strcmp(pkg, "-")) {
454 cp = strrchr(pkg, '.');
456 strcpy(suff, cp + 1);
457 if (strchr(suff, 'z') || strchr(suff, 'Z')) {
458 if (strchr(suff, 'b'))
467 strcat(args, " -xpf");
468 if (vsystem("tar %s '%s' %s", args, pkg, flist ? flist : "")) {
469 warnx("tar extract of %s failed!", pkg);
476 * Using fmt, replace all instances of:
478 * %F With the parameter "name"
479 * %D With the parameter "dir"
480 * %B Return the directory part ("base") of %D/%F
481 * %f Return the filename part of %D/%F
483 * Does not check for overflow - caution!
487 format_cmd(char *buf, const char *fmt, const char *dir, const char *name)
489 char *cp, scratch[FILENAME_MAX * 2];
505 sprintf(scratch, "%s/%s", dir, name);
506 cp = &scratch[strlen(scratch) - 1];
507 while (cp != scratch && *cp != '/')
510 strcpy(buf, scratch);
511 buf += strlen(scratch);
515 sprintf(scratch, "%s/%s", dir, name);
516 cp = &scratch[strlen(scratch) - 1];
517 while (cp != scratch && *(cp - 1) != '/')