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