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