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 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 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 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 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 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 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 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 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 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 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 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