chris@umcp-cs.UUCP (08/29/83)
: Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting abspath.c' sed 's/^X//' <<'//go.sysin dd *' >abspath.c X/* convert a pathname to an absolute one, if it is absolute already, it is returned in the buffer unchanged, otherwise leading "./"s will be removed, the name of the current working directory will be prepended, and "../"s will be resolved. In a moment of weakness, I have implemented the cshell ~ filename convention. ~/foobar will have the ~ replaced by the home directory of the current user. ~user/foobar will have the ~user replaced by the home directory of the named user. This should really be in the kernel (or be replaced by a better kernel mechanism). Doing file name expansion like this in a user-level program leads to some very distasteful non-uniformities. Another fit of dementia has led me to implement the expansion of shell environment variables. $HOME/mbox is the same as ~/mbox. If the environment variable a = "foo" and b = "bar" then: $a => foo $a$b => foobar $a.c => foo.c xxx$a => xxxfoo ${a}! => foo! James Gosling @ CMU */ #include "config.h" #include <sys/types.h> #include <sys/stat.h> #include <pwd.h> #include "keyboard.h" #include "mlisp.h" #include "ctype.h" #ifdef LIBNDIR #include <dir.h> #endif LIBNDIR X/* Hack! */ #define CHDIR(dirname) syscall(12,dirname) static char curwd[MaxPathNameLen];/* the current working directory is remembered here. chdir()'s are trapped and this gets updated. */ char *getenv (); abspath (nm, buf) /* input name in nm, absolute pathname output to buf. returns -1 if the pathname cannot be successfully converted (only happens if the current directory cannot be found) */ char *nm, * buf; { register char *s, *d; char lnm[1000]; s = nm; d = lnm; while (*d++ = *s) if (*s++ == '$') { register char *start = d; register braces = *s == '{'; register char *value; while (*d++ = *s) if (braces ? *s == '}' : !isalnum (*s)) break; else s++; *--d = 0; value = getenv (braces ? start + 1 : start); if (value) { for (d = start - 1; *d++ = *value++;); d--; if (braces && *s) s++; } } d = buf; s = curwd; nm = lnm; if (nm[0] == '~') /* prefix ~ */ if (nm[1] == '/' || nm[1] == 0)/* ~/filename */ if (s = getenv ("HOME")) { if (*++nm) nm++; } else s = ""; else { /* ~user/filename */ register char *nnm; register struct passwd *pw; for (s = nm; *s && *s != '/'; s++); nnm = *s ? s + 1 : s; *s = 0; pw = (struct passwd *) getpwnam (nm + 1); if (pw == 0) { error ("\"%s\" isn't a registered user.", nm+1); s = ""; } else { nm = nnm; s = pw -> pw_dir; } } while (*d++ = *s++); *(d - 1) = '/'; s = nm; if (*s == '/') d = buf; while (*d++ = *s++); *(d - 1) = '/'; *d = '\0'; d = buf; s = buf; while (*s) if ((*d++ = *s++) == '/' && d > buf + 1) { register char *t = d - 2; switch (*t) { case '/': /* found // in the name */ --d; break; case '.': switch (*--t) { case '/': /* found /./ in the name */ d -= 2; break; case '.': if (*--t == '/') {/* found /../ */ while (t > buf && *--t != '/'); d = t + 1; } break; } break; } } if (*(d - 1) == '/' && d > buf+1) d--; *d = '\0'; return 0; } X/* * getwd - get current working directory (algorithm from /bin/pwd) * * Author: Mike Accetta, 19-May-78 * ********************************************************************** * HISTORY * 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University * Modified (by Mike Accetta) for VAX. I tried using a "popen" on "pwd" * to achieve the same effect; there's less risk since errors in pwd * don't trash the current directory of the calling program; however, * it takes about two full seconds (this routine takes about zero time). * This routine wins. * * 10-Jun-83 Chris Kent (cak) at DecWRL * Upgraded to new directory access routines for 4.1cBSD * * 10-Jul-83 Chris Torek at Umcp-Cs * #ifdefs for new directory vs. straight open/read * ********************************************************************** * * Remarks: * * The name of the current working directory is copied into * the supplied string `wdir'. The current working directory * is changed during the execution of the routine and restored * at the end by a chdir(wdir). If an error occurs the current * working directory is undefined. */ getwd (wdir) char *wdir; { #ifdef LIBNDIR #define isbad(xx) (xx) == NULL /* used on opendir() */ #define readit() (dirp = readdir (fd)) #define skipit() 0 struct direct *dirp; register DIR *fd; #else LIBNDIR #define isbad(xx) (xx) < 0 /* used on opendir() (ie open()) */ #define closedir(d) close (d) #define opendir(d) open (d, 0) #define dirp (&db) #define readit() read (fd, &db, 16) == 16 #define skipit() db.d_inode == 0 struct { ino_t d_inode; char d_name[15]; /* long enough to add \0 */ } db; register int fd; #endif LIBNDIR char temp[MaxPathNameLen]; struct stat sb, sbc, root; register int found; /* Initially root */ strcpy (wdir, "/"); stat ("/", &root); #ifndef LIBNDIR db.d_name[14] = 0; #endif LIBNDIR for (;;) { if (isbad (fd = opendir (".."))) return (-1); if (stat (".", &sbc) < 0 || stat ("..", &sb) < 0) goto out; if (sbc.st_ino == root.st_ino && sbc.st_dev == root.st_dev) { closedir (fd); return CHDIR (wdir); } if (sbc.st_ino == sb.st_ino && sbc.st_dev == sb.st_dev) { closedir (fd); CHDIR ("/"); if (isbad (fd = opendir ("."))) return (-1); if (stat (".", &sb) < 0) goto out; /* scan the root directory for directory with same device */ if (sbc.st_dev != sb.st_dev) { while (readit ()) { if (skipit ()) continue; if (stat (dirp->d_name, &sb) < 0) goto out; if (sbc.st_dev == sb.st_dev) { sprintfl (temp, sizeof temp, "%s%s", dirp->d_name, wdir); strcpy (wdir + 1, temp); closedir (fd); return (CHDIR (wdir)); } } } else { closedir (fd); return (CHDIR (wdir)); } } /* scan parent directory for file with inode of current directory */ found = 0; while (readit ()) { if (skipit ()) continue; sprintfl (temp, sizeof temp, "../%s", dirp->d_name); if (stat (temp, &sb) >= 0 && sb.st_ino == sbc.st_ino && sb.st_dev == sbc.st_dev) { closedir (fd); found++; CHDIR (".."); sprintfl (temp, sizeof temp, "%s%s", dirp->d_name, wdir); strcpy (wdir + 1, temp); break; } } if (!found) goto out; } out: closedir (fd); return (-1); } X/* A chdir() that fiddles the global record */ chdir (dirname) register char *dirname; { register ret; register char *p; char path1[MaxPathNameLen], path2[MaxPathNameLen]; for (p = path1; *p++ = *dirname++; ) ; *(p-1) = '/'; /* append a '/' so that "cd ~" works */ *p = 0; ret = abspath (path1, path2); if (ret == 0 && (ret = CHDIR (path2)) == 0) strcpy (curwd, path2); return ret; } X/* return a pointer to a copy of a file name that has been converted to absolute form. This routine cannot return failure. */ char *SaveAbs (fn) char *fn; { static char buf[MaxPathNameLen]; if (fn==0) return 0; abspath (fn, buf); return buf; } WorkingDirectory () { MLvalue -> exp_type = IsString; MLvalue -> exp_v.v_string = curwd; MLvalue -> exp_release = 0; MLvalue -> exp_int = strlen (MLvalue -> exp_v.v_string); return 0; } InitAbs () { int i; if (getwd (curwd) < 0) { char *i = getenv ("HOME"); if (i == 0 || CHDIR (i)) CHDIR (i = "/"); strcpy (curwd, i); fprintf (stderr, "[NOTE: Changed to directory %s]\r\n", curwd); fflush (stderr); } if ((i = strlen (curwd)) > 1 && curwd[i-1] == '/') curwd[i-1] = 0; #ifdef DumpableEmacs if (!Once) #endif defproc (WorkingDirectory, "working-directory"); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 664 abspath.c /bin/echo -n ' '; /bin/ls -ld abspath.c fi /bin/echo 'Extracting dsp.c' sed 's/^X//' <<'//go.sysin dd *' >dsp.c X/* Display routines */ X/* Copyright (c) 1981,1980 James Gosling */ #include "config.h" #include "keyboard.h" #include "buffer.h" #include "window.h" #include "display.h" #include <sys/ioctl.h> #include <sgtty.h> #include <stdio.h> extern QuitDoRstDsp; /* Set while in raw */ struct sgttyb old; /* The initial tty mode bits */ InitDsp () { struct sgttyb sg; extern char _sobuf[]; gtty (0, &old); sg = old; QuitDoRstDsp++; sg.sg_flags = (sg.sg_flags & ~(ECHO | CRMOD | XTABS)) | RAW; stty (0, &sg); ScreenGarbaged = 1; setbuf (stdout, _sobuf); term_init (); if (tt.t_window) (*tt.t_window) (0); } RstDsp () { if (tt.t_window) (*tt.t_window) (0); (*tt.t_topos) (ScreenLength, 1); (*tt.t_wipeline) (0, ScreenWidth); (*tt.t_cleanup) (); fflush (stdout); stty (0, &old); QuitDoRstDsp = 0; } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 664 dsp.c /bin/echo -n ' '; /bin/ls -ld dsp.c fi /bin/echo 'Extracting filecomp.c' sed 's/^X//' <<'//go.sysin dd *' >filecomp.c X/* File completion routines */ X/* Original code by Chris Torek <chris@umcp-cs> * Modifications for 4.1[ac]BSD by Marshall Rose <mrose@uci> If you want the 4.1[ac]BSD version, #define LIBNDIR. Note that this introduces the new global variable fast-file-searches. */ #include "config.h" #include "buffer.h" #include "window.h" #include "keyboard.h" #include "mlisp.h" #include <sys/types.h> #ifndef LIBNDIR #include <sys/dir.h> #else #include <ndir.h> #endif #include <sys/stat.h> #define DONE 0 #define CONTIN 1 #define GARBAGE 2 #define CANTHELP 3 #define EMPTY 4 #define MANY 5 #define min(a,b) ((a)<(b)?(a):(b)) #ifdef LIBNDIR #define max(a,b) ((a)>(b)?(a):(b)) #define WID 18 #define NCOLS 78 #endif #ifndef LIBNDIR static char path[MaxPathNameLen], file[DIRSIZ]; #else static char path[MaxPathNameLen], file[MAXNAMLEN]; #endif static struct stat st; static DirUsed; /* Number of entries in DirEnts */ static unsigned DirSize; /* Number of bytes allocated to DirEnts */ static DirSorted; /* True iff table has been sorted */ static DirMatches; /* Number of matches */ static MatchSize; /* Number of characters matched */ static time_t DirMtime; /* st_mtime of current in-core dir */ static dev_t DirDevice; /* st_dev of current dir */ static ino_t DirInode; /* st_ino of current dir */ static struct direct *DirEnts; /* The first entry */ static struct direct *FirstMatch;/* The first entry matching desired name */ extern int AutoHelp; extern PopUpWindows; extern RemoveHelpWindow; static struct window *killee; #ifdef LIBNDIR extern FastFileSearches; #endif char *malloc (), *realloc (), *index (), *rindex (); X/* Get the name of some existing file */ char * GetFileName (prompt) char *prompt; { static char result[MaxPathNameLen]; register char *name = ""; register f, len; int oldpop = PopUpWindows; struct buffer *old = bf_cur; if (RemoveHelpWindow) PopUpWindows = 0; killee = 0; *result = 0; for (;;) { name = BrGetstr (1, result, &prompt); if (name == 0) { if (killee) WindowOn (old); PopUpWindows = oldpop; return name; } f = name[strlen(name)-1] == '/'; abspath (name, result); name = result; len = strlen (name); if (f) { name[len++] = '/'; name[len] = 0; } if (LastKeyStruck == '?') { /* Show possible completions */ name[len - 1] = 0; SplitPath (name, path, file); if (ReadDir (path) == 0) { MarkDir (file); showchoices ("Choose one of these:\n"); } else Ding (); } else switch (PerformCompletion (name)) { case DONE: /* we got a file */ if (killee) WindowOn (old); PopUpWindows = oldpop; return name; case GARBAGE: /* foo on you */ if (AutoHelp) { ReadDir (path); MarkDir (file); showchoices ("Garbage!! Use one of the following:\n"); } else Ding (); continue; case CONTIN: /* completed something */ continue; case CANTHELP: /* directory unreadable */ Ding (); continue; case EMPTY: /* empty directory */ if (AutoHelp) showchoices ("Empty directory!\n"); Ding (); continue; case MANY: /* matches a bunch of names */ if (AutoHelp) { MarkDir (file); showchoices ("Ambiguous, use one of the following:\n"); } else Ding (); continue; } } } static PerformCompletion (name) register char *name; { register diving = 0; register pathlen; /* Quick index into path or name */ top: if (stat (name, &st) == 0 && (st.st_mode & S_IFDIR) == 0) return DONE; /* Got a filename */ SplitPath (name, path, file); if (stat (path, &st) || (st.st_mode & S_IFDIR) == 0) { do { register char *p = path; while (*p++) ; p[-2] = 0; /* Remove trailing slash */ SplitPath (path, path, file); } while (stat (path, &st) || (st.st_mode & S_IFDIR) == 0); strcpy (name, path); return GARBAGE; /* No such directory */ } if (access (path, 4) < 0) /* Cant read directory */ return diving ? CONTIN : CANTHELP; if (ReadDir (path)) { /* "Can't happen" */ Ding (); return CONTIN; } if (DirUsed == 0) return EMPTY; /* Empty directory */ pathlen = strlen (path); MarkDir (file); if (DirMatches == 0) { /* No such file */ do file[--MatchSize] = 0; while (MarkDir (file) == 0); strcpy (name+pathlen, file); return GARBAGE; } if (DirMatches == 1) { /* Exact match on one name */ diving++; #ifndef LIBNDIR strncpy (name+pathlen, FirstMatch -> d_name, DIRSIZ); *(name+pathlen+DIRSIZ) = 0; #else /* already null terminated */ strcpy (name+pathlen, FirstMatch -> d_name); #endif if (stat (name, &st) == 0 && st.st_mode & S_IFDIR) strcat (name, "/"); goto top; } X/* * Make the name as long as possible such that it still matches the * same entries. If we cannot add anything then the name was ambiguous. */ { register oDirMatches = DirMatches, extended = 0; while (file[MatchSize] = FirstMatch -> d_name[MatchSize]) { MatchSize++; #ifndef LIBNDIR if (MatchSize < DIRSIZ) file[MatchSize] = 0; #else if (MatchSize < MAXNAMLEN) file[MatchSize] = 0; #endif if (MarkDir (file) < oDirMatches) { file[--MatchSize] = 0; break; } extended++; } #ifndef LIBNDIR strncpy (name+pathlen, file, DIRSIZ);/* (current path is correct) */ #else strcpy (name+pathlen, file); #endif return extended || diving ? CONTIN : MANY; } } X/* Make the table by reading the directory. Return 0 if everything goes well. Also, remember current table and only remake if new. */ #ifndef LIBNDIR static ReadDir (dir) char *dir; { static lastrv; register struct direct *d, *p; register char *s; register f, l; f = open (dir, 0); if (f < 0) return lastrv = -1; fstat (f, &st); if (st.st_mtime == DirMtime && st.st_dev == DirDevice && st.st_ino == DirInode) { close (f); return lastrv; } DirMtime = st.st_mtime; DirDevice = st.st_dev; DirInode = st.st_ino; if (st.st_size >= DirSize) { if (DirEnts) free ((char *) DirEnts); DirSize = st.st_size + 30; DirEnts = (struct direct *) malloc (DirSize); } DirSorted = 0; lastrv = read (f, (char *) DirEnts, st.st_size + 1) != st.st_size; close (f); if (lastrv) return lastrv; p = DirEnts; d = DirEnts; for (f = st.st_size / sizeof *p; --f >= 0; p++) { if (p -> d_ino == 0) continue; s = p -> d_name; if (s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0))) continue; l = DIRSIZ; while (*s++ && --l >= 0); --s; switch (*--s) { case 'o': if (*--s == '.') continue; case 'P': if (*--s == 'K' && *--s == 'C' && *--s == '.') continue; case 'k': if (*--s == 'a' && *--s == 'b' && *--s == '.') continue; } *d++ = *p; } DirUsed = d - DirEnts; return 0; } #else static ReadDir (dir) char *dir; { static int lastrv; register int f, g, l; register char *e, *s; short byte; /* 16 bits (I hope) */ static char filnam[MaxPathNameLen]; register struct direct *d, *p; register DIR * dd; if ((dd = opendir (dir)) == NULL) return (lastrv = -1); fstat (dd -> dd_fd, &st); if (st.st_mtime == DirMtime && st.st_dev == DirDevice && st.st_ino == DirInode) { closedir (dd); return lastrv; } DirMtime = st.st_mtime; DirDevice = st.st_dev; DirInode = st.st_ino; for (f = 0; p = readdir (dd); f++) continue; again: ; f += 10; /* fudge factor... */ l = f * sizeof (struct direct); if (l >= DirSize) { if (DirEnts) free ((char *) DirEnts); DirSize = l * 2; /* why not? */ DirEnts = (struct direct *) malloc (DirSize); } DirSorted = 0; rewinddir (dd); for (d = DirEnts, g = 0; p = readdir (dd);) { if ((p -> d_namlen == 1 && !strcmp (p -> d_name, ".")) || (p -> d_namlen == 2 && !strcmp (p -> d_name, ".."))) continue; s = p -> d_name + p -> d_namlen; if (p -> d_namlen > 2 && !strcmp (s - 2, ".o")) continue; #ifdef PrependExtension e = CheckpointExtension; if (!strncmp (p -> d_name, e, strlen (e))) continue; e = BackupExtension; if (!strncmp (p -> d_name, e, strlen (e))) continue; #else if ((p -> d_namlen > (l = strlen (e = CheckpointExtension)) && !strcmp (s - l, e)) || ((p -> d_namlen > (l = strlen (e = BackupExtension)) && !strcmp (s - l, e)))) continue; #endif if (FastFileSearches) goto no_tricks; sprintfl (filnam, sizeof filnam, "%s/%s", dir, p -> d_name); if (stat (filnam, &st)) continue; if ((st.st_mode & S_IFDIR) != 0) l = -1; else if ((l = open (filnam, 0)) < 0) continue; if (l >= 0) { if (read (l, (char *) (&byte), sizeof byte) != sizeof byte) byte = 0; close (l); switch (byte) { /* is it a text file? */ case 0405: case 0407: /* OMAGIC */ case 0410: /* NMAGIC */ case 0411: case 0413: /* ZMAGIC */ case 0177545: continue; default: /* perhaps it is... */ break; } } no_tricks: ; if (f <= g++) { /* directory grew!!! */ for (f = g; p = readdir (dd); f++) continue; goto again; } d -> d_ino = p -> d_ino; d -> d_reclen = sizeof (struct direct); d -> d_namlen = p -> d_namlen; strcpy (d -> d_name, p -> d_name); d++; } closedir (dd); DirUsed = d - DirEnts; return (lastrv = 0); } #endif X/* Mark all the table entries that match 'string' */ static MarkDir (string) char *string; { register struct direct *p; #ifndef LIBNDIR register len = DIRSIZ; register char *s = string; #endif #ifndef LIBNDIR while (*s++ && --len >= 0) ; MatchSize = s - string - 1; #else MatchSize = min (strlen (string), MAXNAMLEN); #endif DirMatches = 0; for (p = &DirEnts[DirUsed - 1]; p>=DirEnts; p--) if (MatchSize == 0 || p->d_name[0]==string[0] && strcmpn(p->d_name,string,MatchSize)==0) { DirMatches++; p -> d_ino = 1; FirstMatch = p; } else p->d_ino = 0; return DirMatches; } X/* Compare two table entries (for qsort) */ static DirCompare (p1, p2) register struct direct *p1, *p2; { #ifndef LIBNDIR return strcmpn (p1 -> d_name, p2 -> d_name, DIRSIZ); #else return strncmp (p1 -> d_name, p2 -> d_name, max (p1 -> d_namlen, p2 -> d_namlen)); #endif } X/* Write all the matched entries into "Help" buffer. Sort first if needed. */ static showchoices (msg) char *msg; { register struct direct *p; register i; #ifndef LIBNDIR register side = 0; char buf[22]; #else register pos, j; char buf[MAXNAMLEN + WID]; #endif if (DirUsed > 1 && !DirSorted) qsort (DirEnts, DirUsed, sizeof (struct direct), DirCompare); DirSorted++; SetBfn ("Help"); WindowOn (bf_cur); EraseBf (bf_cur); InsStr (msg); killee = wn_cur; #ifndef LIBNDIR for (p = DirEnts, i=DirUsed; --i>=0; p++) { if (p -> d_ino) { sprintfl (buf, sizeof buf, (side==3 ? ((side=0), "%.*s\n") : (side++, "%-18.*s")), DIRSIZ, p -> d_name); InsStr (buf); } } #else for (p = DirEnts, i = DirUsed, pos = 0; --i >= 0; p++) if (p -> d_ino) { if (pos > 0) { if (pos + (j = WID - (pos % WID)) + p -> d_namlen > NCOLS) pos = j = 0, InsStr ("\n"); } else j = 0; sprintfl (buf, sizeof buf, "%*s%.*s", j, "", p -> d_namlen, p -> d_name); InsStr (buf); pos += p -> d_namlen +j; } #endif BeginningOfFile (); bf_cur -> b_mode.md_NeedsCheckpointing = 0; bf_modified = 0; } X/* Split a pathname into directory and filename components */ static SplitPath (path, dir, file) register char *path; char *dir, *file; { register char *p, *d = 0; for (p = path; *p; ) if (*p++ == '/') d = p; if (d) { strncpy (dir, path, d - path); dir[d-path] = 0; #ifndef LIBNDIR strncpy (file, d, DIRSIZ); #else strncpy (file, d, MAXNAMLEN); #endif } else { dir[0] = 0; #ifndef LIBNDIR strncpy (file, path, DIRSIZ); #else strncpy (file, path, MAXNAMLEN); #endif } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 664 filecomp.c /bin/echo -n ' '; /bin/ls -ld filecomp.c fi /bin/echo 'Extracting fileio.c' sed 's/^X//' <<'//go.sysin dd *' >fileio.c X/* File IO for Emacs */ X/* Copyright (c) 1981,1980 James Gosling */ #include "keyboard.h" #include "mlisp.h" #include "buffer.h" #include "window.h" #include "config.h" #include "macros.h" #include <sys/types.h> #include <stat.h> char *GetFileName(); struct AutoMode { /* information for automatic mode recognition */ char *a_pattern; /* the pattern that the name must match */ int a_len; /* the length of the pattern string */ struct BoundName *a_what; /* what to do if we find it */ struct AutoMode *a_next; /* the next thing to try */ }; static struct AutoMode *AutoList;/* the list of filename-pattern pairs that have been specified by auto-execute */ static FilesShouldEndWithNewline;/* If true, then if the user trys to write out a buffer that doesn't end in a newline then they'll get asked about it. I almost called this variable "kazar-mode" but good taste prevaled */ static BackupBeforeWriting; /* if true, then file being written will be backed up just before the first time that it is written. */ static BackupByCopying; /* if true, then when a backup is made, it will be made by copying the file, rather than by fancy footwork with links (this is for folks who like to preserve links to files) */ static BackupByCopyingWhenLinked;/* if true, then when a backup for a file with multiple links is made, it will be made by copying */ static UnlinkCheckpointFiles; /* if true, then when a file is written out the corresponding checkpoint file is deleted -- some people don't like to have .CKP files cluttering up their directories, but some people like the added security. */ static AskAboutBufferNames; /* If true (the default) Emacs will ask instead of synthesizing a unique name in the case where visit-file encounters a conflict in generated buffer names. */ #ifdef LIBNDIR int FastFileSearches; /* If true (the default) then Emacs will not perform any fancy tests to determine what files the user is interested in during filename command completion */ #endif LIBNDIR static Umask; /* the current umask() */ DoAuto (filename) /* Perform the auto-execute action (if any) for the specified filename */ char *filename; { register struct AutoMode *p; register len = strlen (filename); register saverr = err; err = 0; for (p = AutoList; p; p = p -> a_next) if ( len+1 >= p->a_len && (*p -> a_pattern == '*' ? strcmpn (p -> a_pattern + 1, filename + len - p -> a_len + 1, p -> a_len - 1) : strcmpn (p -> a_pattern, filename, p -> a_len - 1) ) == 0) { ExecuteBound (p->a_what); break; } err |= saverr; } AutoExecute () { int what = getword (MacNames, ": auto-execute "); char *pattern; register struct AutoMode *p; if (what < 0) return 0; pattern = getstr (": auto-execute %s when name matches ", MacNames[what]); if (pattern == 0) return 0; if ((*pattern == '*') == (pattern[strlen (pattern) - 1] == '*')) error ("Improper pattern \"%s\"; should either be of the form \"*X\" or \"X*\"", pattern); else { p = (struct AutoMode *) malloc (sizeof *p); p -> a_pattern = savestr (pattern); p -> a_len = strlen (p -> a_pattern); p -> a_what = MacBodies[what]; p -> a_next = AutoList; AutoList = p; } return 0; } static PerformAutoMode () { if (CurExec && CurExec -> p_nargs) { StringArg (1); if (err) return 0; DoAuto (MLvalue -> exp_v.v_string); ReleaseExpr (MLvalue); } else DoAuto (bf_cur -> b_name); return 0; } static WriteFileExit () { return ModWrite () ? -1 : 0; } X/* Call with pointer to buffer whose checkpoint file may be deleted. */ DeleteBuffersCheckpointFile (b) register struct buffer *b; { if (UnlinkCheckpointFiles) { if (b -> b_checkpointfn) unlink (b -> b_checkpointfn); b -> b_checkpointed = 0; } } static WriteThis () { register rv = 0; if (!bf_cur -> b_fname) error ("No file name assocated with buffer"); else if (WriteFile (bf_cur -> b_fname, 0)) rv = -1; if (UnlinkCheckpointFiles) { if (!err && bf_cur -> b_checkpointfn) unlink (bf_cur -> b_checkpointfn); bf_cur -> b_checkpointed = 0; } return rv; } static InsertFile () { char *fn = GetFileName("Insert file: "); if (fn) { readfile (SaveAbs (fn), 0, 0); bf_modified++; } return 0; } static WriteModifiedFiles () { ModWrite (); return 0; } static ReadFile () { register char *fn = getstr (": read-file "); if (fn == 0) return 0; readfile (SaveAbs(*fn ? fn : bf_cur->b_fname), 1, 0); DoAuto (fn); return 0; } static UnlinkFile () { register char *fn = (char *) SaveAbs (getstr (": unlink-file ")); MLvalue -> exp_int = fn ? unlink (fn) : -1; MLvalue -> exp_type = IsInteger; return 0; } static FileExists () { register char *fn = (char *) SaveAbs (getstr (": file-exists ")); if (fn==0) return 0; MLvalue -> exp_int = *fn == 0 ? 0 : access (fn, 2)>=0 ? 1 : access(fn, 4)>=0 ? -1 : 0; MLvalue -> exp_type = IsInteger; return 0; } static WriteCurrentFile () { if (!bf_cur -> b_fname) error ("No file name assocated with buffer"); else WriteFile (bf_cur -> b_fname, 0); if (UnlinkCheckpointFiles) { if (!err && bf_cur -> b_checkpointfn) unlink (bf_cur -> b_checkpointfn); bf_cur -> b_checkpointed = 0; } return 0; } static VisitFileCommand () { VisitFile (getstr ("Visit file: "), 1, 1); return 0; } static VisitExistingFileCommand () { VisitFile (GetFileName ("Visit existing file: "), 1, 1); return 0; } static SetFileName () { register char *fn = getstr (": set-file-name to "); if (fn == 0 || *fn == 0) return 0; if (bf_cur -> b_fname) free (bf_cur -> b_fname); bf_cur -> b_fname = savestr ((char *) SaveAbs (fn)); Cant1WinOpt++; bf_cur -> b_kind = FileBuffer; return 0; } static WriteNamedFile () { register char *fn = getstr ("Write file: "); if (fn == 0) return 0; if (*fn == '\0') { if (bf_cur -> b_fname == 0) { error ("I don't like empty file names!"); return 0; } } else { if (bf_cur -> b_fname) free (bf_cur -> b_fname); bf_cur -> b_fname = savestr ((char *) SaveAbs (fn)); } if (bf_cur -> b_checkpointfn) { free (bf_cur -> b_checkpointfn); bf_cur -> b_checkpointfn = 0; bf_cur -> b_checkpointed = 0; } Cant1WinOpt++; bf_cur -> b_kind = FileBuffer; WriteCurrentFile (); return 0; } static AppendToFile () { register char *fn = getstr (": append-to-file "); char fnbuf[MaxPathNameLen]; /* if only C had real strings... Then I wouldn't have to resort to returning strings in static & getting bitten by later overwrites. */ if (fn == 0) return 0; if (*fn=='\0'){ error("I don't like empty file names!"); return 0; } strcpy(fnbuf, SaveAbs (fn)); WriteFile (fnbuf, 1); return 0; } VisitFile (fn, CreateNew, WindowFiddle) char *fn; { char fullname[500]; register struct buffer *b, *oldb = bf_cur; if (fn == 0 || *fn == 0) return 0; strcpy (fullname, SaveAbs (fn)); for (b = buffers; b && (b -> b_fname == 0 || strcmp (fullname, b -> b_fname) != 0); b = b -> b_next); if (b) SetBfp (b); else { char *bufname; register char *p = fullname; bufname = fullname; while (*p) if (*p++ == '/' && *p) bufname = p; if (FindBf (bufname)) { if (interactive && AskAboutBufferNames) { p = getstr ( "Buffer name %s is in use, type a new name or <CR> to clobber: ", bufname); if (p == 0) return 0; if (*p) bufname = p; } else { static char SynthName[100]; register seq = 1; /* I'm making the (perhaps) brash assumption that the following loop is guaranteed to terminate. To those who think that this is an inefficient technique: you have been deluded. */ do sprintf (SynthName, "%s<%d>", bufname, ++seq); while (FindBf (SynthName)); bufname = SynthName; } } SetBfn (bufname); if (!readfile (fullname, 1, CreateNew) && !CreateNew) { SetBfp (oldb); return 0; } else { bf_cur -> b_kind = FileBuffer; if (bf_cur -> b_fname) free (bf_cur -> b_fname); if (bf_cur -> b_checkpointfn) free (bf_cur -> b_checkpointfn); bf_cur -> b_checkpointfn = 0; bf_cur -> b_checkpointed = 0; bf_cur -> b_fname = savestr (fullname); } } if (WindowFiddle) WindowOn (bf_cur); if (b == 0) DoAuto (fn); return 1; } readfile (fn, erase, CreateNew) char *fn; { struct stat st; register int fd; register int n, i; if (fn == 0) return 0; if (*fn == 0) { error ("Aw come on, if you want me to read something I need file name"); return 0; } if (stat (fn, &st) < 0 || (fd = open (fn, 0)) < 0) { error (CreateNew ? "New file: %s" : "Can't find \"%s\"", fn); return 0; } Cant1LineOpt++; RedoModes++; WidenRegion (); if (erase) EraseBf (bf_cur); GapTo (dot); DoneIsDone (); if (GapRoom (st.st_size)) return 0; n = 0; while ((i = read (fd, bf_p1 + bf_s1 + 1 + n, st.st_size - n)) > 0) n += i; close (fd); if (n > 0) { bf_s1 += n; bf_gap -= n; bf_p2 -= n; } if (n == 0) message ("Empty file."); if (i < 0) error ("Error reading file \"%s\"", fn); if (erase) { if (bf_cur -> b_fname) free (bf_cur -> b_fname); if (bf_cur -> b_checkpointfn) { free (bf_cur -> b_checkpointfn); bf_cur -> b_checkpointfn = 0; bf_cur -> b_checkpointed = 0; } bf_cur -> b_fname = savestr (fn); bf_cur -> b_kind = FileBuffer; } return i >= 0; } X/* Given a file name and an extension to be forced, concoct a new file name which is their concatenation, accounting for the restricton on the length of the last component of a file name begin 14 characters. */ char *ConcoctName (fn, extension) char *fn, *extension; { static char name[200]; #ifdef PrependExtension register extlen; #endif register char *p, *s, *tail; #ifdef PrependExtension for (p = extension, extlen = 0; *p++;) extlen++; #endif for (s = fn, p = tail = name; *p = *s++; p++) if (*p == '/') tail = p + 1; #ifdef PrependExtension /* put the extension at the beginning and let system truncate * name */ for( ; p >= tail ; *(p+extlen) = *p, p-- ); /* shift right */ for( p = extension ; *p ; *tail++ = *p++); /* insert extension */ #else if (p - tail > 10) p = tail + 10; for(s=extension; *p++ = *s++; ); #endif return name; } X/* write the current buffer to the named file; returns true iff successful. Appends to the file if AppendIt is >0, does a checkpoint style write if AppendIt is <0. */ WriteFile (fn, AppendIt) register char *fn; { register fd; register nc = bf_s1 + bf_s2; int mode = 0666 & ~Umask; int TempFile = fn[0] == '/' && fn[1] == 't' && fn[2] == 'm' && fn[3] == 'p' && fn[4] == '/'; if(AppendIt<0) mode = 0600 & ~Umask; if(AppendIt>=0 && !access(fn,0) && access(fn,2)) { error("File %s cannot be written",fn); return 0; } if (fn == 0 || *fn == 0) return 0; /* (ACT) Dont write if ReadOnly */ if (AppendIt >= 0 && bf_mode.md_ReadOnly) { error ("File %s is read-only", fn); return 0; } if (AppendIt>0) { fd = open (fn, 1); if (fd < 0) fd = creat (fn, mode); if (fd >= 0) if (lseek (fd, 0, 2) < 0) close (fd), fd = -1; } else { if (AppendIt>=0 && BackupBeforeWriting && !TempFile && !bf_cur -> b_BackedUp) { struct stat st; char *name = ConcoctName (fn, BackupExtension); bf_cur -> b_BackedUp++; if (stat (fn, &st) == 0) mode = st.st_mode; if (BackupByCopying || st.st_nlink>1 && BackupByCopyingWhenLinked) { int ifd, ofd = -1, n; char buf[2048]; if ((ifd = open (fn, 0)) >= 0 && (ofd = creat (name, 0600)) >= 0) while ((n = read (ifd, buf, sizeof buf)) > 0) write (ofd, buf, n); if (ifd >= 0) close (ifd); if (ofd >= 0) close (ofd); } else { unlink (name); link (fn, name); unlink (fn); } } fd = creat (fn, mode); if (fd >=0 && (mode & ~Umask) != mode) chmod (fn,mode); } if (fd < 0) { error ("Can't write %s", fn); return 0; } if (FilesShouldEndWithNewline && nc > 0 && CharAt (nc) != '\n' && interactive && AppendIt>=0 && *getnbstr ( "\"%s\" doesn't end with a newline, should I add one? ", bf_cur->b_name) == 'y') InsertAt (nc + 1, '\n'); if (write (fd, bf_p1 + 1, bf_s1) < 0 || write (fd, bf_p1 + 1 + bf_s1 + bf_gap, bf_s2) < 0) { error ("IO error writing %s", fn); close (fd); return 0; } if(!err) { bf_modified = 0; bf_cur -> b_checkpointed = 0; if (!TempFile && AppendIt >= 0)/* (ACT) Don't message about CKP's */ message ("Wrote %s", fn); } close (fd); Cant1LineOpt++; /* Force update of the mode line */ return 1; } X/* fopenp opens the file fn with the given IO mode using the given search path. The actual file name is returned in fnb. The search path is interpreted in the same way as the PATH environment variable is interpreted by exec?p(). This routine normally comes from the CMU C library, but since Emacs is being distributed I have to roll-my-own. */ FILE * fopenp (path, fn, fnb, mode) register char *path; char *fn, *fnb, *mode; { register FILE *fd; char AbsForm[MaxPathNameLen]; register char *dst, *src; if (path == 0) path = ""; if (*fn=='~') { abspath (fn, AbsForm); fn = AbsForm; } if (*fn=='/'){ if(( fd = fopen(fn, mode)) != NULL) { strcpy(fnb, fn); return fd; } return NULL; } do { dst = fnb; while (*path && *path != ':') *dst++ = *path++; if (dst != fnb) *dst++ = '/'; for (src = fn; *dst++ = *src++;); if ((fd = fopen (fnb, mode)) != NULL) return fd; } while (*path++); return NULL; } X/* returns true if modified buffers exist */ ModExist () { register struct buffer *b; register modcount = 0; SetBfp (bf_cur); for (b = buffers; b; b = b -> b_next) if (b -> b_modified && b -> b_kind == FileBuffer) modcount++; return modcount; } static ModificationsExist () { MLvalue -> exp_int = ModExist(); MLvalue -> exp_type = IsInteger; return 0; } X/* write all modified buffers; return true iff OK */ ModWrite () { register struct buffer *b; struct buffer *old = bf_cur; register WriteErrors = 0; for (b = buffers; b; b = b -> b_next) { SetBfp (b); if (bf_cur->b_kind==FileBuffer && bf_modified && WriteThis () == 0 && 'y' != *getnbstr ("Can't write buffer %s, can I ignore it? ", b -> b_name)){ WriteErrors++; } } SetBfp (old); return !err && !WriteErrors; } static DisplayCheckpointMessage;/* if off, prevents message */ CheckpointEverything () { register struct buffer *b; struct buffer *old = bf_cur; register WriteErrors = 0, modcnt; int Checkpointed = 0; for (b = buffers; b; b = b -> b_next) if (b -> b_mode.md_NeedsCheckpointing && b -> b_checkpointed < (modcnt = b == bf_cur ? bf_modified : b -> b_modified)) { SetBfp (b); if (b -> b_checkpointfn == 0) b -> b_checkpointfn = savestr (ConcoctName (b -> b_fname ? b -> b_fname : b -> b_name, CheckpointExtension)); WriteErrors |= WriteFile (b -> b_checkpointfn, -1) == 0; Checkpointed++; b ->b_checkpointed = bf_modified = modcnt; } SetBfp (old); if(!WriteErrors && Checkpointed && DisplayCheckpointMessage) message("Checkpointed..."); if (WriteErrors) err = 0; /* to avoid having errors during checkpoints blow away functions in the middle of execution. */ return 0; } InitFIO () { umask(Umask = umask(077)); #ifdef DumpableEmacs if (!Once) #endif { DefIntVar ("backup-before-writing", &BackupBeforeWriting); DefIntVar ("backup-by-copying", &BackupByCopying); DefIntVar ("backup-by-copying-when-linked", &BackupByCopyingWhenLinked); DefIntVar ("unlink-checkpoint-files", &UnlinkCheckpointFiles); DefIntVar ("files-should-end-with-newline", &FilesShouldEndWithNewline); FilesShouldEndWithNewline = 1; DefIntVar ("ask-about-buffer-names", &AskAboutBufferNames); AskAboutBufferNames = 1; DefIntVar ("display-checkpoint-message", &DisplayCheckpointMessage); DisplayCheckpointMessage = 1; #ifdef LIBNDIR DefIntVar ("fast-file-searches", &FastFileSearches); FastFileSearches = 1; #endif LIBNDIR setkey (CtlXmap, (Ctl ('F')), WriteFileExit, "write-file-exit"); setkey (CtlXmap, (Ctl ('R')), ReadFile, "read-file"); setkey (CtlXmap, (Ctl ('I')), InsertFile, "insert-file"); setkey (CtlXmap, (Ctl ('V')), VisitFileCommand, "visit-file"); setkey (CtlXmap, (Ctl ('Q')), VisitExistingFileCommand, "visit-existing-file"); setkey (CtlXmap, (Ctl ('W')), WriteNamedFile, "write-named-file"); setkey (CtlXmap, (Ctl ('M')), WriteModifiedFiles, "write-modified-files"); setkey (CtlXmap, (Ctl ('S')), WriteCurrentFile, "write-current-file"); defproc (AppendToFile, "append-to-file"); defproc (UnlinkFile, "unlink-file"); defproc (FileExists, "file-exists"); defproc (ModificationsExist, "modifications-exist"); defproc (PerformAutoMode, "perform-automode-action"); defproc (CheckpointEverything, "checkpoint"); defproc (AutoExecute, "auto-execute"); defproc (SetFileName, "set-file-name"); } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 664 fileio.c /bin/echo -n ' '; /bin/ls -ld fileio.c fi /bin/echo 'Extracting window.c' sed 's/^X//' <<'//go.sysin dd *' >window.c X/* Window manipulation primitives */ X/* Copyright (c) 1981,1980 James Gosling */ X/* DJH added substitute Ding() for putchar(07); */ #include "config.h" #include "buffer.h" #include "keyboard.h" #include "window.h" #include "display.h" #include <stdio.h> #include <ctype.h> #include "mlisp.h" static struct marker *OneLStart; /* Starting character position of the line containing dot -- used when doing the one line redisplay optimization. */ static OneLValid; /* True iff OneLStart points at something valid */ static OneLLine; /* The display line which contains dot */ static MBLine; /* The line on which the minibuf starts */ static LineWrapped; /* True iff the line just dumped has wrapped, this really slows down the the redisplay if it's the current line. */ static QuickRD; /* True iff quick redisplay alg. is to be used */ static UseTime; /* A counter used to set the time of last use of a window: for selecting the LRU window */ static GSaveMiniBuf; /* True iff the cursor is in the minibuf */ char GlobalModeString[30]; /* The global-mode-string variable */ int PopUpWindows; /* True iff new windows should be automatically selected by commands that play with other buffers (eg. ^X^V and ^X^B) */ /* changed from static to int 13-Jan-83 so that minibuf.c could use and reset it. - BNI */ static WrapLines; /* True iff long lines should wrap around */ static ScrollStep; /* The number of lines to try scrolling a window by when dot leaves the window; if it is <=0 then dot is centered in the window */ static SplitHeightThreshhold; /* If a window is larger than this it will be considered splitabble when a window is to be popped up (rather than picking the LRU window) */ static MouseX; /* The X screen coordinate of the mouse */ static MouseY; /* The Y screen coordinate of the mouse */ static struct window *MouseWin; /* The window corresponding to (MouseX,MouseY) */ static MouseDot; /* The character position corresponding to (MouseX,MouseY) */ int QuitDoRstDsp; /* When set, quit should do a RstDsp */ int QuitDoQuitMpx; /* When set, quit should do a QuitMpx */ struct window *SplitWin (); X/* Move dot to the buffer and character corresponding to some absolute X and Y coordinate. */ MoveDotToXY () { MouseX = getnum ("X coordinate: "); if (!err) MouseY = getnum ("Y coordinate: "); if (!err) { MouseWin = 0; Cant1LineOpt++; DoDsp (1); if (MouseWin == 0) error ("The mouse isn't pointing at a part of a buffer"); else { SetWin (MouseWin); SetDot (MouseDot); } } return 0; } X/* initialize the window system */ InitWin () { register struct window *w; #ifdef DumpableEmacs if (Once) { /* Restarting, clean up old junk */ for (w = windows; w; w = w -> w_next) { DestMark (w -> w_dot); DestMark (w -> w_start); free (w); } } else #endif { PopUpWindows = 1; SplitHeightThreshhold = 20; OneLStart = NewMark (); OneLValid = 0; DefStrVar ("global-mode-string", GlobalModeString); DefIntVar ("scroll-step", &ScrollStep); DefIntVar ("quick-redisplay", &QuickRD); DefIntVar ("wrap-long-lines", &WrapLines); DefIntVar ("pop-up-windows", &PopUpWindows); DefIntVar ("split-height-threshhold", &SplitHeightThreshhold); defproc (MoveDotToXY, "move-dot-to-x-y"); } w = (struct window *) malloc (sizeof (struct window)); windows = w; SetDot (1); w -> w_height = ScreenLength; w -> w_prev = 0; w -> w_dot = NewMark (); SetMark (w -> w_dot, bf_cur, 1); w -> w_start = NewMark (); SetMark (w -> w_start, bf_cur, 1); w -> w_force = 0; w -> w_next = 0; w -> w_buf = bf_cur; wn_cur = w; SetWin (SplitWin (w)); TieWin (wn_cur, minibuf); ChangeWindowSize (1 - wn_cur -> w_height); SetWin (w); } X/* set the current window */ SetWin (w) struct window *w; { if (w == 0) return; w -> w_lastuse = UseTime++; SetBfp (w -> w_buf); wn_cur = w; bf_cur = 0; Cant1WinOpt++; SetBfp (w -> w_buf); } struct window *SplitWin (w) register struct window *w; { register struct window *n; register struct buffer *old = bf_cur; if (w -> w_height<=4) { error ("You can't have windows smaller than two lines high."); return w; } n = (struct window *) malloc (sizeof (struct window)); n -> w_prev = w; n -> w_force = 0; n -> w_next = w -> w_next; w -> w_next = n; if (n -> w_next) n -> w_next -> w_prev = n; n -> w_height = w -> w_height / 2; w -> w_height -= n -> w_height; n -> w_dot = NewMark (); n -> w_lastuse = 0; n -> w_buf = w -> w_buf; n -> w_start = NewMark (); SetMark (n -> w_dot, n->w_buf, ToMark (w -> w_dot)); SetMark (n -> w_start, n->w_buf, ToMark (w -> w_start)); SetBfp (old); Cant1WinOpt++; return n; } X/* split the largest window, and return a pointer to it */ struct window *SplitLargestWindow () { register struct window *w, *bestw; register besth = -1; for (w = windows; w -> w_next; w = w->w_next) if (w->w_height>besth) besth = w->w_height, bestw = w; return SplitWin (bestw); } X/* Delete the indicated window */ DelWin (w) register struct window *w; { if (w -> w_next == 0) /* Can't delete the last window -- it's the minibuf */ return 0; if (w -> w_prev) { w -> w_prev -> w_height += w -> w_height; w -> w_prev -> w_next = w -> w_next; } else { if (w -> w_next -> w_next == 0) return 0; windows = w -> w_next; windows -> w_height += w -> w_height; } if (w -> w_next) w -> w_next -> w_prev = w -> w_prev; if (w == wn_cur) SetWin (w -> w_prev ? w -> w_prev : windows); DestMark (w->w_dot); DestMark (w->w_start); Cant1WinOpt++; /* I'm not sure if this'll work. Delete it if it screws up. */ /* ACT 25 Jun 1983 */ free (w); return 0; } X/* tie a window to a buffer */ TieWin (w, b) register struct window *w; register struct buffer *b; { register newdot; if (b == 0 || w == 0 || w -> w_buf == b || b -> b_kind == DeletedBuffer) return; w -> w_buf = b; w -> w_force = 0; w -> w_lastuse = UseTime++; newdot = b == bf_cur ? dot : b -> b_EphemeralDot; SetMark (w -> w_dot, b, newdot); SetMark (w -> w_start, b, 1); } X/* Change the height of the pointed to window by delta; returns true iff the change succeeds. Chains forward if dir>0, backward if dir<0 in attempting to find a suitable window. */ ChgWHeight (w, delta, dir) register struct window *w; { while (w) if (w -> w_height + delta >= (w -> w_next ? 2 : 1) && (dir == 0 || w -> w_next)) { Cant1WinOpt++; w -> w_height += delta; return 1; } else w = dir == 0 ? 0 : dir < 0 ? w -> w_prev : w -> w_next; return 0; } X/* find the least recently used window; split if only one window */ struct window *LRUwin () { register struct window *w, *bestw = 0; register youngest = 07777777777; register LargestHeight = 0; for (w = windows; w -> w_next; w = w -> w_next) { if ((w -> w_buf == bf_cur ? bf_s1 + bf_s2 : w -> w_buf -> b_size1 + w -> w_buf -> b_size2) == 0) return w; if (w -> w_lastuse < youngest && w != wn_cur) { bestw = w; youngest = w -> w_lastuse; } if (w -> w_height > LargestHeight) LargestHeight = w -> w_height; } if (bestw == 0 || LargestHeight >= SplitHeightThreshhold) bestw = SplitLargestWindow (); return bestw; } X/* make sure that the current window is on the given buffer, either by picking the window that already contains it, the LRU window, or some brand new window */ WindowOn (bf) struct buffer *bf; { register struct window *w; if ((w = wn_cur) -> w_buf != bf) for (w = windows; w; w = w -> w_next) if (w -> w_buf == bf) break; if (!w) w = (PopUpWindows || wn_cur->w_next == NULL) ? LRUwin () : wn_cur; TieWin (w, bf); SetWin (w); } X/* full screen update -- called when absolutely nothing is known or many things have been fiddled with */ FullUpd () { register struct buffer *keep_bf = bf_cur, *hit_bf = wn_cur -> w_buf; register struct window *w = windows; register sline = 1; register hits = 0; register slow = 0; while (w) { SetBfp (w -> w_buf); if (bf_cur == hit_bf) hits++; slow |= w -> w_force; if ( /* w != wn_cur */ 0) DumpWin (w, sline, 1); else { register ldot; register dumpstate = 0; if (w != wn_cur) ldot = dot, SetDot (ToMark (w -> w_dot)); while (dumpstate >= 0 && DumpWin (w, sline, dumpstate == 0)) { slow++; if (w -> w_force) { SetDot (dumpstate ? ToMark (w -> w_start) : ScanBf ('\n', ToMark (w -> w_start), w -> w_height / 2)); if (w != wn_cur) SetMark (w -> w_dot, w -> w_buf, dot); if (dumpstate++) w -> w_force = 0; } else { register old, next; switch (dumpstate) { case 0: dumpstate++; if (ScrollStep > 0) { old = ToMark (w -> w_start); next = ScanBf ('\n', old, old>dot ? -ScrollStep-1 : ScrollStep); if (dot >= next) break; } case 1: next = ScanBf ('\n', dot, -(w -> w_height / 2)); dumpstate++; break; case 2: next = ScanBf ('\n', (old = ToMark (w -> w_start)), 1); if (old < next && next <= dot) break; default: dumpstate++; next = ToMark (w -> w_start) + 50; if (dumpstate > 10) dumpstate = -1; case -1: break; } if (next <= dot) SetMark (w -> w_start, w -> w_buf, next); else dumpstate = -1; } } if (w != wn_cur) SetDot (ldot); w -> w_force = 0; } sline += w -> w_height; if (RedoModes && w -> w_next) DumpMode (w, sline - 1); w = w -> w_next; } CantEverOpt = hits > 1 && !QuickRD; SetBfp (keep_bf); return slow; } X/* Dump the mode line for window w on line n -- assumes the current buffer is the one associated with window w */ DumpMode (w, l) register struct window *w; { char buf[300], tbuf[20]; register char *p = buf; register char *s = bf_mode.md_ModeFormat; register char *str; register char c; int width; X/* ACT 17-Oct-1982 Added '-' format */ int negative = 0; #define ModeC(c) if (p>buf+(sizeof buf)-2) goto out; else *p++ = c; while (c = *s++) if (c == '%') { str = 0; width = 0; if (*s == '-') { ++negative; ++s; } while (isdigit (c = *s++)) width = width * 10 + (c - '0'); switch (c) { case 0: goto out; default: ModeC (c); break; case 'b': str = bf_cur -> b_name; break; case 'f': if ((str = bf_cur -> b_fname) == 0) str = "[None]"; break; case 'F': /* ACT 17-Oct-1982 */ if ((str = bf_cur -> b_fname) == 0) str = "[None]"; else { register char *str1 = str; while (*str1) if (*str1++ == '/' && *str1) str = str1; } break; case 'm': str = bf_mode.md_ModeString; break; case 'a': /* ACT 19-Aug-1983 */ str = bf_mode.md_AbbrevOn ? "abbrev" : ""; break; case 'M': str = GlobalModeString; break; case '*': str = bf_modified ? "*" : ""; break; case 'p': { int tl = bf_s1 + bf_s2, d; d = w == wn_cur ? dot : ToMark (w -> w_dot); if (d <= 1) str = "Top"; else if (d > tl) str = "Bottom"; else { sprintf (tbuf, "%2d%%", (d - 1) * 100 / tl); str = tbuf; } break; } case 'D': /* ACT 25 Jun 1983 */ sprintf (tbuf, "%d", RecurseDepth - MinibufDepth); str = tbuf; break; case '[': str = RecurseDepth-MinibufDepth > 10 ? "*[" : ("[[[[[[[[[[" + 10) - (RecurseDepth-MinibufDepth); break; case ']': str = RecurseDepth-MinibufDepth > 10 ? "*]" : ("]]]]]]]]]]" + 10) - (RecurseDepth-MinibufDepth); break; } if (str) { if (negative && width) { if ((negative = strlen (str)) > width) str += negative - width; else { while (width > negative) { width--; ModeC (' '); } } } while (*str) { ModeC (*str++); if (--width == 0) break; } while (--width >= 0) ModeC (' '); } } else ModeC (c); out: *p++ = 0; DumpStr (buf, 300, l, 1); } X/* dump the indicated string (with maximum length n) to line l */ DumpStr (s, n, l, highlight) register char *s; { register col = 1; register setcurs = s == MiniBuf && InMiniBuf; setpos (l, col); if (highlight) HighLine (); while (--n >= 0) { register char c = *s++; if (c == 0) break; if (c == 011 && bf_mode.md_TabSize >= 1) { col = ((col - 1) / bf_mode.md_TabSize + 1) * bf_mode.md_TabSize + 1; if (col < ScreenWidth) setpos (l, col); } else if (c < 040 || c >= 0177) if (CtlArrow && (c & 0200) == 0) { col += 2; if (col <= ScreenWidth) { dsputc ('^'); dsputc (c < 040 ? (c & 037) + 0100 : '?'); } } else { col += 4; if (col <= ScreenWidth) { dsputc ('\\'); dsputc (((c >> 6) & 3) + '0'); dsputc (((c >> 3) & 7) + '0'); dsputc ((c & 7) + '0'); } } else { col++; if (col <= ScreenWidth) dsputc (c); } } if (col > ScreenWidth) { setpos (l, ScreenWidth); dsputc ('$'); } if (setcurs) { cursY = l; cursX = col > ScreenWidth ? ScreenWidth : col; } } X/* dump one line from the current buffer starting at character n onto line l; setting cursX and cursY if appropriate */ DumpBfl (n, l, w) register struct window *w; register n; { register col = ScreenWidth + 1 - left; register lim = NumCharacters; int misseddot = 1; register char c; while (1) { if (n == dot) { if (w == wn_cur && (!GSaveMiniBuf || !InMiniBuf)) { cursX = col; cursY = l; DotCol = col; ColValid++; if (cursX > ScreenWidth) cursX = ScreenWidth; } misseddot = 0; } if (n > lim) { n++; c = '\n'; break; } if (MouseY == l && MouseX<col && MouseWin==0) { MouseWin = w; MouseDot = n-1; } c = CharAt (n); n++; if (c == '\n') break; if (c == 011 && bf_mode.md_TabSize >= 1) { col = ((col - 1) / bf_mode.md_TabSize + 1) * bf_mode.md_TabSize + 1; if (col < ScreenWidth) setpos (l, col); else if (WrapLines) { n--; break; } } else if (c < 040 || c >= 0177) if (CtlArrow && (c & 0200) == 0) { col += 2; if (col <= ScreenWidth) { dsputc ('^'); dsputc (c < 040 ? (c & 037) + 0100 : '?'); } else if (WrapLines) { n--; break; } } else { col += 4; if (col <= ScreenWidth) { dsputc ('\\'); dsputc (((c >> 6) & 3) + '0'); dsputc (((c >> 3) & 7) + '0'); dsputc ((c & 7) + '0'); } else if (WrapLines) { n--; break; } } else { col++; if (col <= ScreenWidth) dsputc (c); else if (WrapLines) { n--; break; } } } if (MouseY == l && MouseWin==0) { MouseWin = w; MouseDot = n-1; } LineWrapped = 0; if (col > ScreenWidth || c != '\n') { setpos (l, ScreenWidth); dsputc (WrapLines ? '\\' : '$'); if (WrapLines) LineWrapped++; } return misseddot ? n : -n; } X/* dump the text from the indicated window on the indicated line; the current buffer must be the one tied to this window */ DumpWin (Window, Line, CanMove) register struct window *Window; register Line; { register left = Window -> w_next ? Window -> w_height - 1 : Window -> w_height; register n = ToMark (Window -> w_start); int misseddot = 1; int DoClear = 0; if (CanMove && ((n > FirstCharacter && CharAt (n - 1) != '\n') || n < FirstCharacter)) { n = n < FirstCharacter ? FirstCharacter : ScanBf ('\n', n, -1); SetMark (Window -> w_start, Window -> w_buf, n); } if (Window -> w_next == 0) { MBLine = Line; if (GSaveMiniBuf && MiniBuf == 0) return 0; clearline (Line); if (MiniBuf) { if (n == 1) DumpStr (MiniBuf, 300, Line, 0); if (*MiniBuf == 0) { while (--left > 0) clearline (++Line); return 0; } } } else clearline (Line); while (--left >= 0) { register next; if (DoClear) clearline (Line); DoClear++; next = DumpBfl (n, Line++, Window); if (next < 0) { if (Window == wn_cur) { SetMark (OneLStart, bf_cur, LineWrapped ? 1 : n); OneLValid = !LineWrapped; OneLLine = Line - 1; } next = -next; misseddot = 0; } n = next; } return misseddot; } X/* Leave emacs after (optionally) spitting some expletive on the tty */ X/* VARARGS 1 */ quit (code, fmt, args) char *fmt; { #ifdef subprocesses kill_processes (); #endif if (QuitDoQuitMpx) QuitMpx (); if (QuitDoRstDsp) RstDsp (); if (fmt) _doprnt (fmt, &args, stderr); #ifdef OneEmacsPerTty UnlockTty (); #endif exit (code); } X/* Scan the current buffer for the k'th occurrence of character c, starting at position n; k may be negative. Returns the position of the character following the one found */ ScanBf (c, n, k) char c; register n; { while (k) if (k > 0) { do { if (n > NumCharacters) return n; if (CharAt (n) == c) break; n++; } while (1); if(--k) n++; } else { do { n--; if (n < FirstCharacter) return FirstCharacter; if (CharAt (n) == c) break; } while (1); k++; } return n + 1; } X/* do a screen update, taking possible shortcuts into account */ DoDsp (SaveMiniBuf) { register SlowUpdate = 0; register DoneMiniBuf = 0; GSaveMiniBuf = SaveMiniBuf; if (ScreenGarbaged || err || (LastRedisplayPaused && !InMiniBuf)) Cant1WinOpt++, DumpMiniBuf++, LastRedisplayPaused = 0; if (Cant1WinOpt) Cant1LineOpt++, RedoModes++; if (!Cant1LineOpt && OneLValid && !OneLStart -> m_modified && OneLStart -> m_buf == bf_cur) { register n = ToMark (OneLStart); clearline (OneLLine); if (MiniBuf && wn_cur -> w_next == 0) { if (n == 1) DumpStr (MiniBuf, 300, OneLLine, 0); DoneMiniBuf++; } if (DumpBfl (n, OneLLine, wn_cur) < 0 && !LineWrapped) goto update; /* we made it ! */ else if (!WrapLines) SlowUpdate = -1; } DoneMiniBuf++; SlowUpdate++; OneLValid = 0; if (FullUpd ()) SlowUpdate = 1; update: if (MiniBuf && (!GSaveMiniBuf || *MiniBuf)) { if (!DoneMiniBuf) { clearline (MBLine); DumpStr (MiniBuf, 300, MBLine, 0); } if (ResetMiniBuf) { MiniBuf = ResetMiniBuf; if (*ResetMiniBuf == 0) ResetMiniBuf = 0; } else MiniBuf = *MiniBuf ? "" : 0; } UpdateScreen (SlowUpdate); if (err) { Ding (); err = 0; } Cant1LineOpt = 0; Cant1WinOpt = CantEverOpt; fflush (stdout); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 664 window.c /bin/echo -n ' '; /bin/ls -ld window.c fi -- In-Real-Life: Chris Torek, Univ of MD Comp Sci UUCP: {seismo,allegra,brl-bmd}!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris.umcp-cs@UDel-Relay