xref: /minix/external/bsd/pkg_install/dist/lib/file.c (revision a824f5a1)
1 /*	$NetBSD: file.c,v 1.1.1.6 2011/02/18 22:32:30 aymeric Exp $	*/
2 
3 #if HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 #include <nbcompat.h>
7 #if HAVE_SYS_CDEFS_H
8 #include <sys/cdefs.h>
9 #endif
10 #if HAVE_SYS_PARAM_H
11 #include <sys/param.h>
12 #endif
13 #if HAVE_SYS_QUEUE_H
14 #include <sys/queue.h>
15 #endif
16 __RCSID("$NetBSD: file.c,v 1.1.1.6 2011/02/18 22:32:30 aymeric Exp $");
17 
18 /*
19  * FreeBSD install - a package for the installation and maintainance
20  * of non-core utilities.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the above copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  *
31  * Jordan K. Hubbard
32  * 18 July 1993
33  *
34  * Miscellaneous file access utilities.
35  *
36  */
37 
38 #include "lib.h"
39 
40 #if HAVE_SYS_WAIT_H
41 #include <sys/wait.h>
42 #endif
43 
44 #if HAVE_ASSERT_H
45 #include <assert.h>
46 #endif
47 #if HAVE_ERR_H
48 #include <err.h>
49 #endif
50 #if HAVE_GLOB_H
51 #include <glob.h>
52 #endif
53 #if HAVE_PWD_H
54 #include <pwd.h>
55 #endif
56 #if HAVE_TIME_H
57 #include <time.h>
58 #endif
59 #if HAVE_FCNTL_H
60 #include <fcntl.h>
61 #endif
62 
63 
64 /*
65  * Quick check to see if a file (or dir ...) exists
66  */
67 Boolean
fexists(const char * fname)68 fexists(const char *fname)
69 {
70 	struct stat dummy;
71 	if (!lstat(fname, &dummy))
72 		return TRUE;
73 	return FALSE;
74 }
75 
76 /*
77  * Quick check to see if something is a directory
78  */
79 Boolean
isdir(const char * fname)80 isdir(const char *fname)
81 {
82 	struct stat sb;
83 
84 	if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
85 		return TRUE;
86 	else
87 		return FALSE;
88 }
89 
90 /*
91  * Check if something is a link to a directory
92  */
93 Boolean
islinktodir(const char * fname)94 islinktodir(const char *fname)
95 {
96 	struct stat sb;
97 
98 	if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) {
99 		if (stat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
100 			return TRUE;	/* link to dir! */
101 		else
102 			return FALSE;	/* link to non-dir */
103 	} else
104 		return FALSE;	/* non-link */
105 }
106 
107 /*
108  * Check if something is a link that points to nonexistant target.
109  */
110 Boolean
isbrokenlink(const char * fname)111 isbrokenlink(const char *fname)
112 {
113 	struct stat sb;
114 
115 	if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) {
116 		if (stat(fname, &sb) != FAIL)
117 			return FALSE;	/* link target exists! */
118 		else
119 			return TRUE;	/* link target missing*/
120 	} else
121 		return FALSE;	/* non-link */
122 }
123 
124 /*
125  * Check to see if file is a dir, and is empty
126  */
127 Boolean
isemptydir(const char * fname)128 isemptydir(const char *fname)
129 {
130 	if (isdir(fname) || islinktodir(fname)) {
131 		DIR    *dirp;
132 		struct dirent *dp;
133 
134 		dirp = opendir(fname);
135 		if (!dirp)
136 			return FALSE;	/* no perms, leave it alone */
137 		for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
138 			if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
139 				closedir(dirp);
140 				return FALSE;
141 			}
142 		}
143 		(void) closedir(dirp);
144 		return TRUE;
145 	}
146 	return FALSE;
147 }
148 
149 /*
150  * Check if something is a regular file
151  */
152 Boolean
isfile(const char * fname)153 isfile(const char *fname)
154 {
155 	struct stat sb;
156 	if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
157 		return TRUE;
158 	return FALSE;
159 }
160 
161 /*
162  * Check to see if file is a file and is empty. If nonexistent or not
163  * a file, say "it's empty", otherwise return TRUE if zero sized.
164  */
165 Boolean
isemptyfile(const char * fname)166 isemptyfile(const char *fname)
167 {
168 	struct stat sb;
169 	if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
170 		if (sb.st_size != 0)
171 			return FALSE;
172 	}
173 	return TRUE;
174 }
175 
176 /* This struct defines the leading part of a valid URL name */
177 typedef struct url_t {
178 	const char *u_s;	/* the leading part of the URL */
179 	int     u_len;		/* its length */
180 }       url_t;
181 
182 /* A table of valid leading strings for URLs */
183 static const url_t urls[] = {
184 #define	STR_AND_SIZE(str)	{ str, sizeof(str) - 1 }
185 	STR_AND_SIZE("file://"),
186 	STR_AND_SIZE("ftp://"),
187 	STR_AND_SIZE("http://"),
188 	STR_AND_SIZE("https://"),
189 #undef STR_AND_SIZE
190 	{NULL, 0}
191 };
192 
193 /*
194  * Returns length of leading part of any URL from urls table, or -1
195  */
196 int
URLlength(const char * fname)197 URLlength(const char *fname)
198 {
199 	const url_t *up;
200 	int     i;
201 
202 	if (fname != (char *) NULL) {
203 		for (i = 0; isspace((unsigned char) *fname); i++) {
204 			fname++;
205 		}
206 		for (up = urls; up->u_s; up++) {
207 			if (strncmp(fname, up->u_s, up->u_len) == 0) {
208 				return i + up->u_len;    /* ... + sizeof(up->u_s);  - HF */
209 			}
210 		}
211 	}
212 	return -1;
213 }
214 
215 /*
216  * Takes a filename and package name, returning (in "try") the canonical
217  * "preserve" name for it.
218  */
219 Boolean
make_preserve_name(char * try,size_t max,const char * name,const char * file)220 make_preserve_name(char *try, size_t max, const char *name, const char *file)
221 {
222 	size_t len, i;
223 
224 	if ((len = strlen(file)) == 0)
225 		return FALSE;
226 	i = len - 1;
227 	strncpy(try, file, max);
228 	if (try[i] == '/')	/* Catch trailing slash early and save checking in the loop */
229 		--i;
230 	for (; i; i--) {
231 		if (try[i] == '/') {
232 			try[i + 1] = '.';
233 			strncpy(&try[i + 2], &file[i + 1], max - i - 2);
234 			break;
235 		}
236 	}
237 	if (!i) {
238 		try[0] = '.';
239 		strncpy(try + 1, file, max - 1);
240 	}
241 	/* I should probably be called rude names for these inline assignments */
242 	strncat(try, ".", max -= strlen(try));
243 	strncat(try, name, max -= strlen(name));
244 	strncat(try, ".", max--);
245 	strncat(try, "backup", max -= 6);
246 	return TRUE;
247 }
248 
249 void
remove_files(const char * path,const char * pattern)250 remove_files(const char *path, const char *pattern)
251 {
252 	char	fpath[MaxPathSize];
253 	glob_t	globbed;
254 	int	i;
255 	size_t  j;
256 
257 	(void) snprintf(fpath, sizeof(fpath), "%s/%s", path, pattern);
258 	if ((i=glob(fpath, GLOB_NOSORT, NULL, &globbed)) != 0) {
259 		switch(i) {
260 		case GLOB_NOMATCH:
261 			warn("no files matching ``%s'' found", fpath);
262 			break;
263 		case GLOB_ABORTED:
264 			warn("globbing aborted");
265 			break;
266 		case GLOB_NOSPACE:
267 			warn("out-of-memory during globbing");
268 			break;
269 		default:
270 			warn("unknown error during globbing");
271 			break;
272 		}
273 		return;
274 	}
275 
276 	/* deleting globbed files */
277 	for (j = 0; j < globbed.gl_pathc; j++)
278 		if (unlink(globbed.gl_pathv[j]) < 0)
279 			warn("can't delete ``%s''", globbed.gl_pathv[j]);
280 
281 	return;
282 }
283 
284 /*
285  * Using fmt, replace all instances of:
286  *
287  * %F	With the parameter "name"
288  * %D	With the parameter "dir"
289  * %B	Return the directory part ("base") of %D/%F
290  * %f	Return the filename part of %D/%F
291  *
292  * Check that no overflows can occur.
293  */
294 int
format_cmd(char * buf,size_t size,const char * fmt,const char * dir,const char * name)295 format_cmd(char *buf, size_t size, const char *fmt, const char *dir, const char *name)
296 {
297 	size_t  remaining, quoted;
298 	char   *bufp, *tmp;
299 	char   *cp;
300 
301 	for (bufp = buf, remaining = size; remaining > 1 && *fmt;) {
302 		if (*fmt != '%') {
303 			*bufp++ = *fmt++;
304 			--remaining;
305 			continue;
306 		}
307 
308 		if (*++fmt != 'D' && name == NULL) {
309 			warnx("no last file available for '%s' command", buf);
310 			return -1;
311 		}
312 		switch (*fmt) {
313 		case 'F':
314 			quoted = shquote(name, bufp, remaining);
315 			if (quoted >= remaining) {
316 				warnx("overflow during quoting");
317 				return -1;
318 			}
319 			bufp += quoted;
320 			remaining -= quoted;
321 			break;
322 
323 		case 'D':
324 			quoted = shquote(dir, bufp, remaining);
325 			if (quoted >= remaining) {
326 				warnx("overflow during quoting");
327 				return -1;
328 			}
329 			bufp += quoted;
330 			remaining -= quoted;
331 			break;
332 
333 		case 'B':
334 			tmp = xasprintf("%s/%s", dir, name);
335 			cp = strrchr(tmp, '/');
336 			*cp = '\0';
337 			quoted = shquote(tmp, bufp, remaining);
338 			free(tmp);
339 			if (quoted >= remaining) {
340 				warnx("overflow during quoting");
341 				return -1;
342 			}
343 			bufp += quoted;
344 			remaining -= quoted;
345 			break;
346 
347 		case 'f':
348 			tmp = xasprintf("%s/%s", dir, name);
349 			cp = strrchr(tmp, '/') + 1;
350 			quoted = shquote(cp, bufp, remaining);
351 			free(tmp);
352 			if (quoted >= remaining) {
353 				warnx("overflow during quoting");
354 				return -1;
355 			}
356 			bufp += quoted;
357 			remaining -= quoted;
358 			break;
359 
360 		default:
361 			if (remaining == 1) {
362 				warnx("overflow during quoting");
363 				return -1;
364 			}
365 			*bufp++ = '%';
366 			*bufp++ = *fmt;
367 			remaining -= 2;
368 			break;
369 		}
370 		++fmt;
371 	}
372 	*bufp = '\0';
373 	return 0;
374 }
375