Home | History | Annotate | Line # | Download | only in lib
      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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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