1 /* $OpenBSD: fileio.c,v 1.36 2003/05/08 12:37:13 vincent Exp $ */ 2 3 /* 4 * POSIX fileio.c 5 */ 6 #include "def.h" 7 8 static FILE *ffp; 9 10 #include <sys/types.h> 11 #include <limits.h> 12 #include <sys/stat.h> 13 #include <sys/dir.h> 14 #include <string.h> 15 #include <fcntl.h> 16 #include <unistd.h> 17 18 /* 19 * Open a file for reading. 20 */ 21 int 22 ffropen(const char *fn, BUFFER *bp) 23 { 24 struct stat statbuf; 25 26 if ((ffp = fopen(fn, "r")) == NULL) 27 return (FIOFNF); 28 if (bp && fstat(fileno(ffp), &statbuf) == 0) { 29 /* set highorder bit to make sure this isn't all zero */ 30 bp->b_fi.fi_mode = statbuf.st_mode | 0x8000; 31 bp->b_fi.fi_uid = statbuf.st_uid; 32 bp->b_fi.fi_gid = statbuf.st_gid; 33 } 34 return (FIOSUC); 35 } 36 37 /* 38 * Open a file for writing. 39 * Return TRUE if all is well, and 40 * FALSE on error (cannot create). 41 */ 42 int 43 ffwopen(const char *fn, BUFFER *bp) 44 { 45 int fd; 46 mode_t mode = DEFFILEMODE; 47 48 if (bp && bp->b_fi.fi_mode) 49 mode = bp->b_fi.fi_mode & 07777; 50 51 fd = open(fn, O_RDWR | O_CREAT | O_TRUNC, mode); 52 if (fd == -1) { 53 ffp = NULL; 54 ewprintf("Cannot open file for writing : %s", strerror(errno)); 55 return (FIOERR); 56 } 57 58 if ((ffp = fdopen(fd, "w")) == NULL) { 59 ewprintf("Cannot open file for writing : %s", strerror(errno)); 60 close(fd); 61 return (FIOERR); 62 } 63 64 /* 65 * If we have file information, use it. We don't bother to check for 66 * errors, because there's no a lot we can do about it. Certainly 67 * trying to change ownership will fail if we aren't root. That's 68 * probably OK. If we don't have info, no need to get it, since any 69 * future writes will do the same thing. 70 */ 71 if (bp && bp->b_fi.fi_mode) { 72 fchmod(fd, bp->b_fi.fi_mode & 07777); 73 fchown(fd, bp->b_fi.fi_uid, bp->b_fi.fi_gid); 74 } 75 return (FIOSUC); 76 } 77 78 /* 79 * Close a file. 80 * XXX - Should look at the status. 81 */ 82 /* ARGSUSED */ 83 int 84 ffclose(BUFFER *bp) 85 { 86 87 (void) fclose(ffp); 88 return (FIOSUC); 89 } 90 91 /* 92 * Write a buffer to the already 93 * opened file. bp points to the 94 * buffer. Return the status. 95 * Check only at the newline and 96 * end of buffer. 97 */ 98 int 99 ffputbuf(BUFFER *bp) 100 { 101 char *cp; 102 char *cpend; 103 LINE *lp; 104 LINE *lpend; 105 106 lpend = bp->b_linep; 107 lp = lforw(lpend); 108 do { 109 cp = <ext(lp)[0]; /* beginning of line */ 110 cpend = &cp[llength(lp)]; /* end of line */ 111 while (cp != cpend) { 112 putc(*cp, ffp); 113 cp++; /* putc may evaluate arguments 114 more than once */ 115 } 116 lp = lforw(lp); 117 if (lp == lpend) 118 break; /* no implied \n on last line */ 119 putc('\n', ffp); 120 } while (!ferror(ffp)); 121 if (ferror(ffp)) { 122 ewprintf("Write I/O error"); 123 return FIOERR; 124 } 125 return (FIOSUC); 126 } 127 128 /* 129 * Read a line from a file, and store the bytes 130 * in the supplied buffer. Stop on end of file or end of 131 * line. When FIOEOF is returned, there is a valid line 132 * of data without the normally implied \n. 133 */ 134 int 135 ffgetline(char *buf, int nbuf, int *nbytes) 136 { 137 int c, i; 138 139 i = 0; 140 while ((c = getc(ffp)) != EOF && c != '\n') { 141 buf[i++] = c; 142 if (i >= nbuf) 143 return FIOLONG; 144 } 145 if (c == EOF && ferror(ffp) != FALSE) { 146 ewprintf("File read error"); 147 return FIOERR; 148 } 149 *nbytes = i; 150 return c == EOF ? FIOEOF : FIOSUC; 151 } 152 153 #ifndef NO_BACKUP 154 /* 155 * Make a backup copy of "fname". On Unix the backup has the same 156 * name as the original file, with a "~" on the end; this seems to 157 * be newest of the new-speak. The error handling is all in "file.c". 158 * We do a copy instead of a rename since otherwise another process 159 * with an open fd will get the backup, not the new file. This is 160 * a problem when using mg with things like crontab and vipw. 161 */ 162 int 163 fbackupfile(const char *fn) 164 { 165 struct stat sb; 166 int from, to, serrno; 167 size_t nread; 168 char buf[BUFSIZ]; 169 char *nname; 170 171 if (stat(fn, &sb) == -1) { 172 ewprintf("Can't stat %s : %s", fn, strerror(errno)); 173 return (FALSE); 174 } 175 176 if (asprintf(&nname, "%s~", fn) == -1) { 177 ewprintf("Can't allocate temp file name : %s", 178 strerror(errno)); 179 return (ABORT); 180 } 181 182 if ((from = open(fn, O_RDONLY)) == -1) { 183 free(nname); 184 return (FALSE); 185 } 186 to = open(nname, O_WRONLY|O_CREAT|O_TRUNC, (sb.st_mode & 0777)); 187 if (to == -1) { 188 serrno = errno; 189 close(from); 190 free(nname); 191 errno = serrno; 192 return (FALSE); 193 } 194 while ((nread = read(from, buf, sizeof(buf))) > 0) { 195 if (write(to, buf, nread) != nread) { 196 nread = -1; 197 break; 198 } 199 } 200 serrno = errno; 201 close(from); 202 close(to); 203 if (nread == -1) { 204 if (unlink(nname) == -1) 205 ewprintf("Can't unlink temp : %s", strerror(errno)); 206 } 207 free(nname); 208 errno = serrno; 209 210 return (nread == -1 ? FALSE : TRUE); 211 } 212 #endif 213 214 /* 215 * The string "fn" is a file name. 216 * Perform any required appending of directory name or case adjustments. 217 * If NO_DIR is not defined, the same file should be refered to even if the 218 * working directory changes. 219 */ 220 #ifdef SYMBLINK 221 #include <sys/types.h> 222 #include <sys/stat.h> 223 #ifndef MAXLINK 224 #define MAXLINK 8 /* maximum symbolic links to follow */ 225 #endif 226 #endif 227 #include <pwd.h> 228 #ifndef NO_DIR 229 extern char *wdir; 230 #endif 231 232 char * 233 adjustname(const char *fn) 234 { 235 static char fnb[MAXPATHLEN]; 236 const char *cp; 237 char user[LOGIN_NAME_MAX + 1], path[MAXPATHLEN]; 238 int len; 239 240 path[0] = '\0'; 241 /* first handle tilde expansion */ 242 if (fn[0] == '~') { 243 struct passwd *pw; 244 245 cp = strchr(fn, '/'); 246 if (cp == NULL) 247 cp = fn + strlen(fn); /* point to the NUL byte */ 248 249 if ((cp - &fn[1]) > LOGIN_NAME_MAX) { 250 ewprintf("login name too long"); 251 return (NULL); 252 } 253 if (cp == &fn[1]) /* ~/ */ 254 strlcpy(user, getlogin(), sizeof user); 255 else 256 strlcpy(user, &fn[1], cp - &fn[1] + 1); 257 pw = getpwnam(user); 258 if (pw == NULL) { 259 ewprintf("unknown user %s", user); 260 return (NULL); 261 } 262 strlcpy(path, pw->pw_dir, sizeof path - 1); 263 len = strlen(path); 264 if (path[len] != '/') { 265 path[len] = '/'; 266 path[len + 1] = '\0'; 267 } 268 fn = cp; 269 if (*fn == '/') 270 fn++; 271 } 272 strlcat(path, fn, sizeof path); 273 274 return (realpath(path, fnb)); 275 } 276 277 #ifndef NO_STARTUP 278 /* 279 * Find a startup file for the user and return its name. As a service 280 * to other pieces of code that may want to find a startup file (like 281 * the terminal driver in particular), accepts a suffix to be appended 282 * to the startup file name. 283 */ 284 char * 285 startupfile(char *suffix) 286 { 287 static char file[NFILEN]; 288 char *home; 289 290 if ((home = getenv("HOME")) == NULL || *home == '\0') 291 goto nohome; 292 293 if (suffix == NULL) { 294 if (snprintf(file, sizeof(file), "%s/.mg", home) 295 >= sizeof(file)) 296 return (NULL); 297 } else { 298 if (snprintf(file, sizeof(file), "%s/.mg-%s", home, suffix) 299 >= sizeof(file)) 300 return (NULL); 301 } 302 303 if (access(file, R_OK) == 0) 304 return (file); 305 nohome: 306 #ifdef STARTUPFILE 307 if (suffix == NULL) { 308 if (snprintf(file, sizeof(file), "%s", STARTUPFILE) 309 >= sizeof(file)) 310 return (NULL); 311 } else { 312 if (snprintf(file, sizeof(file), "%s%s", STARTUPFILE, suffix) 313 >= sizeof(file)) 314 return (NULL); 315 } 316 317 if (access(file, R_OK) == 0) 318 return (file); 319 #endif 320 return (NULL); 321 } 322 #endif 323 324 #ifndef NO_DIRED 325 #include <sys/wait.h> 326 #include "kbd.h" 327 328 int 329 copy(char *frname, char *toname) 330 { 331 int ifd, ofd, n; 332 char buf[BUFSIZ]; 333 mode_t mode = DEFFILEMODE; /* XXX?? */ 334 struct stat orig; 335 336 if ((ifd = open(frname, O_RDONLY)) == -1) 337 return (FALSE); 338 if (fstat(ifd, &orig) == -1) { 339 ewprintf("fstat: %s", strerror(errno)); 340 close(ifd); 341 return (FALSE); 342 } 343 344 if ((ofd = open(toname, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { 345 close(ifd); 346 return (FALSE); 347 } 348 while ((n = read(ifd, buf, sizeof buf)) > 0) { 349 if (write(ofd, buf, n) != n) { 350 ewprintf("write error : %s", strerror(errno)); 351 break; 352 } 353 } 354 if (fchmod(ofd, orig.st_mode) == -1) 355 ewprintf("Cannot set original mode : %s", strerror(errno)); 356 357 if (n == -1) { 358 ewprintf("Read error : %s", strerror(errno)); 359 close(ifd); 360 close(ofd); 361 return (FALSE); 362 } 363 /* 364 * It is "normal" for this to fail since we can't guarantee that 365 * we will be running as root 366 */ 367 if (fchown(ofd, orig.st_uid, orig.st_gid) && errno != EPERM) 368 ewprintf("Cannot set owner : %s", strerror(errno)); 369 370 (void) close(ifd); 371 (void) close(ofd); 372 373 return (TRUE); 374 } 375 376 /* 377 * dirname needs to have enough place to store an additional '/'. 378 */ 379 BUFFER * 380 dired_(char *dirname) 381 { 382 BUFFER *bp; 383 FILE *dirpipe; 384 char line[256]; 385 int len; 386 387 if ((dirname = adjustname(dirname)) == NULL) { 388 ewprintf("Bad directory name"); 389 return NULL; 390 } 391 /* this should not be done, instead adjustname() should get a flag */ 392 len = strlen(dirname); 393 if (dirname[len - 1] != '/') { 394 dirname[len++] = '/'; 395 dirname[len] = '\0'; 396 } 397 if ((bp = findbuffer(dirname)) == NULL) { 398 ewprintf("Could not create buffer"); 399 return NULL; 400 } 401 if (bclear(bp) != TRUE) 402 return NULL; 403 bp->b_flag |= BFREADONLY; 404 if (snprintf(line, sizeof(line), "ls -al %s", dirname) >= sizeof(line)){ 405 ewprintf("Path too long"); 406 return NULL; 407 } 408 if ((dirpipe = popen(line, "r")) == NULL) { 409 ewprintf("Problem opening pipe to ls"); 410 return NULL; 411 } 412 line[0] = line[1] = ' '; 413 while (fgets(&line[2], sizeof(line) - 2, dirpipe) != NULL) { 414 line[strlen(line) - 1] = '\0'; /* remove ^J */ 415 (void) addline(bp, line); 416 } 417 if (pclose(dirpipe) == -1) { 418 ewprintf("Problem closing pipe to ls"); 419 return NULL; 420 } 421 bp->b_dotp = lforw(bp->b_linep); /* go to first line */ 422 (void) strlcpy(bp->b_fname, dirname, sizeof bp->b_fname); 423 if ((bp->b_modes[0] = name_mode("dired")) == NULL) { 424 bp->b_modes[0] = name_mode("fundamental"); 425 ewprintf("Could not find mode dired"); 426 return NULL; 427 } 428 bp->b_nmodes = 0; 429 return bp; 430 } 431 432 #define NAME_FIELD 8 433 434 int 435 d_makename(LINE *lp, char *fn, int len) 436 { 437 int i; 438 char *p, *np; 439 440 strlcpy(fn, curbp->b_fname, len); 441 p = lp->l_text; 442 for (i = 0; i < NAME_FIELD; i++) { 443 np = strpbrk(p, "\t "); 444 if (np == NULL) 445 return ABORT; 446 p = np + 1; 447 while (*p != '\0' && strchr("\t ", *p)) 448 p++; 449 } 450 strlcat(fn, p, len); 451 return lgetc(lp, 2) == 'd'; 452 } 453 #endif /* NO_DIRED */ 454 455 struct filelist { 456 LIST fl_l; 457 char fl_name[NFILEN + 2]; 458 }; 459 460 /* 461 * return list of file names that match the name in buf. 462 */ 463 464 LIST * 465 make_file_list(char *buf) 466 { 467 char *dir, *file, *cp; 468 int len, preflen; 469 DIR *dirp; 470 struct dirent *dent; 471 LIST *last; 472 struct filelist *current; 473 char prefixx[NFILEN + 1]; 474 475 /* 476 * We need three different strings: dir - the name of the directory 477 * containing what the user typed. Must be a real unix file name, 478 * e.g. no ~user, etc.. Must not end in /. prefix - the portion of 479 * what the user typed that is before the names we are going to find 480 * in the directory. Must have a trailing / if the user typed it. 481 * names from the directory. we open dir, and return prefix 482 * concatenated with names. 483 */ 484 485 /* first we get a directory name we can look up */ 486 /* 487 * Names ending in . are potentially odd, because adjustname will 488 * treat foo/.. as a reference to another directory, whereas we are 489 * interested in names starting with .. 490 */ 491 len = strlen(buf); 492 if (buf[len - 1] == '.') { 493 buf[len - 1] = 'x'; 494 dir = adjustname(buf); 495 buf[len - 1] = '.'; 496 } else 497 dir = adjustname(buf); 498 if (dir == NULL) 499 return (NULL); 500 /* 501 * If the user typed a trailing / or the empty string 502 * he wants us to use his file spec as a directory name. 503 */ 504 if (buf[0] && buf[strlen(buf) - 1] != '/') { 505 file = strrchr(dir, '/'); 506 if (file) { 507 *file = 0; 508 if (*dir == 0) 509 dir = "/"; 510 } else { 511 return (NULL); 512 } 513 } 514 /* Now we get the prefix of the name the user typed. */ 515 strlcpy(prefixx, buf, sizeof prefixx); 516 cp = strrchr(prefixx, '/'); 517 if (cp == NULL) 518 prefixx[0] = 0; 519 else 520 cp[1] = 0; 521 522 preflen = strlen(prefixx); 523 /* cp is the tail of buf that really needs to be compared */ 524 cp = buf + preflen; 525 len = strlen(cp); 526 527 /* 528 * Now make sure that file names will fit in the buffers allocated. 529 * SV files are fairly short. For BSD, something more general would 530 * be required. 531 */ 532 if ((preflen + MAXNAMLEN) > NFILEN) 533 return (NULL); 534 535 /* loop over the specified directory, making up the list of files */ 536 537 /* 538 * Note that it is worth our time to filter out names that don't 539 * match, even though our caller is going to do so again, and to 540 * avoid doing the stat if completion is being done, because stat'ing 541 * every file in the directory is relatively expensive. 542 */ 543 544 dirp = opendir(dir); 545 if (dirp == NULL) 546 return (NULL); 547 last = NULL; 548 549 while ((dent = readdir(dirp)) != NULL) { 550 int isdir; 551 552 if (dent->d_namlen < len || memcmp(cp, dent->d_name, len) != 0) 553 continue; 554 555 isdir = 0; 556 if (dent->d_type == DT_DIR) { 557 isdir = 1; 558 } else if (dent->d_type == DT_LNK || 559 dent->d_type == DT_UNKNOWN) { 560 struct stat statbuf; 561 char statname[NFILEN + 2]; 562 563 statbuf.st_mode = 0; 564 if (snprintf(statname, sizeof(statname), "%s/%s", 565 dir, dent->d_name) > sizeof(statname) - 1) { 566 continue; 567 } 568 if (stat(statname, &statbuf) < 0) 569 continue; 570 if (statbuf.st_mode & S_IFDIR) 571 isdir = 1; 572 } 573 574 current = malloc(sizeof(struct filelist)); 575 if (current == NULL) 576 break; 577 578 if (snprintf(current->fl_name, sizeof(current->fl_name), 579 "%s%s%s", prefixx, dent->d_name, isdir ? "/" : "") 580 >= sizeof(current->fl_name)) { 581 free(current); 582 continue; 583 } 584 current->fl_l.l_next = last; 585 current->fl_l.l_name = current->fl_name; 586 last = (LIST *) current; 587 } 588 closedir(dirp); 589 590 return (last); 591 } 592