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