|
|
1.1 root 1: /***************************************************************************
2: * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE *
3: * is provided to you without charge, and with no warranty. You may give *
4: * away copies of JOVE, including sources, provided that this notice is *
5: * included in all the files. *
6: ***************************************************************************/
7:
8: #include "jove.h"
9: #include "list.h"
10: #include "fp.h"
11: #include "termcap.h"
12: #include "ctype.h"
13: #include "disp.h"
14: #include "scandir.h"
15:
16:
17: #ifdef IPROCS
18: # include <signal.h>
19: #endif
20:
21: #ifdef MAC
22: # include "mac.h"
23: #else
24: # include <sys/stat.h>
25: #endif
26:
27: #ifdef UNIX
28: # include <sys/file.h>
29: #endif
30:
31: #ifdef MSDOS
32: # include <fcntl.h>
33: # include <io.h>
34: # include <direct.h>
35: # include <dos.h>
36: #endif /* MSDOS */
37:
38: #include <errno.h>
39:
40: private struct block
41: *b_unlink proto((struct block *)),
42: *lookup proto((/*int*/short));
43:
44: private char
45: *dbackup proto((char *, char *, int)),
46: #if defined(MSDOS)
47: *fixpath proto((char *)),
48: #endif
49: *getblock proto((daddr, int));
50:
51: private void
52: #if defined(MSDOS)
53: abspath proto((char *, char *)),
54: #endif
55: fake_blkio proto((struct block *, int (*)())),
56: DoWriteReg proto((int app)),
57: LRUunlink proto((struct block *)),
58: file_backup proto((char *fname)),
59: real_blkio proto((struct block *, int (*)())),
60: dfollow proto((char *, char *));
61:
62: #if defined(MSDOS)
63: private int
64: Dchdir proto((char *));
65: #endif
66:
67: #ifndef W_OK
68: # define W_OK 2
69: # define F_OK 0
70: #endif
71:
72: #define READ 0
73: #define WRITE 1 /* block operation read or write */
74:
75: long io_chars; /* number of chars in this open_file */
76: int io_lines; /* number of lines in this open_file */
77:
78: #if defined(VMUNIX) || defined(MSDOS)
79: char iobuff[LBSIZE],
80: genbuf[LBSIZE],
81: linebuf[LBSIZE];
82: #else
83: char *iobuff,
84: *genbuf,
85: *linebuf;
86: #endif
87:
88: #ifdef BACKUPFILES
89: int BkupOnWrite = 0;
90: #endif
91:
92: void
93: close_file(fp)
94: File *fp;
95: {
96: if (fp) {
97: if (fp->f_flags & F_TELLALL)
98: add_mess(" %d lines, %D characters.",
99: io_lines,
100: io_chars);
101: f_close(fp);
102: }
103: }
104:
105: /* Write the region from line1/char1 to line2/char2 to FP. This
106: never CLOSES the file since we don't know if we want to. */
107:
108: int EndWNewline = 1;
109:
110: void
111: putreg(fp, line1, char1, line2, char2, makesure)
112: register File *fp;
113: Line *line1,
114: *line2;
115: int char1,
116: char2,
117: makesure;
118: {
119: register int c;
120: register char *lp;
121:
122: if (makesure)
123: (void) fixorder(&line1, &char1, &line2, &char2);
124: while (line1 != line2->l_next) {
125: lp = lcontents(line1) + char1;
126: if (line1 == line2) {
127: fputnchar(lp, (char2 - char1), fp);
128: io_chars += (char2 - char1);
129: } else {
130: while ((c = *lp++) != '\0') {
131: jputc(c, fp);
132: io_chars += 1;
133: }
134: }
135: if (line1 != line2) {
136: io_lines += 1;
137: io_chars += 1;
138: #ifdef MSDOS
139: jputc('\r', fp);
140: #endif /* MSDOS */
141: jputc('\n', fp);
142: }
143: line1 = line1->l_next;
144: char1 = 0;
145: }
146: flush(fp);
147: }
148:
149: private void
150: dofread(fp)
151: register File *fp;
152: {
153: char end[LBSIZE];
154: int xeof = 0;
155: Line *savel = curline;
156: int savec = curchar;
157:
158: strcpy(end, linebuf + curchar);
159: xeof = f_gets(fp, linebuf + curchar, (size_t) (LBSIZE - curchar));
160: SavLine(curline, linebuf);
161: if (!xeof) do {
162: curline = listput(curbuf, curline);
163: xeof = f_getputl(curline, fp);
164: } while (!xeof);
165: getDOT();
166: linecopy(linebuf, (curchar = strlen(linebuf)), end);
167: SavLine(curline, linebuf);
168: IFixMarks(savel, savec, curline, curchar);
169: }
170:
171: void
172: read_file(file, is_insert)
173: char *file;
174: int is_insert;
175: {
176: Bufpos save;
177: File *fp;
178:
179: if (is_insert == NO)
180: curbuf->b_ntbf = NO;
181: fp = open_file(file, iobuff, F_READ, NO, NO);
182: if (fp == NIL) {
183: if (!is_insert && errno == ENOENT)
184: s_mess("(new file)");
185: else
186: s_mess(IOerr("open", file));
187: return;
188: }
189: if (is_insert == NO) {
190: set_ino(curbuf);
191: if (fp->f_flags & F_READONLY) {
192: set_arg_value(1);
193: } else {
194: set_arg_value(0);
195: }
196: TogMinor(ReadOnly);
197: }
198:
199: DOTsave(&save);
200: dofread(fp);
201: if (is_insert && io_chars > 0) {
202: modify();
203: set_mark();
204: }
205: SetDot(&save);
206: getDOT();
207: close_file(fp);
208: }
209:
210: void
211: SaveFile()
212: {
213: if (IsModified(curbuf)) {
214: if (curbuf->b_fname == 0)
215: WriteFile();
216: else {
217: filemunge(curbuf->b_fname);
218: #if !defined(MAC) && !defined(MSDOS)
219: chk_mtime(curbuf, curbuf->b_fname, "save");
220: #endif
221: file_write(curbuf->b_fname, 0);
222: }
223: } else
224: message("No changes need to be written.");
225: }
226:
227: char *HomeDir; /* home directory */
228: size_t HomeLen; /* length of home directory string */
229:
230: private List *DirStack = 0;
231: #define dir_name(dp) ((char *) list_data(dp))
232: #define PWD_PTR (list_data(DirStack))
233: #define PWD ((char *) PWD_PTR)
234:
235: char *
236: pwd()
237: {
238: return (char *) PWD_PTR;
239: }
240:
241: char *
242: pr_name(fname, okay_home)
243: char *fname;
244: int okay_home;
245: {
246: int n;
247:
248: if (fname == 0)
249: return 0;
250: n = numcomp(fname, PWD);
251:
252: if ((PWD[n] == 0) && /* Matched to end of PWD */
253: (fname[n] == '/'))
254: return fname + n + 1;
255:
256: if (okay_home && strcmp(HomeDir, "/") != 0 &&
257: strncmp(fname, HomeDir, HomeLen) == 0 &&
258: fname[HomeLen] == '/') {
259: static char name_buf[100];
260:
261: swritef(name_buf, "~%s", fname + HomeLen);
262: return name_buf;
263: }
264:
265: return fname; /* return entire path name */
266: }
267:
268: #ifdef MSDOS
269: extern unsigned int fmask;
270: #endif /* MSDOS */
271:
272: void
273: Chdir()
274: {
275: char dirbuf[FILESIZE];
276:
277: #ifdef MSDOS
278: fmask = 0x10;
279: #endif
280: (void) ask_file((char *) 0, PWD, dirbuf);
281: #ifdef MSDOS
282: fmask = 0x13;
283: if (Dchdir(dirbuf) == -1)
284: #else
285: if (chdir(dirbuf) == -1)
286: #endif
287: {
288: s_mess("cd: cannot change into %s.", dirbuf);
289: return;
290: }
291: UpdModLine = YES;
292: setCWD(dirbuf);
293: prCWD();
294: #ifdef MAC
295: Bufchange++;
296: #endif
297: }
298:
299: #if defined(UNIX)
300:
301: # if !defined(BSD4_2)
302: char *
303: getwd(buffer)
304: char *buffer;
305: {
306: Buffer *old = curbuf;
307: char *ret_val;
308:
309: SetBuf(do_select((Window *) 0, "pwd-output"));
310: curbuf->b_type = B_PROCESS;
311: (void) UnixToBuf("pwd-output", NO, 0, YES, "/bin/pwd", (char *) 0);
312: ToFirst();
313: strcpy(buffer, linebuf);
314: SetBuf(old);
315: return buffer;
316: }
317: # endif /* not BSD4_2 */
318:
319: /* Check if dn is the name of the current working directory
320: and that it is in cannonical form */
321:
322: int
323: chkCWD(dn)
324: char *dn;
325: {
326: char filebuf[FILESIZE];
327: struct stat dnstat,
328: dotstat;
329:
330: if (dn[0] != '/')
331: return FALSE; /* need absolute pathname */
332: PathParse(dn, filebuf);
333: return stat(filebuf, &dnstat) == 0 &&
334: stat(".", &dotstat) == 0 &&
335: dnstat.st_dev == dotstat.st_dev &&
336: dnstat.st_ino == dotstat.st_ino;
337: }
338:
339: #endif /* UNIX */
340:
341: void
342: setCWD(d)
343: char *d;
344: {
345: if (DirStack == NIL)
346: list_push(&DirStack, (Element *) 0);
347: if (PWD == 0)
348: PWD_PTR = (Element *) emalloc((size_t) (strlen(d) + 1));
349: else
350: PWD_PTR = (Element *) ralloc(PWD, strlen(d) + 1);
351: strcpy(PWD, d);
352: }
353:
354: void
355: getCWD()
356: {
357: char *cwd;
358: char pathname[FILESIZE];
359: #if defined(UNIX) && defined(JOB_CONTROL)
360: extern char *getwd();
361: #endif
362: #if defined(MSDOS)
363: extern char *getcwd();
364: #endif
365:
366: #ifndef MSDOS
367: cwd = getenv("CWD");
368: if (cwd == 0 || !chkCWD(cwd)) {
369: cwd = getenv("PWD");
370: if (cwd == 0 || !chkCWD(cwd))
371: cwd = getwd(pathname);
372: }
373: #else /* MSDOS */
374: cwd = fixpath(getcwd(pathname, FILESIZE));
375: #endif /* MSDOS */
376: setCWD(cwd);
377: }
378:
379: void
380: prDIRS()
381: {
382: register List *lp;
383:
384: s_mess(": %f ");
385: for (lp = DirStack; lp != NIL; lp = list_next(lp))
386: add_mess("%s ", pr_name(dir_name(lp), YES));
387: }
388:
389: void
390: prCWD()
391: {
392: s_mess(": %f => \"%s\"", PWD);
393: }
394:
395: void
396: Pushd()
397: {
398: char *newdir,
399: dirbuf[FILESIZE];
400:
401: #ifdef MSDOS
402: fmask = 0x10;
403: #endif
404: newdir = ask_file((char *) 0, NullStr, dirbuf);
405: #ifdef MSDOS
406: fmask = 0x13;
407: #endif
408: UpdModLine = YES;
409: if (*newdir == 0) { /* Wants to swap top two entries */
410: char *old_top;
411:
412: if (list_next(DirStack) == NIL)
413: complain("pushd: no other directory.");
414: old_top = PWD;
415: list_data(DirStack) = (Element *) dir_name(list_next(DirStack));
416: list_data(list_next(DirStack)) = (Element *) old_top;
417: #ifdef MSDOS
418: (void) Dchdir(PWD);
419: #else
420: (void) chdir(PWD);
421: #endif
422: } else {
423: #ifdef MSDOS
424: if (Dchdir(dirbuf) == -1)
425: #else
426: if (chdir(dirbuf) == -1)
427: #endif
428: {
429: s_mess("pushd: cannot change into %s.", dirbuf);
430: return;
431: }
432: (void) list_push(&DirStack, (Element *) 0);
433: setCWD(dirbuf);
434: }
435: prDIRS();
436: }
437:
438: void
439: Popd()
440: {
441: if (list_next(DirStack) == NIL)
442: complain("popd: directory stack is empty.");
443: UpdModLine = YES;
444: free((char *) list_pop(&DirStack));
445: #ifdef MSDOS
446: (void) Dchdir(PWD); /* If this doesn't work, we's in deep shit. */
447: #else
448: (void) chdir(PWD); /* If this doesn't work, we's in deep shit. */
449: #endif
450: prDIRS();
451: }
452:
453: private char *
454: dbackup(base, offset, c)
455: register char *base,
456: *offset;
457: register int c;
458: {
459: while (offset > base && *--offset != c)
460: ;
461: return offset;
462: }
463:
464: private void
465: dfollow(file, into)
466: char *file,
467: *into;
468: {
469: char *dp,
470: #ifdef MSDOS
471: filefix[FILESIZE],
472: #endif
473: *sp;
474:
475: #ifndef MSDOS
476: if (*file == '/') { /* Absolute pathname */
477: strcpy(into, "/");
478: file += 1;
479: #ifdef apollo
480: /* handle apollo "//..." */
481: if (*file == '/') {
482: strcpy(into+1, "/");
483: file += 1;
484: }
485: #endif
486: } else
487: strcpy(into, PWD);
488: #else
489: abspath(file, filefix); /* convert to absolute pathname */
490: strcpy(into, filefix); /* and forget about drives */
491: into[3] = 0;
492: into = &(into[2]);
493: file = &(filefix[3]);
494: #endif
495: dp = into + strlen(into);
496:
497: sp = file;
498: do {
499: if (*file == 0)
500: break;
501: if ((sp = strchr(file, '/')) != '\0')
502: *sp = 0;
503: if (strcmp(file, ".") == 0)
504: ; /* So it will get to the end of the loop */
505: else if (strcmp(file, "..") == 0) {
506: *(dp = dbackup(into, dp, '/')) = 0;
507: if (dp == into)
508: strcpy(into, "/"), dp = into + 1;
509: } else {
510: if (into[strlen(into) - 1] != '/')
511: (void) strcat(into, "/"), dp += 1;
512: (void) strcat(into, file);
513: dp += strlen(file); /* stay at the end */
514: }
515: file = sp + 1;
516: } while (sp != 0);
517: }
518:
519: #if defined(UNIX)
520:
521: # if defined(YP_PASSWD)
522:
523: #include <pwd.h>
524:
525: private void
526: get_hdir(user, buf)
527: register char *user,
528: *buf;
529: {
530: struct passwd *p;
531:
532: p = getpwnam(user);
533: endpwent();
534: if (p == NULL) {
535: add_mess(" [unknown user: %s]", user);
536: SitFor(7);
537: complain((char *) 0);
538: /* NOTREACHED */
539: }
540: strcpy(buf, p->pw_dir);
541: }
542:
543: #else
544:
545: private
546: get_hdir(user, buf)
547: register char *user,
548: *buf;
549: {
550: char fbuf[LBSIZE],
551: pattern[100];
552: register int u_len;
553: File *fp;
554:
555: u_len = strlen(user);
556: fp = open_file("/etc/passwd", fbuf, F_READ, YES, YES);
557: swritef(pattern, "%s:[^:]*:[^:]*:[^:]*:[^:]*:\\([^:]*\\):", user);
558: while (f_gets(fp, genbuf, LBSIZE) != EOF)
559: if ((strncmp(genbuf, user, u_len) == 0) &&
560: (LookingAt(pattern, genbuf, 0))) {
561: putmatch(1, buf, FILESIZE);
562: close_file(fp);
563: return;
564: }
565: close_file(fp);
566: add_mess(" [unknown user: %s]", user);
567: SitFor(7);
568: complain((char *) 0);
569: }
570:
571: #endif /* YP_PASSWD */
572: #endif /* UNIX */
573:
574: void
575: PathParse(name, intobuf)
576: char *name,
577: *intobuf;
578: {
579: char localbuf[FILESIZE];
580:
581: intobuf[0] = localbuf[0] = '\0';
582: if (*name == '\0')
583: return;
584: if (*name == '~') {
585: if (name[1] == '/' || name[1] == '\0') {
586: strcpy(localbuf, HomeDir);
587: name += 1;
588: }
589: #if !(defined(MSDOS) || defined(MAC)) /* may add for mac in future */
590: else {
591: char *uendp = strchr(name, '/'),
592: unamebuf[30];
593:
594: if (uendp == 0)
595: uendp = name + strlen(name);
596: name += 1;
597: null_ncpy(unamebuf, name, (size_t) (uendp - name));
598: get_hdir(unamebuf, localbuf);
599: name = uendp;
600: }
601: #endif
602: }
603: #ifndef MSDOS
604: else if (*name == '\\')
605: name += 1;
606: #endif /* MSDOS */
607: (void) strcat(localbuf, name);
608: dfollow(localbuf, intobuf);
609: }
610:
611: void
612: filemunge(newname)
613: char *newname;
614: {
615: struct stat stbuf;
616:
617: if (newname == 0)
618: return;
619: if (stat(newname, &stbuf))
620: return;
621: #ifndef MSDOS
622: if (((stbuf.st_dev != curbuf->b_dev) ||
623: (stbuf.st_ino != curbuf->b_ino)) &&
624: #else /* MSDOS */
625: if ( /* (stbuf.st_ino != curbuf->b_ino) && */
626: #endif /* MSDOS */
627: #ifndef MAC
628: ((stbuf.st_mode & S_IFMT) != S_IFCHR) &&
629: #endif
630: (curbuf->b_fname==NIL || strcmp(newname, curbuf->b_fname) != 0)) {
631: rbell();
632: confirm("\"%s\" already exists; overwrite it? ", newname);
633: }
634: }
635:
636: void
637: WrtReg()
638: {
639: DoWriteReg(NO);
640: }
641:
642: void
643: AppReg()
644: {
645: DoWriteReg(YES);
646: }
647:
648: int CreatMode = DFLT_MODE;
649:
650: private void
651: DoWriteReg(app)
652: int app;
653: {
654: char fnamebuf[FILESIZE],
655: *fname;
656: Mark *mp = CurMark();
657: File *fp;
658:
659: /* Won't get here if there isn't a Mark */
660: fname = ask_file((char *) 0, (char *) 0, fnamebuf);
661:
662: #ifdef BACKUPFILES
663: if (app == NO) {
664: filemunge(fname);
665:
666: if (BkupOnWrite)
667: file_backup(fname);
668: }
669: #else
670: if (!app)
671: filemunge(fname);
672: #endif
673:
674: fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, YES, NO);
675: putreg(fp, mp->m_line, mp->m_char, curline, curchar, YES);
676: close_file(fp);
677: }
678:
679: int OkayBadChars = 0;
680:
681: void
682: WriteFile()
683: {
684: char *fname,
685: fnamebuf[FILESIZE];
686: #ifdef MAC
687: if (Macmode) {
688: if(!(fname = pfile(fnamebuf))) return;
689: }
690: else
691: #endif /* MAC */
692:
693: fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
694: /* Don't allow bad characters when creating new files. */
695: if (!OkayBadChars
696: && (curbuf->b_fname==NIL || strcmp(curbuf->b_fname, fnamebuf) != 0))
697: {
698: #ifdef UNIX
699: static char *badchars = "!$^&*()~`{}\"'\\|<>? ";
700: #endif /* UNIX */
701: #ifdef MSDOS
702: static char *badchars = "*|<>? ";
703: #endif /* MSDOS */
704: #ifdef MAC
705: static char *badchars = ":";
706: #endif /* MAC */
707: register char *cp = fnamebuf;
708: register int c;
709:
710: while ((c = *cp++ & CHARMASK) != '\0') /* avoid sign extension... */
711: if (c < ' ' || c == '\177' || strchr(badchars, c))
712: complain("'%p': bad character in filename.", c);
713: }
714:
715: #if !defined(MAC) && !defined(MSDOS)
716: chk_mtime(curbuf, fname, "write");
717: #endif
718: filemunge(fname);
719: curbuf->b_type = B_FILE; /* in case it wasn't before */
720: setfname(curbuf, fname);
721: file_write(fname, 0);
722: }
723:
724: /* Open file FNAME supplying the buffer IO routine with buffer BUF.
725: HOW is F_READ, F_WRITE or F_APPEND. IFBAD == COMPLAIN means that
726: if we fail at opening the file, call complain. LOUDNESS says
727: whether or not to print the "reading ..." message on the message
728: line.
729:
730: NOTE: This opens the pr_name(fname, NO) of fname. That is, FNAME
731: is usually an entire pathname, which can be slow when the
732: pathname is long and there are lots of symbolic links along
733: the way (which has become very common in my experience). So,
734: this speeds up opens file names in the local directory. It
735: will not speed up things like "../scm/foo.scm" simple because
736: by the time we get here that's already been expanded to an
737: absolute pathname. But this is a start.
738: */
739:
740: File *
741: open_file(fname, buf, how, complainifbad, quiet)
742: register char *fname;
743: char *buf;
744: register int how;
745: int complainifbad,
746: quiet;
747: {
748: register File *fp;
749:
750: io_chars = 0;
751: io_lines = 0;
752:
753: fp = f_open(pr_name(fname, NO), how, buf, LBSIZE);
754: if (fp == NIL) {
755: message(IOerr((how == F_READ) ? "open" : "create", fname));
756: if (complainifbad)
757: complain((char *) 0);
758: } else {
759: int rd_only = FALSE;
760: #ifndef MAC
761: if (access(pr_name(fname, NO), W_OK) == -1 && errno != ENOENT) {
762: rd_only = TRUE;
763: fp->f_flags |= F_READONLY;
764: }
765: #endif
766: if (!quiet) {
767: fp->f_flags |= F_TELLALL;
768: f_mess("\"%s\"%s", pr_name(fname, YES),
769: rd_only ? " [Read only]" : NullStr);
770: }
771: }
772: return fp;
773: }
774:
775: #ifndef MSDOS
776: /* Check to see if the file has been modified since it was
777: last written. If so, make sure they know what they're
778: doing.
779:
780: I hate to use another stat(), but to use confirm we gotta
781: do this before we open the file.
782:
783: NOTE: This stats FNAME after converting it to a path-relative
784: name. I can't see why this would cause a problem ...
785: */
786:
787: void
788: chk_mtime(thisbuf, fname, how)
789: Buffer *thisbuf;
790: char *fname,
791: *how;
792: {
793: struct stat stbuf;
794: Buffer *b;
795: char *mesg = "Shall I go ahead and %s anyway? ";
796:
797: if ((thisbuf->b_mtime != 0) && /* if we care ... */
798: ((b = file_exists(fname)) != NIL) && /* we already have this file */
799: (b == thisbuf) && /* and it's the current buffer */
800: (stat(pr_name(fname, NO), &stbuf) != -1) && /* and we can stat it */
801: (stbuf.st_mtime != b->b_mtime)) { /* and there's trouble. */
802: rbell();
803: redisplay(); /* Ring that bell! */
804: TOstart("Warning", TRUE);
805: Typeout("\"%s\" now saved on disk is not what you last", pr_name(fname, YES));
806: Typeout("visited or saved. Probably someone else is editing");
807: Typeout("your file at the same time.");
808: if (how) {
809: Typeout("");
810: Typeout("Type \"y\" if I should %s, anyway.", how);
811: f_mess(mesg, how);
812: }
813: TOstop();
814: if (how)
815: confirm(mesg, how);
816: }
817: }
818:
819: #endif /* MSDOS */
820:
821: void
822: file_write(fname, app)
823: char *fname;
824: int app;
825: {
826: File *fp;
827:
828: #ifdef BACKUPFILES
829: if (!app && BkupOnWrite)
830: file_backup(fname);
831: #endif
832:
833: fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, YES, NO);
834:
835: if (EndWNewline) { /* Make sure file ends with a newLine */
836: Bufpos save;
837:
838: DOTsave(&save);
839: ToLast();
840: if (length(curline)) /* Not a blank Line */
841: LineInsert(1);
842: SetDot(&save);
843: }
844: putreg(fp, curbuf->b_first, 0, curbuf->b_last, length(curbuf->b_last), NO);
845: close_file(fp);
846: set_ino(curbuf);
847: unmodify();
848: }
849:
850: void
851: ReadFile()
852: {
853: Buffer *bp;
854: char *fname,
855: fnamebuf[FILESIZE];
856: int lineno;
857:
858: #ifdef MAC
859: if(Macmode) {
860: if(!(fname = gfile(fnamebuf))) return;
861: }
862: else
863: #endif /* MAC */
864: fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
865: #if !(defined(MSDOS) || defined(MAC))
866: chk_mtime(curbuf, fname, "read");
867: #endif /* MSDOS || MAC */
868:
869: if (IsModified(curbuf)) {
870: char *y_or_n;
871: int c;
872:
873: for (;;) {
874: rbell();
875: y_or_n = ask(NullStr, "Shall I make your changes to \"%s\" permanent? ", curbuf->b_name);
876: c = CharUpcase(*y_or_n);
877: if (c == 'Y' || c == 'N')
878: break;
879: }
880: if (c == 'Y')
881: SaveFile();
882: }
883:
884: if ((bp = file_exists(fnamebuf)) != NIL &&
885: (bp == curbuf))
886: lineno = pnt_line() - 1;
887: else
888: lineno = 0;
889:
890: unmodify();
891: initlist(curbuf);
892: setfname(curbuf, fname);
893: read_file(fname, 0);
894: SetLine(next_line(curbuf->b_first, lineno));
895: }
896:
897: void
898: InsFile()
899: {
900: char *fname,
901: fnamebuf[FILESIZE];
902: #ifdef MAC
903: if(Macmode) {
904: if(!(fname = gfile(fnamebuf))) return;
905: }
906: else
907: #endif /* MAC */
908: fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
909: read_file(fname, 1);
910: }
911:
912: #include "temp.h"
913:
914: int DOLsave = 0; /* Do Lsave flag. If lines aren't being saved
915: when you think they should have been, this
916: flag is probably not being set, or is being
917: cleared before lsave() was called. */
918:
919: private int nleft, /* number of good characters left in current block */
920: tmpfd = -1;
921: daddr DFree = 1; /* pointer to end of tmp file */
922: private char *tfname;
923:
924: void
925: tmpinit()
926: {
927: char buf[FILESIZE];
928:
929: #ifdef MAC
930: swritef(buf, "%s/%s", HomeDir, d_tempfile);
931: #else
932: swritef(buf, "%s/%s", TmpFilePath, d_tempfile);
933: #endif
934: tfname = copystr(buf);
935: tfname = mktemp(tfname);
936: (void) close(creat(tfname, 0600));
937: #ifndef MSDOS
938: tmpfd = open(tfname, 2);
939: #else /* MSDOS */
940: tmpfd = open(tfname, 0x8002); /* MSDOS fix */
941: #endif /* MSDOS */
942: if (tmpfd == -1)
943: complain("Warning: cannot create tmp file!");
944: }
945:
946: void
947: tmpclose()
948: {
949: if (tmpfd == -1)
950: return;
951: (void) close(tmpfd);
952: tmpfd = -1;
953: (void) unlink(tfname);
954: }
955:
956: /* get a line at `tl' in the tmp file into `buf' which should be LBSIZE
957: long */
958:
959: int Jr_Len; /* length of Just Read Line */
960:
961: #ifdef MAC /* The Lighspeed compiler can't copy with static here */
962: char *getblock();
963: #else
964: private char *getblock();
965: #endif
966: void
967: getline(addr, buf)
968: daddr addr;
969: register char *buf;
970: {
971: register char *bp,
972: *lp;
973:
974: lp = buf;
975: bp = getblock(addr >> 1, READ);
976: do ; while ((*lp++ = *bp++) != '\0');
977: Jr_Len = (lp - buf) - 1;
978: }
979:
980: /* Put `buf' and return the disk address */
981:
982: daddr
983: putline(buf)
984: char *buf;
985: {
986: register char *bp,
987: *lp;
988: register int nl;
989: daddr free_ptr;
990:
991: lp = buf;
992: free_ptr = DFree;
993: bp = getblock(free_ptr, WRITE);
994: nl = nleft;
995: free_ptr = blk_round(free_ptr);
996: while ((*bp = *lp++) != '\0') {
997: if (*bp++ == '\n') {
998: *--bp = 0;
999: break;
1000: }
1001: if (--nl == 0) {
1002: free_ptr = forward_block(free_ptr);
1003: DFree = free_ptr;
1004: bp = getblock(free_ptr, WRITE);
1005: lp = buf; /* start over ... */
1006: nl = nleft;
1007: }
1008: }
1009: free_ptr = DFree;
1010: DFree += (((lp - buf) + CH_SIZE - 1) / CH_SIZE);
1011: /* (lp - buf) includes the null */
1012: return (free_ptr << 1);
1013: }
1014:
1015: /* The theory is that critical section of code inside this procedure
1016: will never cause a problem to occur. Basically, we need to ensure
1017: that two blocks are in memory at the same time, but I think that
1018: this can never screw up. */
1019:
1020: #define lockblock(addr)
1021: #define unlockblock(addr)
1022:
1023: daddr
1024: f_getputl(line, fp)
1025: Line *line;
1026: register File *fp;
1027: {
1028: register char *bp;
1029: register int c,
1030: nl,
1031: room = LBSIZE;
1032: daddr free_ptr;
1033: char *base;
1034: #ifdef MSDOS
1035: char crleft = 0;
1036: #endif /* MSDOS */
1037:
1038: free_ptr = DFree;
1039: base = bp = getblock(free_ptr, WRITE);
1040: nl = nleft;
1041: free_ptr = blk_round(free_ptr);
1042: while (--room > 0) {
1043: #ifdef MSDOS
1044: if (crleft) {
1045: c = crleft;
1046: crleft = 0;
1047: } else
1048: #endif /* MSDOS */
1049: c = jgetc(fp);
1050: if (c == EOF || c == '\n')
1051: break;
1052: #ifdef MSDOS
1053: if (c == '\r')
1054: if ((crleft = jgetc(fp)) == '\n') {
1055: crleft = 0;
1056: break;
1057: }
1058: #endif /* MSDOS */
1059: if (--nl == 0) {
1060: char *newbp;
1061: size_t nbytes;
1062:
1063: lockblock(free_ptr);
1064: DFree = free_ptr = forward_block(free_ptr);
1065: nbytes = bp - base;
1066: newbp = getblock(free_ptr, WRITE);
1067: nl = nleft;
1068: byte_copy(base, newbp, nbytes);
1069: bp = newbp + nbytes;
1070: base = newbp;
1071: unlockblock(free_ptr);
1072: }
1073: *bp++ = c;
1074: }
1075: *bp++ = '\0';
1076: free_ptr = DFree;
1077: DFree += (((bp - base) + CH_SIZE - 1) / CH_SIZE);
1078: line->l_dline = (free_ptr << 1);
1079: if (room == 0) {
1080: add_mess(" [Line too long]");
1081: rbell();
1082: return EOF;
1083: }
1084: if (c == EOF) {
1085: if (--bp != base)
1086: add_mess(" [Incomplete last line]");
1087: return EOF;
1088: }
1089: io_lines += 1;
1090: return 0;
1091: }
1092:
1093: typedef struct block {
1094: short b_dirty,
1095: b_bno;
1096: char b_buf[JBUFSIZ];
1097: struct block
1098: *b_LRUnext,
1099: *b_LRUprev,
1100: *b_HASHnext;
1101: } Block;
1102:
1103: #define HASHSIZE 7 /* Primes work best (so I'm told) */
1104: #define B_HASH(bno) ((bno) % HASHSIZE)
1105:
1106: #ifdef MAC
1107: private Block *b_cache,
1108: #else
1109: private Block b_cache[NBUF],
1110: #endif
1111: *bht[HASHSIZE], /* Block hash table. Must be zero initially */
1112: *f_block = 0,
1113: *l_block = 0;
1114: private int max_bno = -1,
1115: NBlocks;
1116:
1117: #ifdef MAC
1118: void (*blkio)();
1119: #else
1120: private void (*blkio) proto((Block *, int (*)()));
1121: #endif /* MAC */
1122:
1123: #ifdef MAC
1124: make_cache() /* Only 32K of static space on Mac, so... */
1125: {
1126: return((b_cache = (Block *) calloc(NBUF,sizeof(Block))) == 0 ? 0 : 1);
1127: }
1128: #endif /* MAC */
1129:
1130: extern int read(), write();
1131:
1132: private void
1133: real_blkio(b, iofcn)
1134: register Block *b;
1135: #if defined(MAC) || defined(IBMPC)
1136: register int (*iofcn)();
1137: #else
1138: register int (*iofcn) proto((int, UnivPtr, size_t));
1139: #endif /* MAC */
1140: {
1141: (void) lseek(tmpfd, (long) ((unsigned) b->b_bno) * JBUFSIZ, 0);
1142: if ((*iofcn)(tmpfd, b->b_buf, (size_t)JBUFSIZ) != JBUFSIZ)
1143: error("[Tmp file %s error; to continue editing would be dangerous]",
1144: (iofcn == read) ? "READ" : "WRITE");
1145: }
1146:
1147: private void
1148: fake_blkio(b, iofcn)
1149: register Block *b;
1150: register int (*iofcn)();
1151: {
1152: tmpinit();
1153: blkio = real_blkio;
1154: real_blkio(b, iofcn);
1155: }
1156:
1157: void
1158: d_cache_init()
1159: {
1160: register Block *bp, /* Block pointer */
1161: **hp; /* Hash pointer */
1162: register short bno;
1163:
1164: for (bp = b_cache, bno = NBUF; --bno >= 0; bp++) {
1165: NBlocks += 1;
1166: bp->b_dirty = 0;
1167: bp->b_bno = bno;
1168: if (l_block == 0)
1169: l_block = bp;
1170: bp->b_LRUprev = 0;
1171: bp->b_LRUnext = f_block;
1172: if (f_block != 0)
1173: f_block->b_LRUprev = bp;
1174: f_block = bp;
1175:
1176: bp->b_HASHnext = *(hp = &bht[B_HASH(bno)]);
1177: *hp = bp;
1178: }
1179: blkio = fake_blkio;
1180: }
1181:
1182: void
1183: SyncTmp()
1184: {
1185: register Block *b;
1186: #ifdef IBMPC
1187: register int bno = 0;
1188:
1189: /* sync the blocks in order, for file systems that don't allow
1190: holes (MSDOS). Perhaps this benefits floppy-based file systems. */
1191:
1192: for (bno = 0; bno <= max_bno; ) {
1193: if ((b = lookup(bno++)) && b->b_dirty) {
1194: (*blkio)(b, write);
1195: b->b_dirty = 0;
1196: }
1197: }
1198: #else
1199: for (b = f_block; b != 0; b = b->b_LRUnext)
1200: if (b->b_dirty) {
1201: (*blkio)(b, write);
1202: b->b_dirty = 0;
1203: }
1204: #endif
1205: }
1206:
1207: private Block *
1208: lookup(bno)
1209: register short bno;
1210: {
1211: register Block *bp;
1212:
1213: for (bp = bht[B_HASH(bno)]; bp != 0; bp = bp->b_HASHnext)
1214: if (bp->b_bno == bno)
1215: break;
1216: return bp;
1217: }
1218:
1219: private void
1220: LRUunlink(b)
1221: register Block *b;
1222: {
1223: if (b->b_LRUprev == 0)
1224: f_block = b->b_LRUnext;
1225: else
1226: b->b_LRUprev->b_LRUnext = b->b_LRUnext;
1227: if (b->b_LRUnext == 0)
1228: l_block = b->b_LRUprev;
1229: else
1230: b->b_LRUnext->b_LRUprev = b->b_LRUprev;
1231: }
1232:
1233: private Block *
1234: b_unlink(bp)
1235: register Block *bp;
1236: {
1237: register Block *hp,
1238: *prev = 0;
1239:
1240: LRUunlink(bp);
1241: /* Now that we have the block, we remove it from its position
1242: in the hash table, so we can THEN put it somewhere else with
1243: it's new block assignment. */
1244:
1245: for (hp = bht[B_HASH(bp->b_bno)]; hp != 0; prev = hp, hp = hp->b_HASHnext)
1246: if (hp == bp)
1247: break;
1248: if (hp == 0) {
1249: writef("\rBlock %d missing!", bp->b_bno);
1250: finish(0);
1251: }
1252: if (prev)
1253: prev->b_HASHnext = hp->b_HASHnext;
1254: else
1255: bht[B_HASH(bp->b_bno)] = hp->b_HASHnext;
1256:
1257: if (bp->b_dirty) { /* do, now, the delayed write */
1258: (*blkio)(bp, write);
1259: bp->b_dirty = 0;
1260: }
1261:
1262: return bp;
1263: }
1264:
1265: /* Get a block which contains at least part of the line with the address
1266: atl. Returns a pointer to the block and sets the global variable
1267: nleft (number of good characters left in the buffer). */
1268:
1269: private char *
1270: getblock(atl, iof)
1271: daddr atl;
1272: int iof;
1273: {
1274: register int bno,
1275: off;
1276: register Block *bp;
1277: static Block *lastb = 0;
1278:
1279: bno = da_to_bno(atl);
1280: off = da_to_off(atl);
1281: if (da_too_huge(atl))
1282: error("Tmp file too large. Get help!");
1283: nleft = JBUFSIZ - off;
1284: if (lastb != 0 && lastb->b_bno == bno) {
1285: lastb->b_dirty |= iof;
1286: return lastb->b_buf + off;
1287: }
1288:
1289: /* The requested block already lives in memory, so we move
1290: it to the end of the LRU list (making it Most Recently Used)
1291: and then return a pointer to it. */
1292: if ((bp = lookup(bno)) != NIL) {
1293: if (bp != l_block) {
1294: LRUunlink(bp);
1295: if (l_block == 0)
1296: f_block = l_block = bp;
1297: else
1298: l_block->b_LRUnext = bp;
1299: bp->b_LRUprev = l_block;
1300: l_block = bp;
1301: bp->b_LRUnext = 0;
1302: }
1303: if (bp->b_bno > max_bno)
1304: max_bno = bp->b_bno;
1305: bp->b_dirty |= iof;
1306: lastb = bp;
1307: return bp->b_buf + off;
1308: }
1309:
1310: /* The block we want doesn't reside in memory so we take the
1311: least recently used clean block (if there is one) and use
1312: it. */
1313: bp = f_block;
1314: if (bp->b_dirty) /* The best block is dirty ... */
1315: SyncTmp();
1316:
1317: bp = b_unlink(bp);
1318: if (l_block == 0)
1319: l_block = f_block = bp;
1320: else
1321: l_block->b_LRUnext = bp; /* Place it at the end ... */
1322: bp->b_LRUprev = l_block;
1323: l_block = bp;
1324: bp->b_LRUnext = 0; /* so it's Most Recently Used */
1325:
1326: bp->b_dirty = iof;
1327: bp->b_bno = bno;
1328: bp->b_HASHnext = bht[B_HASH(bno)];
1329: bht[B_HASH(bno)] = bp;
1330:
1331: /* Get the current contents of the block UNLESS this is a new
1332: block that's never been looked at before, i.e., it's past
1333: the end of the tmp file. */
1334:
1335: if (bp->b_bno <= max_bno)
1336: (*blkio)(bp, read);
1337: else
1338: max_bno = bno;
1339:
1340: lastb = bp;
1341: return bp->b_buf + off;
1342: }
1343:
1344: char *
1345: lbptr(line)
1346: Line *line;
1347: {
1348: return getblock(line->l_dline >> 1, READ);
1349: }
1350:
1351: /* save the current contents of linebuf, if it has changed */
1352:
1353: void
1354: lsave()
1355: {
1356: if (curbuf == 0 || !DOLsave) /* Nothing modified recently */
1357: return;
1358:
1359: if (strcmp(lbptr(curline), linebuf) != 0)
1360: SavLine(curline, linebuf); /* Put linebuf on the disk. */
1361: DOLsave = 0;
1362: }
1363:
1364: #ifdef BACKUPFILES
1365: private void
1366: file_backup(fname)
1367: char *fname;
1368: {
1369: #ifndef MSDOS
1370: char *s;
1371: register int i;
1372: int fd1,
1373: fd2;
1374: char tmp1[JBUFSIZ],
1375: tmp2[JBUFSIZ];
1376: struct stat buf;
1377: int mode;
1378:
1379: strcpy(tmp1, fname);
1380: if ((s = strrchr(tmp1, '/')) == NULL)
1381: swritef(tmp2, "#%s~", fname);
1382: else {
1383: *s++ = '\0';
1384: swritef(tmp2, "%s/#%s~", tmp1, s);
1385: }
1386:
1387: if ((fd1 = open(fname, 0)) < 0)
1388: return;
1389:
1390: /* create backup file with same mode as input file */
1391: #ifndef MAC
1392: if (fstat(fd1, &buf) != 0)
1393: mode = CreatMode;
1394: else
1395: #endif
1396: mode = buf.st_mode;
1397:
1398: if ((fd2 = creat(tmp2, mode)) < 0) {
1399: (void) close(fd1);
1400: return;
1401: }
1402: while ((i = read(fd1, tmp1, sizeof(tmp1))) > 0)
1403: write(fd2, tmp1, (size_t) i);
1404: #ifdef BSD4_2
1405: (void) fsync(fd2);
1406: #endif
1407: (void) close(fd2);
1408: (void) close(fd1);
1409: #else /* MSDOS */
1410: char *dot,
1411: *slash,
1412: tmp[FILESIZE];
1413:
1414: strcpy(tmp, fname);
1415: slash = basename(tmp);
1416: if (dot = strrchr(slash, '.')) {
1417: if (!stricmp(dot,".bak"))
1418: return;
1419: else *dot = 0;
1420: }
1421: strcat(tmp, ".bak");
1422: unlink(tmp);
1423: rename(fname, tmp);
1424: #endif /* MSDOS */
1425: }
1426: #endif
1427:
1428: #if defined(MSDOS)
1429:
1430: private int /* chdir + drive */
1431: Dchdir(to)
1432: char *to;
1433: {
1434: unsigned d, dd, n;
1435:
1436: if (to[1] == ':') {
1437: d = to[0];
1438: if (d >= 'a') d = d - 'a' + 1;
1439: if (d >= 'A') d = d - 'A' + 1;
1440: _dos_getdrive(&dd);
1441: if (dd != d)
1442: _dos_setdrive(d, &n);
1443: if (to[2] == 0)
1444: return 0;
1445: }
1446: return chdir(to);
1447: }
1448:
1449: private char *
1450: fixpath(p)
1451: char *p;
1452: {
1453: char *pp = p;
1454:
1455: while (*p) {
1456: if (*p == '\\')
1457: *p = '/';
1458: p++;
1459: }
1460: return(strlwr(pp));
1461: }
1462:
1463:
1464: private void
1465: abspath(so, dest)
1466: char *so, *dest;
1467: {
1468: char cwd[FILESIZE], cwdD[3], cwdDIR[FILESIZE], cwdF[9], cwdEXT[5],
1469: soD[3], soDIR[FILESIZE], soF[9], soEXT[5];
1470: char *drive, *path;
1471:
1472: _splitpath(fixpath(so), soD, soDIR, soF, soEXT);
1473: getcwd(cwd, FILESIZE);
1474: if (*soD != 0) {
1475: Dchdir(soD); /* this is kinda messy */
1476: getcwd(cwdDIR, FILESIZE); /* should probably just */
1477: Dchdir(cwd); /* call DOS to do it */
1478: strcpy(cwd, cwdDIR);
1479: }
1480: (void) fixpath(cwd);
1481: if (cwd[strlen(cwd)-1] != '/')
1482: strcat(cwd, "/x.x"); /* need dummy filename */
1483:
1484: _splitpath(fixpath(cwd), cwdD, cwdDIR, cwdF, cwdEXT);
1485:
1486: drive = (*soD == 0) ? cwdD : soD;
1487:
1488: if (*soDIR != '/')
1489: path = strcat(cwdDIR, soDIR);
1490: else
1491: path = soDIR;
1492: _makepath(dest, drive, path, soF, soEXT);
1493: fixpath(dest); /* can't do it often enough */
1494: }
1495:
1496: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.