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