1 /* $OpenBSD: file.c,v 1.9 2001/05/24 03:05:22 mickey Exp $ */ 2 3 /* 4 * File commands. 5 */ 6 7 #include <libgen.h> 8 #include "def.h" 9 10 /* 11 * Insert a file into the current buffer. Real easy - just call the 12 * insertfile routine with the file name. 13 */ 14 /* ARGSUSED */ 15 int 16 fileinsert(f, n) 17 int f, n; 18 { 19 int s; 20 char fname[NFILEN]; 21 22 s = eread("Insert file: ", fname, NFILEN, EFNEW | EFCR | EFFILE); 23 if (s != TRUE) 24 return (s); 25 return insertfile(adjustname(fname), NULL, FALSE); 26 /* don't set buffer name */ 27 } 28 29 /* 30 * Select a file for editing. Look around to see if you can find the file 31 * in another buffer; if you can find it, just switch to the buffer. If 32 * you cannot find the file, create a new buffer, read in the text, and 33 * switch to the new buffer. 34 */ 35 /* ARGSUSED */ 36 int 37 filevisit(f, n) 38 int f, n; 39 { 40 BUFFER *bp; 41 int s; 42 char fname[NFILEN]; 43 char *adjf; 44 45 s = eread("Find file: ", fname, NFILEN, EFNEW | EFCR | EFFILE); 46 if (s != TRUE) 47 return s; 48 adjf = adjustname(fname); 49 if ((bp = findbuffer(adjf)) == NULL) 50 return FALSE; 51 curbp = bp; 52 if (showbuffer(bp, curwp, WFHARD) != TRUE) 53 return FALSE; 54 if (bp->b_fname[0] == 0) 55 return readin(adjf); 56 return TRUE; 57 } 58 59 /* 60 * Pop to a file in the other window. Same as the last function, but uses 61 * popbuf instead of showbuffer. 62 */ 63 /* ARGSUSED */ 64 int 65 poptofile(f, n) 66 int f, n; 67 { 68 BUFFER *bp; 69 MGWIN *wp; 70 int s; 71 char fname[NFILEN]; 72 char *adjf; 73 74 if ((s = eread("Find file in other window: ", fname, NFILEN, 75 EFNEW | EFCR | EFFILE)) != TRUE) 76 return s; 77 adjf = adjustname(fname); 78 if ((bp = findbuffer(adjf)) == NULL) 79 return FALSE; 80 if ((wp = popbuf(bp)) == NULL) 81 return FALSE; 82 curbp = bp; 83 curwp = wp; 84 if (bp->b_fname[0] == 0) 85 return readin(adjf); 86 return TRUE; 87 } 88 89 /* 90 * given a file name, either find the buffer it uses, or create a new 91 * empty buffer to put it in. 92 */ 93 BUFFER * 94 findbuffer(fname) 95 char *fname; 96 { 97 BUFFER *bp; 98 char bname[NBUFN], *cp; 99 unsigned int count = 1; 100 101 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 102 if (strcmp(bp->b_fname, fname) == 0) 103 return bp; 104 } 105 /* new buffer name */ 106 strcpy(bname, basename(fname)); 107 cp = bname + strlen(bname); 108 for (count = 1; bfind(bname, FALSE) != NULL; count++) 109 sprintf(cp, "<%d>", count); 110 return bfind(bname, TRUE); 111 } 112 113 /* 114 * Read the file "fname" into the current buffer. Make all of the text 115 * in the buffer go away, after checking for unsaved changes. This is 116 * called by the "read" command, the "visit" command, and the mainline 117 * (for "uemacs file"). 118 */ 119 int 120 readin(fname) 121 char *fname; 122 { 123 MGWIN *wp; 124 int status; 125 126 /* might be old */ 127 if (bclear(curbp) != TRUE) 128 return TRUE; 129 status = insertfile(fname, fname, TRUE); 130 131 /* no change */ 132 curbp->b_flag &= ~BFCHG; 133 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 134 if (wp->w_bufp == curbp) { 135 wp->w_dotp = wp->w_linep = lforw(curbp->b_linep); 136 wp->w_doto = 0; 137 wp->w_markp = NULL; 138 wp->w_marko = 0; 139 } 140 } 141 return status; 142 } 143 144 /* 145 * NB, getting file attributes is done here under control of a flag 146 * rather than in readin, which would be cleaner. I was concerned 147 * that some operating system might require the file to be open 148 * in order to get the information. Similarly for writing. 149 */ 150 151 /* 152 * Insert a file in the current buffer, after dot. Set mark at the end of 153 * the text inserted; point at the beginning. Return a standard status. 154 * Print a summary (lines read, error message) out as well. If the BACKUP 155 * conditional is set, then this routine also does the read end of backup 156 * processing. The BFBAK flag, if set in a buffer, says that a backup 157 * should be taken. It is set when a file is read in, but not on a new 158 * file. (You don't need to make a backup copy of nothing.) 159 */ 160 static char *line = NULL; 161 static int linesize = 0; 162 163 int 164 insertfile(fname, newname, needinfo) 165 char *fname, *newname; 166 int needinfo; 167 { 168 BUFFER *bp; 169 LINE *lp1, *lp2; 170 LINE *olp; /* line we started at */ 171 MGWIN *wp; 172 int nbytes, s, nline; 173 int opos; /* and offset into it */ 174 175 lp1 = NULL; 176 177 if (line == NULL) { 178 line = malloc(NLINE); 179 linesize = NLINE; 180 } 181 182 /* cheap */ 183 bp = curbp; 184 if (newname != NULL) 185 (void)strcpy(bp->b_fname, newname); 186 187 /* hard file open */ 188 if ((s = ffropen(fname, needinfo ? bp : NULL)) == FIOERR) 189 goto out; 190 if (s == FIOFNF) { 191 /* file not found */ 192 if (newname != NULL) 193 ewprintf("(New file)"); 194 else 195 ewprintf("(File not found)"); 196 goto out; 197 } 198 opos = curwp->w_doto; 199 200 /* open a new line, at point, and start inserting after it */ 201 (void)lnewline(); 202 olp = lback(curwp->w_dotp); 203 if (olp == curbp->b_linep) { 204 /* if at end of buffer, create a line to insert before */ 205 (void)lnewline(); 206 curwp->w_dotp = lback(curwp->w_dotp); 207 } 208 209 /* don't count fake lines at the end */ 210 nline = 0; 211 while ((s = ffgetline(line, linesize, &nbytes)) != FIOERR) { 212 doneread: 213 switch (s) { 214 case FIOSUC: 215 ++nline; 216 /* and continue */ 217 case FIOEOF: 218 /* the last line of the file */ 219 if ((lp1 = lalloc(nbytes)) == NULL) { 220 /* keep message on the display */ 221 s = FIOERR; 222 goto endoffile; 223 } 224 bcopy(line, <ext(lp1)[0], nbytes); 225 lp2 = lback(curwp->w_dotp); 226 lp2->l_fp = lp1; 227 lp1->l_fp = curwp->w_dotp; 228 lp1->l_bp = lp2; 229 curwp->w_dotp->l_bp = lp1; 230 if (s == FIOEOF) 231 goto endoffile; 232 break; 233 case FIOLONG:{ 234 /* a line too long to fit in our buffer */ 235 char *cp; 236 int newsize; 237 238 newsize = linesize * 2; 239 if (newsize < 0 || 240 (cp = malloc((unsigned)newsize)) == NULL) { 241 ewprintf("Could not allocate %d bytes", 242 newsize); 243 s = FIOERR; 244 goto endoffile; 245 } 246 bcopy(line, cp, linesize); 247 free(line); 248 line = cp; 249 s = ffgetline(line + linesize, linesize, 250 &nbytes); 251 nbytes += linesize; 252 linesize = newsize; 253 if (s == FIOERR) 254 goto endoffile; 255 goto doneread; 256 } 257 default: 258 ewprintf("Unknown code %d reading file", s); 259 s = FIOERR; 260 break; 261 } 262 } 263 endoffile: 264 /* ignore errors */ 265 ffclose(NULL); 266 /* don't zap an error */ 267 if (s == FIOEOF) { 268 if (nline == 1) 269 ewprintf("(Read 1 line)"); 270 else 271 ewprintf("(Read %d lines)", nline); 272 } 273 /* set mark at the end of the text */ 274 curwp->w_dotp = curwp->w_markp = lback(curwp->w_dotp); 275 curwp->w_marko = llength(curwp->w_markp); 276 (void)ldelnewline(); 277 curwp->w_dotp = olp; 278 curwp->w_doto = opos; 279 if (olp == curbp->b_linep) 280 curwp->w_dotp = lforw(olp); 281 #ifndef NO_BACKUP 282 if (newname != NULL) 283 bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */ 284 else 285 bp->b_flag |= BFCHG; 286 #else /* !NO_BACKUP */ 287 bp->b_flag |= BFCHG; 288 #endif /* !NO_BACKUP */ 289 /* 290 * if the insert was at the end of buffer, set lp1 to the end of 291 * buffer line, and lp2 to the beginning of the newly inserted text. 292 * (Otherwise lp2 is set to NULL.) This is used below to set 293 * pointers in other windows correctly if they are also at the end of 294 * buffer. 295 */ 296 lp1 = bp->b_linep; 297 if (curwp->w_markp == lp1) { 298 lp2 = curwp->w_dotp; 299 } else { 300 /* delete extraneous newline */ 301 (void)ldelnewline(); 302 out: lp2 = NULL; 303 } 304 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 305 if (wp->w_bufp == curbp) { 306 wp->w_flag |= WFMODE | WFEDIT; 307 if (wp != curwp && lp2 != NULL) { 308 if (wp->w_dotp == lp1) 309 wp->w_dotp = lp2; 310 if (wp->w_markp == lp1) 311 wp->w_markp = lp2; 312 if (wp->w_linep == lp1) 313 wp->w_linep = lp2; 314 } 315 } 316 } 317 /* return false if error */ 318 return s != FIOERR; 319 } 320 321 /* 322 * Ask for a file name and write the contents of the current buffer to that 323 * file. Update the remembered file name and clear the buffer changed flag. 324 * This handling of file names is different from the earlier versions and 325 * is more compatable with Gosling EMACS than with ITS EMACS. 326 */ 327 /* ARGSUSED */ 328 int 329 filewrite(f, n) 330 int f, n; 331 { 332 int s; 333 char fname[NFILEN]; 334 char *adjfname; 335 336 if ((s = eread("Write file: ", fname, NFILEN, 337 EFNEW | EFCR | EFFILE)) != TRUE) 338 return (s); 339 adjfname = adjustname(fname); 340 /* old attributes are no longer current */ 341 bzero(&curbp->b_fi, sizeof(curbp->b_fi)); 342 if ((s = writeout(curbp, adjfname)) == TRUE) { 343 (void)strcpy(curbp->b_fname, adjfname); 344 #ifndef NO_BACKUP 345 curbp->b_flag &= ~(BFBAK | BFCHG); 346 #else /* !NO_BACKUP */ 347 curbp->b_flag &= ~BFCHG; 348 #endif /* !NO_BACKUP */ 349 upmodes(curbp); 350 } 351 return s; 352 } 353 354 /* 355 * Save the contents of the current buffer back into its associated file. 356 */ 357 #ifndef NO_BACKUP 358 #ifndef MAKEBACKUP 359 #define MAKEBACKUP TRUE 360 #endif /* !MAKEBACKUP */ 361 static int makebackup = MAKEBACKUP; 362 #endif /* !NO_BACKUP */ 363 364 /* ARGSUSED */ 365 int 366 filesave(f, n) 367 int f, n; 368 { 369 return buffsave(curbp); 370 } 371 372 /* 373 * Save the contents of the buffer argument into its associated file. Do 374 * nothing if there have been no changes (is this a bug, or a feature?). 375 * Error if there is no remembered file name. If this is the first write 376 * since the read or visit, then a backup copy of the file is made. 377 * Allow user to select whether or not to make backup files by looking at 378 * the value of makebackup. 379 */ 380 int 381 buffsave(bp) 382 BUFFER *bp; 383 { 384 int s; 385 386 /* return, no changes */ 387 if ((bp->b_flag & BFCHG) == 0) { 388 ewprintf("(No changes need to be saved)"); 389 return TRUE; 390 } 391 392 /* must have a name */ 393 if (bp->b_fname[0] == '\0') { 394 ewprintf("No file name"); 395 return (FALSE); 396 } 397 398 #ifndef NO_BACKUP 399 if (makebackup && (bp->b_flag & BFBAK)) { 400 s = fbackupfile(bp->b_fname); 401 /* hard error */ 402 if (s == ABORT) 403 return FALSE; 404 /* softer error */ 405 if (s == FALSE && 406 (s = eyesno("Backup error, save anyway")) != TRUE) 407 return s; 408 } 409 #endif /* !NO_BACKUP */ 410 if ((s = writeout(bp, bp->b_fname)) == TRUE) { 411 #ifndef NO_BACKUP 412 bp->b_flag &= ~(BFCHG | BFBAK); 413 #else /* !NO_BACKUP */ 414 bp->b_flag &= ~BFCHG; 415 #endif /* !NO_BACKUP */ 416 upmodes(bp); 417 } 418 return s; 419 } 420 421 #ifndef NO_BACKUP 422 /* 423 * Since we don't have variables (we probably should) this is a command 424 * processor for changing the value of the make backup flag. If no argument 425 * is given, sets makebackup to true, so backups are made. If an argument is 426 * given, no backup files are made when saving a new version of a file. Only 427 * used when BACKUP is #defined. 428 */ 429 /* ARGSUSED */ 430 int 431 makebkfile(f, n) 432 int f, n; 433 { 434 if (f & FFARG) 435 makebackup = n > 0; 436 else 437 makebackup = !makebackup; 438 ewprintf("Backup files %sabled", makebackup ? "en" : "dis"); 439 return TRUE; 440 } 441 #endif /* !NO_BACKUP */ 442 443 /* 444 * NB: bp is passed to both ffwopen and ffclose because some 445 * attribute information may need to be updated at open time 446 * and others after the close. This is OS-dependent. Note 447 * that the ff routines are assumed to be able to tell whether 448 * the attribute information has been set up in this buffer 449 * or not. 450 */ 451 452 /* 453 * This function performs the details of file writing; writing the file 454 * in buffer bp to file fn. Uses the file management routines in the 455 * "fileio.c" package. Most of the grief is checking of some sort. 456 */ 457 int 458 writeout(bp, fn) 459 BUFFER *bp; 460 char *fn; 461 { 462 int s; 463 464 /* open writes message */ 465 if ((s = ffwopen(fn, bp)) != FIOSUC) 466 return (FALSE); 467 s = ffputbuf(bp); 468 if (s == FIOSUC) { 469 /* no write error */ 470 s = ffclose(bp); 471 if (s == FIOSUC) 472 ewprintf("Wrote %s", fn); 473 } else 474 /* ignore close error if it is a write error */ 475 (void)ffclose(bp); 476 return s == FIOSUC; 477 } 478 479 /* 480 * Tag all windows for bp (all windows if bp == NULL) as needing their 481 * mode line updated. 482 */ 483 void 484 upmodes(bp) 485 BUFFER *bp; 486 { 487 MGWIN *wp; 488 489 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 490 if (bp == NULL || curwp->w_bufp == bp) 491 wp->w_flag |= WFMODE; 492 } 493