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