1 /* $OpenBSD: file.c,v 1.102 2019/06/22 15:03:43 lum Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * File commands. 7 */ 8 9 #include <sys/queue.h> 10 #include <sys/stat.h> 11 #include <errno.h> 12 #include <libgen.h> 13 #include <signal.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <unistd.h> 18 19 #include "def.h" 20 21 size_t xdirname(char *, const char *, size_t); 22 23 /* 24 * Insert a file into the current buffer. Real easy - just call the 25 * insertfile routine with the file name. 26 */ 27 /* ARGSUSED */ 28 int 29 fileinsert(int f, int n) 30 { 31 char fname[NFILEN], *bufp, *adjf; 32 33 if (getbufcwd(fname, sizeof(fname)) != TRUE) 34 fname[0] = '\0'; 35 bufp = eread("Insert file: ", fname, NFILEN, 36 EFNEW | EFCR | EFFILE | EFDEF); 37 if (bufp == NULL) 38 return (ABORT); 39 else if (bufp[0] == '\0') 40 return (FALSE); 41 adjf = adjustname(bufp, TRUE); 42 if (adjf == NULL) 43 return (FALSE); 44 return (insertfile(adjf, NULL, FALSE)); 45 } 46 47 /* 48 * Select a file for editing. If the file is a directory, invoke dired. 49 * Otherwise, look around to see if you can find the file in another buffer; 50 * if you can find it, just switch to the buffer. If you cannot find the 51 * file, create a new buffer, read in the text, and switch to the new buffer. 52 */ 53 /* ARGSUSED */ 54 int 55 filevisit(int f, int n) 56 { 57 struct buffer *bp; 58 char fname[NFILEN], *bufp, *adjf; 59 int status; 60 61 if (getbufcwd(fname, sizeof(fname)) != TRUE) 62 fname[0] = '\0'; 63 bufp = eread("Find file: ", fname, NFILEN, 64 EFNEW | EFCR | EFFILE | EFDEF); 65 if (bufp == NULL) 66 return (ABORT); 67 else if (bufp[0] == '\0') 68 return (FALSE); 69 adjf = adjustname(fname, TRUE); 70 if (adjf == NULL) 71 return (FALSE); 72 if (fisdir(adjf) == TRUE) 73 return (do_dired(adjf)); 74 if ((bp = findbuffer(adjf)) == NULL) 75 return (FALSE); 76 curbp = bp; 77 if (showbuffer(bp, curwp, WFFULL) != TRUE) 78 return (FALSE); 79 if (bp->b_fname[0] == '\0') { 80 if ((status = readin(adjf)) != TRUE) 81 killbuffer(bp); 82 return (status); 83 } 84 return (TRUE); 85 } 86 87 /* 88 * Replace the current file with an alternate one. Semantics for finding 89 * the replacement file are the same as 'filevisit', except the current 90 * buffer is killed before the switch. If the kill fails, or is aborted, 91 * revert to the original file. 92 */ 93 /* ARGSUSED */ 94 int 95 filevisitalt(int f, int n) 96 { 97 char fname[NFILEN], *bufp; 98 99 if (getbufcwd(fname, sizeof(fname)) != TRUE) 100 fname[0] = '\0'; 101 bufp = eread("Find alternate file: ", fname, NFILEN, 102 EFNEW | EFCR | EFFILE | EFDEF); 103 if (bufp == NULL) 104 return (ABORT); 105 else if (bufp[0] == '\0') 106 return (FALSE); 107 108 return (do_filevisitalt(fname)); 109 } 110 111 int 112 do_filevisitalt(char *fn) 113 { 114 struct buffer *bp; 115 int status; 116 char *adjf; 117 118 status = killbuffer(curbp); 119 if (status == ABORT || status == FALSE) 120 return (ABORT); 121 122 adjf = adjustname(fn, TRUE); 123 if (adjf == NULL) 124 return (FALSE); 125 if (fisdir(adjf) == TRUE) 126 return (do_dired(adjf)); 127 if ((bp = findbuffer(adjf)) == NULL) 128 return (FALSE); 129 curbp = bp; 130 if (showbuffer(bp, curwp, WFFULL) != TRUE) 131 return (FALSE); 132 if (bp->b_fname[0] == '\0') { 133 if ((status = readin(adjf)) != TRUE) 134 killbuffer(bp); 135 return (status); 136 } 137 return (TRUE); 138 } 139 140 int 141 filevisitro(int f, int n) 142 { 143 int error; 144 145 error = filevisit(f, n); 146 if (error != TRUE) 147 return (error); 148 curbp->b_flag |= BFREADONLY; 149 return (TRUE); 150 } 151 152 /* 153 * Pop to a file in the other window. Same as the last function, but uses 154 * popbuf instead of showbuffer. 155 */ 156 /* ARGSUSED */ 157 int 158 poptofile(int f, int n) 159 { 160 struct buffer *bp; 161 struct mgwin *wp; 162 char fname[NFILEN], *adjf, *bufp; 163 int status; 164 165 if (getbufcwd(fname, sizeof(fname)) != TRUE) 166 fname[0] = '\0'; 167 if ((bufp = eread("Find file in other window: ", fname, NFILEN, 168 EFNEW | EFCR | EFFILE | EFDEF)) == NULL) 169 return (ABORT); 170 else if (bufp[0] == '\0') 171 return (FALSE); 172 adjf = adjustname(fname, TRUE); 173 if (adjf == NULL) 174 return (FALSE); 175 if (fisdir(adjf) == TRUE) 176 return (do_dired(adjf)); 177 if ((bp = findbuffer(adjf)) == NULL) 178 return (FALSE); 179 if (bp == curbp) 180 return (splitwind(f, n)); 181 if ((wp = popbuf(bp, WNONE)) == NULL) 182 return (FALSE); 183 curbp = bp; 184 curwp = wp; 185 if (bp->b_fname[0] == '\0') { 186 if ((status = readin(adjf)) != TRUE) 187 killbuffer(bp); 188 return (status); 189 } 190 return (TRUE); 191 } 192 193 /* 194 * Read the file "fname" into the current buffer. Make all of the text 195 * in the buffer go away, after checking for unsaved changes. This is 196 * called by the "read" command, the "visit" command, and the mainline 197 * (for "mg file"). 198 */ 199 int 200 readin(char *fname) 201 { 202 struct mgwin *wp; 203 struct stat statbuf; 204 int status, i, ro = FALSE; 205 PF *ael; 206 char dp[NFILEN]; 207 208 /* might be old */ 209 if (bclear(curbp) != TRUE) 210 return (TRUE); 211 /* Clear readonly. May be set by autoexec path */ 212 curbp->b_flag &= ~BFREADONLY; 213 if ((status = insertfile(fname, fname, TRUE)) != TRUE) { 214 dobeep(); 215 ewprintf("File is not readable: %s", fname); 216 return (FALSE); 217 } 218 219 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 220 if (wp->w_bufp == curbp) { 221 wp->w_dotp = wp->w_linep = bfirstlp(curbp); 222 wp->w_doto = 0; 223 wp->w_markp = NULL; 224 wp->w_marko = 0; 225 } 226 } 227 228 /* 229 * Call auto-executing function if we need to. 230 */ 231 if ((ael = find_autoexec(fname)) != NULL) { 232 for (i = 0; ael[i] != NULL; i++) 233 (*ael[i])(0, 1); 234 free(ael); 235 } 236 237 /* no change */ 238 curbp->b_flag &= ~BFCHG; 239 240 /* 241 * Set the buffer READONLY flag if any of following are true: 242 * 1. file is a directory. 243 * 2. file is read-only. 244 * 3. file doesn't exist and directory is read-only. 245 */ 246 if (fisdir(fname) == TRUE) { 247 ro = TRUE; 248 } else if ((access(fname, W_OK) == -1)) { 249 if (errno != ENOENT) { 250 ro = TRUE; 251 } else if (errno == ENOENT) { 252 (void)xdirname(dp, fname, sizeof(dp)); 253 (void)strlcat(dp, "/", sizeof(dp)); 254 255 /* Missing directory; keep buffer rw, like emacs */ 256 if (stat(dp, &statbuf) == -1 && errno == ENOENT) { 257 if (eyorn("Missing directory, create") == TRUE) 258 (void)do_makedir(dp); 259 } else if (access(dp, W_OK) == -1 && errno == EACCES) { 260 ewprintf("File not found and directory" 261 " write-protected"); 262 ro = TRUE; 263 } 264 } 265 } 266 if (ro == TRUE) 267 curbp->b_flag |= BFREADONLY; 268 269 if (startrow) { 270 gotoline(FFARG, startrow); 271 startrow = 0; 272 } 273 274 undo_add_modified(); 275 return (status); 276 } 277 278 /* 279 * NB, getting file attributes is done here under control of a flag 280 * rather than in readin, which would be cleaner. I was concerned 281 * that some operating system might require the file to be open 282 * in order to get the information. Similarly for writing. 283 */ 284 285 /* 286 * Insert a file in the current buffer, after dot. If file is a directory, 287 * and 'replacebuf' is TRUE, invoke dired mode, else die with an error. 288 * If file is a regular file, set mark at the end of the text inserted; 289 * point at the beginning. Return a standard status. Print a summary 290 * (lines read, error message) out as well. This routine also does the 291 * read end of backup processing. The BFBAK flag, if set in a buffer, 292 * says that a backup should be taken. It is set when a file is read in, 293 * but not on a new file. You don't need to make a backup copy of nothing. 294 */ 295 296 static char *line = NULL; 297 static int linesize = 0; 298 299 int 300 insertfile(char *fname, char *newname, int replacebuf) 301 { 302 struct buffer *bp; 303 struct line *lp1, *lp2; 304 struct line *olp; /* line we started at */ 305 struct mgwin *wp; 306 int nbytes, s, nline = 0, siz, x, x2; 307 int opos; /* offset we started at */ 308 int oline; /* original line number */ 309 FILE *ffp; 310 311 if (replacebuf == TRUE) 312 x = undo_enable(FFRAND, 0); 313 else 314 x = undo_enabled(); 315 316 lp1 = NULL; 317 if (line == NULL) { 318 line = malloc(NLINE); 319 if (line == NULL) 320 panic("out of memory"); 321 linesize = NLINE; 322 } 323 324 /* cheap */ 325 bp = curbp; 326 if (newname != NULL) { 327 (void)strlcpy(bp->b_fname, newname, sizeof(bp->b_fname)); 328 (void)xdirname(bp->b_cwd, newname, sizeof(bp->b_cwd)); 329 (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd)); 330 } 331 332 /* hard file open */ 333 if ((s = ffropen(&ffp, fname, (replacebuf == TRUE) ? bp : NULL)) 334 == FIOERR) 335 goto out; 336 if (s == FIOFNF) { 337 /* file not found */ 338 if (newname != NULL) 339 ewprintf("(New file)"); 340 else 341 ewprintf("(File not found)"); 342 goto out; 343 } else if (s == FIODIR) { 344 /* file was a directory */ 345 if (replacebuf == FALSE) { 346 dobeep(); 347 ewprintf("Cannot insert: file is a directory, %s", 348 fname); 349 goto cleanup; 350 } 351 killbuffer(bp); 352 bp = dired_(fname); 353 undo_enable(FFRAND, x); 354 if (bp == NULL) 355 return (FALSE); 356 curbp = bp; 357 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 358 } else { 359 (void)xdirname(bp->b_cwd, fname, sizeof(bp->b_cwd)); 360 (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd)); 361 } 362 opos = curwp->w_doto; 363 oline = curwp->w_dotline; 364 /* 365 * Open a new line at dot and start inserting after it. 366 * We will delete this newline after insertion. 367 * Disable undo, as we create the undo record manually. 368 */ 369 x2 = undo_enable(FFRAND, 0); 370 (void)lnewline(); 371 olp = lback(curwp->w_dotp); 372 undo_enable(FFRAND, x2); 373 374 nline = 0; 375 siz = 0; 376 while ((s = ffgetline(ffp, line, linesize, &nbytes)) != FIOERR) { 377 retry: 378 siz += nbytes + 1; 379 switch (s) { 380 case FIOSUC: 381 /* FALLTHRU */ 382 case FIOEOF: 383 ++nline; 384 if ((lp1 = lalloc(nbytes)) == NULL) { 385 /* keep message on the display */ 386 s = FIOERR; 387 undo_add_insert(olp, opos, 388 siz - nbytes - 1 - 1); 389 goto endoffile; 390 } 391 bcopy(line, <ext(lp1)[0], nbytes); 392 lp2 = lback(curwp->w_dotp); 393 lp2->l_fp = lp1; 394 lp1->l_fp = curwp->w_dotp; 395 lp1->l_bp = lp2; 396 curwp->w_dotp->l_bp = lp1; 397 if (s == FIOEOF) { 398 undo_add_insert(olp, opos, siz - 1); 399 goto endoffile; 400 } 401 break; 402 case FIOLONG: { 403 /* a line too long to fit in our buffer */ 404 char *cp; 405 int newsize; 406 407 newsize = linesize * 2; 408 if (newsize < 0 || 409 (cp = malloc(newsize)) == NULL) { 410 dobeep(); 411 ewprintf("Could not allocate %d bytes", 412 newsize); 413 s = FIOERR; 414 goto endoffile; 415 } 416 bcopy(line, cp, linesize); 417 free(line); 418 line = cp; 419 s = ffgetline(ffp, line + linesize, linesize, 420 &nbytes); 421 nbytes += linesize; 422 linesize = newsize; 423 if (s == FIOERR) 424 goto endoffile; 425 goto retry; 426 } 427 default: 428 dobeep(); 429 ewprintf("Unknown code %d reading file", s); 430 s = FIOERR; 431 break; 432 } 433 } 434 endoffile: 435 /* ignore errors */ 436 (void)ffclose(ffp, NULL); 437 /* don't zap an error */ 438 if (s == FIOEOF) { 439 if (nline == 1) 440 ewprintf("(Read 1 line)"); 441 else 442 ewprintf("(Read %d lines)", nline); 443 } 444 /* set mark at the end of the text */ 445 curwp->w_dotp = curwp->w_markp = lback(curwp->w_dotp); 446 curwp->w_marko = llength(curwp->w_markp); 447 curwp->w_markline = oline + nline + 1; 448 /* 449 * if we are at the end of the file, ldelnewline is a no-op, 450 * but we still need to decrement the line and markline counts 451 * as we've accounted for this fencepost in our arithmetic 452 */ 453 if (lforw(curwp->w_dotp) == curwp->w_bufp->b_headp) { 454 curwp->w_bufp->b_lines--; 455 curwp->w_markline--; 456 } else 457 (void)ldelnewline(); 458 curwp->w_dotp = olp; 459 curwp->w_doto = opos; 460 curwp->w_dotline = oline; 461 if (olp == curbp->b_headp) 462 curwp->w_dotp = lforw(olp); 463 if (newname != NULL) 464 bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */ 465 else 466 bp->b_flag |= BFCHG; 467 /* 468 * If the insert was at the end of buffer, set lp1 to the end of 469 * buffer line, and lp2 to the beginning of the newly inserted text. 470 * (Otherwise lp2 is set to NULL.) This is used below to set 471 * pointers in other windows correctly if they are also at the end of 472 * buffer. 473 */ 474 lp1 = bp->b_headp; 475 if (curwp->w_markp == lp1) { 476 lp2 = curwp->w_dotp; 477 } else { 478 /* delete extraneous newline */ 479 (void)ldelnewline(); 480 out: lp2 = NULL; 481 } 482 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 483 if (wp->w_bufp == curbp) { 484 wp->w_rflag |= WFMODE | WFEDIT; 485 if (wp != curwp && lp2 != NULL) { 486 if (wp->w_dotp == lp1) 487 wp->w_dotp = lp2; 488 if (wp->w_markp == lp1) 489 wp->w_markp = lp2; 490 if (wp->w_linep == lp1) 491 wp->w_linep = lp2; 492 } 493 } 494 } 495 bp->b_lines += nline; 496 cleanup: 497 undo_enable(FFRAND, x); 498 499 /* return FALSE if error */ 500 return (s != FIOERR); 501 } 502 503 /* 504 * Ask for a file name and write the contents of the current buffer to that 505 * file. Update the remembered file name and clear the buffer changed flag. 506 * This handling of file names is different from the earlier versions and 507 * is more compatible with Gosling EMACS than with ITS EMACS. 508 */ 509 /* ARGSUSED */ 510 int 511 filewrite(int f, int n) 512 { 513 struct stat statbuf; 514 int s; 515 char fname[NFILEN], bn[NBUFN], tmp[NFILEN + 25]; 516 char *adjfname, *bufp; 517 FILE *ffp; 518 519 if (getbufcwd(fname, sizeof(fname)) != TRUE) 520 fname[0] = '\0'; 521 if ((bufp = eread("Write file: ", fname, NFILEN, 522 EFDEF | EFNEW | EFCR | EFFILE)) == NULL) 523 return (ABORT); 524 else if (bufp[0] == '\0') 525 return (FALSE); 526 527 adjfname = adjustname(fname, TRUE); 528 if (adjfname == NULL) 529 return (FALSE); 530 531 /* Check if file exists; write checks done later */ 532 if (stat(adjfname, &statbuf) == 0) { 533 if (S_ISDIR(statbuf.st_mode)) { 534 dobeep(); 535 ewprintf("%s is a directory", adjfname); 536 return (FALSE); 537 } 538 snprintf(tmp, sizeof(tmp), "File `%s' exists; overwrite", 539 adjfname); 540 if ((s = eyorn(tmp)) != TRUE) 541 return (s); 542 } 543 544 /* old attributes are no longer current */ 545 bzero(&curbp->b_fi, sizeof(curbp->b_fi)); 546 if ((s = writeout(&ffp, curbp, adjfname)) == TRUE) { 547 (void)strlcpy(curbp->b_fname, adjfname, sizeof(curbp->b_fname)); 548 if (getbufcwd(curbp->b_cwd, sizeof(curbp->b_cwd)) != TRUE) 549 (void)strlcpy(curbp->b_cwd, "/", sizeof(curbp->b_cwd)); 550 if (augbname(bn, curbp->b_fname, sizeof(bn)) 551 == FALSE) 552 return (FALSE); 553 free(curbp->b_bname); 554 if ((curbp->b_bname = strdup(bn)) == NULL) 555 return (FALSE); 556 (void)fupdstat(curbp); 557 curbp->b_flag &= ~(BFBAK | BFCHG); 558 upmodes(curbp); 559 undo_add_boundary(FFRAND, 1); 560 undo_add_modified(); 561 } 562 return (s); 563 } 564 565 /* 566 * Save the contents of the current buffer back into its associated file. 567 */ 568 static int makebackup = TRUE; 569 570 /* ARGSUSED */ 571 int 572 filesave(int f, int n) 573 { 574 if (curbp->b_fname[0] == '\0') 575 return (filewrite(f, n)); 576 else 577 return (buffsave(curbp)); 578 } 579 580 /* 581 * Save the contents of the buffer argument into its associated file. Do 582 * nothing if there have been no changes (is this a bug, or a feature?). 583 * Error if there is no remembered file name. If this is the first write 584 * since the read or visit, then a backup copy of the file is made. 585 * Allow user to select whether or not to make backup files by looking at 586 * the value of makebackup. 587 */ 588 int 589 buffsave(struct buffer *bp) 590 { 591 int s; 592 FILE *ffp; 593 594 /* return, no changes */ 595 if ((bp->b_flag & BFCHG) == 0) { 596 ewprintf("(No changes need to be saved)"); 597 return (TRUE); 598 } 599 600 /* must have a name */ 601 if (bp->b_fname[0] == '\0') { 602 dobeep(); 603 ewprintf("No file name"); 604 return (FALSE); 605 } 606 607 /* Ensure file has not been modified elsewhere */ 608 /* We don't use the ignore flag here */ 609 if (fchecktime(bp) != TRUE) { 610 if ((s = eyesno("File has changed on disk since last save. " 611 "Save anyway")) != TRUE) 612 return (s); 613 } 614 615 if (makebackup && (bp->b_flag & BFBAK)) { 616 s = fbackupfile(bp->b_fname); 617 /* hard error */ 618 if (s == ABORT) 619 return (FALSE); 620 /* softer error */ 621 if (s == FALSE && 622 (s = eyesno("Backup error, save anyway")) != TRUE) 623 return (s); 624 } 625 if ((s = writeout(&ffp, bp, bp->b_fname)) == TRUE) { 626 (void)fupdstat(bp); 627 bp->b_flag &= ~(BFCHG | BFBAK); 628 upmodes(bp); 629 undo_add_boundary(FFRAND, 1); 630 undo_add_modified(); 631 } 632 return (s); 633 } 634 635 /* 636 * Since we don't have variables (we probably should) this is a command 637 * processor for changing the value of the make backup flag. If no argument 638 * is given, sets makebackup to true, so backups are made. If an argument is 639 * given, no backup files are made when saving a new version of a file. 640 */ 641 /* ARGSUSED */ 642 int 643 makebkfile(int f, int n) 644 { 645 if (f & FFARG) 646 makebackup = n > 0; 647 else 648 makebackup = !makebackup; 649 ewprintf("Backup files %sabled", makebackup ? "en" : "dis"); 650 return (TRUE); 651 } 652 653 /* 654 * NB: bp is passed to both ffwopen and ffclose because some 655 * attribute information may need to be updated at open time 656 * and others after the close. This is OS-dependent. Note 657 * that the ff routines are assumed to be able to tell whether 658 * the attribute information has been set up in this buffer 659 * or not. 660 */ 661 662 /* 663 * This function performs the details of file writing; writing the file 664 * in buffer bp to file fn. Uses the file management routines in the 665 * "fileio.c" package. Most of the grief is checking of some sort. 666 * You may want to call fupdstat() after using this function. 667 */ 668 int 669 writeout(FILE ** ffp, struct buffer *bp, char *fn) 670 { 671 struct stat statbuf; 672 struct line *lpend; 673 int s, eobnl; 674 char dp[NFILEN]; 675 676 if (stat(fn, &statbuf) == -1 && errno == ENOENT) { 677 errno = 0; 678 (void)xdirname(dp, fn, sizeof(dp)); 679 (void)strlcat(dp, "/", sizeof(dp)); 680 if (access(dp, W_OK) && errno == EACCES) { 681 dobeep(); 682 ewprintf("Directory %s write-protected", dp); 683 return (FIOERR); 684 } else if (errno == ENOENT) { 685 dobeep(); 686 ewprintf("%s: no such directory", dp); 687 return (FIOERR); 688 } 689 } 690 lpend = bp->b_headp; 691 eobnl = 0; 692 if (llength(lback(lpend)) != 0) { 693 eobnl = eyorn("No newline at end of file, add one"); 694 if (eobnl != TRUE && eobnl != FALSE) 695 return (eobnl); /* abort */ 696 } 697 /* open writes message */ 698 if ((s = ffwopen(ffp, fn, bp)) != FIOSUC) 699 return (FALSE); 700 s = ffputbuf(*ffp, bp, eobnl); 701 if (s == FIOSUC) { 702 /* no write error */ 703 s = ffclose(*ffp, bp); 704 if (s == FIOSUC) 705 ewprintf("Wrote %s", fn); 706 } else { 707 /* print a message indicating write error */ 708 (void)ffclose(*ffp, bp); 709 dobeep(); 710 ewprintf("Unable to write %s", fn); 711 } 712 return (s == FIOSUC); 713 } 714 715 /* 716 * Tag all windows for bp (all windows if bp == NULL) as needing their 717 * mode line updated. 718 */ 719 void 720 upmodes(struct buffer *bp) 721 { 722 struct mgwin *wp; 723 724 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 725 if (bp == NULL || curwp->w_bufp == bp) 726 wp->w_rflag |= WFMODE; 727 } 728 729 /* 730 * dirname using strlcpy semantic. 731 * Like dirname() except an empty string is returned in 732 * place of "/". This means we can always add a trailing 733 * slash and be correct. 734 * Address portability issues by copying argument 735 * before using. Some implementations modify the input string. 736 */ 737 size_t 738 xdirname(char *dp, const char *path, size_t dplen) 739 { 740 char ts[NFILEN]; 741 size_t len; 742 743 (void)strlcpy(ts, path, NFILEN); 744 len = strlcpy(dp, dirname(ts), dplen); 745 if (dplen > 0 && dp[0] == '/' && dp[1] == '\0') { 746 dp[0] = '\0'; 747 len = 0; 748 } 749 return (len); 750 } 751 752 /* 753 * basename using strlcpy/strlcat semantic. 754 * Address portability issue by copying argument 755 * before using: some implementations modify the input string. 756 */ 757 size_t 758 xbasename(char *bp, const char *path, size_t bplen) 759 { 760 char ts[NFILEN]; 761 762 (void)strlcpy(ts, path, NFILEN); 763 return (strlcpy(bp, basename(ts), bplen)); 764 } 765 766 /* 767 * The adjusted file name refers to a directory, so open dired mode. 768 */ 769 int 770 do_dired(char *adjf) 771 { 772 struct buffer *bp; 773 774 if ((bp = dired_(adjf)) == FALSE) 775 return (FALSE); 776 curbp = bp; 777 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 778 } 779