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