1 /* $NetBSD: file.c,v 1.1.1.5 2009/10/07 13:19:43 joerg 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.5 2009/10/07 13:19:43 joerg 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 {"file://", 7}, 185 {"ftp://", 6}, 186 {"http://", 7}, 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