/************************************************************************ * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is * * provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ************************************************************************/ #define NO_PROCDECL /* Kludge to get this through teensy pdp11 compiler. */ #include "jove.h" RCS("$Id: util.c,v 14.32.0.11 1994/06/03 04:16:31 tom Exp tom $") #include "ctype.h" #include "io.h" DEF_INT( "abort-char", AbortChar, V_CHAR ) = CTL('G'); Line * lastline(lp) register Line *lp; { register Line *next; while (next = lp->l_next) lp = next; return lp; } int alarmed ZERO; /* * Keystrokes are remembered in their "raw", i.e., unprocessed form in the * raw_strokes buffer. Occasionally these strokes are appended to the * prettyprint stroke buffer, and the raw_strokes queue is emptied, * whenever `key_strokes' is called. Thus formatting happens only when it * is really needed so we save some time. */ #define NSTROKES 20 private short raw_strokes[NSTROKES], *raw_p; private char printed_strokes[5*NSTROKES], *print_p; void init_strokes() { alarmed = 0; raw_p = raw_strokes; print_p = printed_strokes; } void add_stroke(c) { register short *p = raw_p; if (p == &raw_strokes[NSTROKES]) p -= NSTROKES; *p++ = c; raw_p = p; } char * key_strokes() { register short *p = raw_strokes; register char *d = print_p, *end = &printed_strokes[sizeof printed_strokes - 5]; while (d < end && p < raw_p) { d += sprintf(d, "%p ", *p++); } print_p = d; raw_p = raw_strokes; return printed_strokes; } /* * [TRH] a nice cheat -- replace contents of keystroke buffer */ void repl_strokes(s) register const char *s; { register char *d = printed_strokes; if (alarmed) message(s); while (*d++ = *s++) ; print_p = --d; raw_p = raw_strokes; } #ifndef TINY int pop_stroke() { register int c = EOF; register short *p = raw_p; if (p > raw_strokes) { c = *--p; raw_p = p; } return c; } #endif /* Timeout is now handled by getchar (or rawchar if FUNCKEYS) */ void (*timeout_proc)__(( void )); #define trigger() { if (!alarmed) timeout_proc = slowpoke; } #define end_wait() { timeout_proc = NULL; } private void slowpoke __(( void )); private void slowpoke() { alarmed++; f_mess("%s", key_strokes()); end_wait(); } int waitchar() { register int c; trigger(); c = getch(); if (alarmed) message(key_strokes()); end_wait(); return c; } /* dir > 0 means forward; else means backward. */ #if 0 /* [TRH] unused now... */ char * StrIndex(dir, buf, charpos, what) register const char *buf; register char what; { register const char *cp = &buf[charpos]; if (dir > 0) { while (*cp) if (*cp++ == what) return (char *)(--cp); } else { cp++; while (cp > buf) if (*--cp == what) return (char *)(cp); } return 0; } #endif int blnkp(buf) register const char *buf; { register char c; do { if ((c = *buf++) == '\0') return YES; } while (isspace(c)); return NO; /* It's zero if we didn't get to the end of the Line */ } Line * next_line(line, num) Line *line; int num; { register Line *lp, *np; register int n; if (lp = line) if ((n = num) < 0) while (++n <= 0 && (np = lp->l_prev) != NULL) lp = np; else while (--n >= 0 && (np = lp->l_next) != NULL) lp = np; return lp; } /* prev_line is replaced with a macro. */ /* If new line is != current line, then save current line. Then set dot to line, and if they weren't equal get that line into linebuf. [TRH Jun-91] Check that `col' is contained in the line, pad with blanks if necessary. This makes life in OverWrite mode a lot easier... */ void DotTo(line, col) Line *line; register int col; { register Buffer *cb = curbuf; { register Line *newdot; if ((newdot = line) && (newdot != cb->b_dot)) { lsave(); cb->b_dot = newdot; cb->b_char = getline(newdot->l_dline, linebuf); /* i.e., length of line */ } } if ((col -= cb->b_char) > 0) { register char *cp = &linebuf[cb->b_char]; do { if (*cp++ == '\0') { --cp; if (BufMinorMode(cb, OverWrite)) ins_c(' ', cp, 0, col, (int)(&linebuf[LBSIZE] - cp)); else col = 0; break; } } while (--col); cb->b_char = cp - linebuf; } cb->b_char += col; } void SetDot(pos) Bufpos *pos; { register Bufpos *bp; if (bp = pos) DotTo(bp->p_line, bp->p_char); } void ToFirst() { SetLine(curbuf->b_first); } void ToLast() { SetLine(curbuf->b_last); Eol(); } DEF_INT( "mark-threshold", MarkThresh, V_BASE10 ) = 22; /* Average screen size ... */ static int line_diff; int LineDist(nextp, endp) Line *nextp, *endp; { inorder(nextp, 0, endp, 0); return line_diff; } int inorder(nextp, char1, endp, char2) register Line *nextp, *endp; { register Line *prevp; line_diff = 0; if (nextp == endp) return char1 < char2; for (prevp = nextp; ; line_diff++) { if (nextp == endp) return YES; if (prevp == endp) return NO; if (!nextp) { if (!prevp) return line_diff = 0, -1; } else { nextp = nextp->l_next; if (!prevp) continue; } prevp = prevp->l_prev; } /* NOTREACHED */ } void PushPntp(line) Line *line; { if (LineDist(curline, line) >= MarkThresh) set_mark(); } int length(line) Line *line; { return strlen(lcontents(line)); } int lineno(line) Line *line; { register int n = 0; register Line *l; if (l = line) do ++n; while (l = l->l_prev); return n; } /* copy the word at dot in buffer "buf" of size "size" */ void WordAtDot(buf, size) register char *buf; register int size; { register char *cp = &linebuf[curchar]; while (--cp >= linebuf && isword(*cp)) ; cp++; while (--size > 0 && isword(*cp)) *buf++ = *cp++; *buf = '\0'; } /* Are there any modified buffers? Allp means include B_PROCESS buffers in the check. */ int ModBufs(allp) { register Buffer *b; if (b = world) do { switch (b->b_type) { default: if (!allp) continue; /* fall through... */ case B_FILE: if (IsModified(b)) return YES; case B_SCRATCH: continue; } } while (b = b->b_next); return NO; } char * filename(b) const Buffer *b; { register const char *fname; return (fname = b->b_fname) ? pr_name(fname) : "[No file]"; } char * itos(num) int num; { static char line[1 + 3 * sizeof(int)]; sprintf(line, "%d", num); return line; } void tiewind(w, bp) register Window *w; register Buffer *bp; { register Buffer *old_bp; updmodline(); /* Kludge ... but speeds things up considerably */ w->w_line = bp->b_dot; w->w_char = bp->b_char; if (old_bp = w->w_bufp) old_bp->b_top = w->w_top; w->w_bufp = bp; SetTop(w, bp->b_top); } char * lcontents(line) register const Line *line; { if (line == curline) return linebuf; else return lbptr(line); } ltobuf(line, buf) register Line *line; register char *buf; { if (line == curline) { register char *lb = linebuf; return (buf == lb) ? strlen(lb) : appcpy(buf, lb) - buf; } return getline(line->l_dline, buf); } void DOTsave(bp) register Bufpos *bp; { register Buffer *cb = curbuf; bp->p_line = cb->b_dot; bp->p_char = cb->b_char; } /* Return non-zero if we had to rearrange the order. */ int fixorder(line1, char1, line2, char2) register Line **line1, **line2; register int *char1, *char2; { register Line *tline; register int tchar; if (inorder(*line1, *char1, *line2, *char2)) return NO; tline = *line1; tchar = *char1; *line1 = *line2; *char1 = *char2; *line2 = tline; *char2 = tchar; return YES; } /* Fill in the bounds of the current region in Bufpos array `r[2]', return whether point is at end of region. */ int CurRegion(r) register Bufpos r[2]; { register Mark *m = CurMark(); DOTsave(&r[0]); r[1].p_line = m->m_line; r[1].p_char = m->m_char; return fixorder(&r[0].p_line, &r[0].p_char, &r[1].p_line, &r[1].p_char); } int inlist(first, what) Line *first; register Line *what; { register Line *line; if (line = first) do { if (line == what) return YES; } while (line = line->l_next); return NO; } /* Make `buf' modified and tell the redisplay code to update the modeline if it will need to be changed. */ int ModCount ZERO; void modify() { extern int DOLsave; register Buffer *cb = curbuf; if (!cb->b_modified) { chk_file(cb->b_fname, "modify"); cb->b_modified++; updmodline(); } DOLsave++; if (!Asking) ModCount++; } DEF_CMD( "make-buffer-unmodified", unmodify, NO ) { updmodline(); curbuf->b_modified = NO; } void len_error(how) void (*how)__(( const char *_(fmt), ... )); { (*how)("[line too long]"); } /* Insert num number of c's at offset atchar in a linebuf of LBSIZE */ void ins_c(c, buf, atchar, num, maxsize) char c, *buf; { register char *s, *t, *at; if (num <= 0) return; s = at = &buf[atchar]; while (*s++) ; /* skip to end of line */ if ((int)(s - at) + atchar + num > maxsize) /* incl. terminating \0 */ len_error(complain); /* shift tail num characters forward (by copying backwards) */ t = s + num; while (s > at) *--t = *--s; /* now fill gap with c's */ while (s < t) *s++ = c; } int TwoBlank() { register Line *next; return (((next = curline->l_next) != NULL) && (*(lcontents(next)) == '\0') && ((next = next->l_next) != NULL) && (*(lcontents(next)) == '\0')); } void linecopy(onto, atchar, from) register char *onto; register const char *from; { register const char *endp = &onto[LBSIZE - 2]; onto += atchar; while (*onto++ = *from++) if (onto > endp) len_error(error); } char * syserr() { register int err; if (!(err = errno)) return "Disk full?"; /* an educated guess... */ #ifndef TINY # if _ANSI_LIBRARY_ # if VAXC return strerror(err, vaxc$errno); # else return strerror(err); # endif # else { # ifndef sys_nerr extern const int sys_nerr; # endif # ifndef sys_errlist extern const char *sys_errlist[]; # endif if ((unsigned) err < sys_nerr) return sys_errlist[err]; } return itos(err); # endif #else return itos(err); #endif /* TINY */ } char * IOerr(err, file) const char *err, *file; { #ifdef TINY return sprint("Couldn't %s \"%s\".", err, pr_name(file)); #else return sprint("Couldn't %s \"%s\". (%s)", err, pr_name(file), syserr()); #endif } #if unix void pipeclose(p) register int *p; { close(*p++); close(*p); } void pipeopen(p) int p[2]; { if (pipe(p) < 0) complain("[Pipe failed]"); } #endif /* unix || vms */ /* NOSTRICT */ void_* emalloc(size) size_t size; { register void_* ptr; while ((ptr = malloc(size)) == NULL) /* Try garbage collecting lines */ if (!GCchunks()) /* Uh ... Oh screw it! */ error("[Out of memory] "); return ptr; } /* Get the current time string. */ /* Assumes ctime() returns string with format "Wkd Mon dd hh:mm:ss yyyy\n". */ #ifndef get_ctime char * get_ctime() { extern char *ctime __(( const time_t *_(timep) )); extern time_t now; register char *cp; time(&now); /* be accurate. */ cp = ctime(&now); cp[24] = '\0'; /* get rid of '\n'. */ return cp; } #endif /* get current time, as HH:MM. */ const char * hh_mm() { static char HH_MM[6]; static time_t last ZERO; extern time_t now; /* Only update if a minute or more passed since last time called, or just past a minute. */ if (now % 60 + last < now) { last = now; null_ncpy(HH_MM, get_ctime() + 11, 5); } return HH_MM; } #ifndef min int min(a, b) register int a, b; { return (a < b) ? a : b; } #endif #ifndef max int max(a, b) register int a, b; { return (a > b) ? a : b; } #endif int numcomp(s1, s2) register const char *s1, *s2; { register const char *base = s1; while (*s1 == *s2++) if (*s1++ == '\0') { --s1; break; }; return s1 - base; } char * copystr(str) const char *str; { return strcpy(emalloc(strlen(str) + 1), str); } char * set_str(strptr, value) register char **strptr; const char *value; { if (*strptr == NULL || strcmp(*strptr, value) != 0) { if (!ISSTATIC(*strptr)) { free(*strptr); *strptr = (char *) NullStr; } *strptr = copystr(value); } return *strptr; } #ifndef byte_copy void byte_copy(from, to, count) register const char *from; register char *to; register int count; { while (--count >= 0) *to++ = *from++; } /* assume there is no bzero() too */ void bzero(p, count) register char *p; register int count; { while (--count >= 0) *p++ = 0; } /* assume there is no bcmp() too */ int bcmp(s, t, count) register const char *s, *t; register int count; { while (--count >= 0) if (*s++ != *t++) return (*--s - *--t); return 0; } #endif char * strlwr(str) char *str; { register char *s = str; register char c; while (c = tolower(*s)) { #if (BPC == 8) /* "regular" ascii characters only */ if ((unsigned) c > '\177') s++; else #endif *s++ = c; } return str; } char * strupr(str) char *str; { register char *s = str; register char c; while (c = toupper(*s)) { #if (BPC == 8) /* "regular" ascii characters only */ if ((unsigned) c > '\177') s++; else #endif *s++ = c; } return str; } #if 0 /* never used anywhere */ int casecmp(str1, str2) register const char *str1, *str2; { while (CEquiv(*str1) == CEquiv(*str2++)) if (*str1++ == '\0') return 0; return (*str1 - *--str2); } #endif int casencmp(str1, str2, n) register const char *str1, *str2; register int n; { while (--n >= 0) if (CEquiv(*str1) != CEquiv(*str2++)) return (*str1 - *--str2); else if (*str1++ == '\0') break; return 0; } void null_ncpy(to, from, n) register char *to; register const char *from; register int n; { while (--n >= 0) if ((*to++ = *from++) == '\0') return; *to = '\0'; } char * appcpy(t, f) register char *t; register const char *f; { while (*t++ = *f++) ; return --t; } int sindex(pattern, string) /* equiv to ANSI strstr() */ const char *pattern, *string; { register const char *p = pattern, *s = string; register char c; if ((c = *p++) == '\0') /* Null pattern matches anything. */ return 1; while (*s != '\0') { if (c == *s++ && p[numcomp(p, s)] == '\0') return (s - string); } return NO; } /* Return the basename of file F. */ char * basename(f) register const char *f; { register const char *cp; register char c; #ifdef DOS if (f[0] && f[1] == ':') f += 2; #endif for (cp = f; (c = *cp++); ) { #if vms /* handle VMS-style filespec. */ if (c == ']' || c == ':' || c == '>') f = cp; #endif if (ISDIRSEP(c)) f = cp; } return (char *) f; } /* Build a filename from prefix `path' and basename `name' in `buf' */ char * make_filename(buf, path, name) register char *buf; const char *path, *name; { register const char *s = path; register char *d = buf; while(*d++ = *s++) ; if (--d > buf && !ISDIRSEP(d[-1])) *d++ = SLASH; s = name; while (*d++ = *s++) ; return buf; } #ifndef DOS void make_argv(argv, ap) register char *argv[]; va_register va_list ap; { register char *cp; *argv++ = (cp = va_arg(ap, char *)); *argv++ = basename(cp); while (*argv++ = va_arg(ap, char *)) ; } #endif #if unix /* * this one is to catch exit() in crt0.o -- TRH sep-88 */ #ifndef PROFILING #ifdef _P_EXIT_RETURN_TYPE _P_EXIT_RETURN_TYPE #else void #endif #ifdef __GNUC__ volatile #endif exit(exitval) int exitval; { _exit(exitval); /* NOTREACHED */ } #endif #endif /* unix */ #if unix || vms void exec_fail(program) const char *program; { register const char *prg; if (prg = program) { extern int CapLine, CapCol; register int s_line = CapLine, s_col = CapCol; f_mess("[%s: exec failed!]", prg); Placur(s_line, s_col); /* as if we did nothing! */ flusho(); } _exit(-ENOEXEC); /* NOTREACHED */ } #endif /* unix || vms */ /* we need a predictable mktemp for recovery... */ public char * mktemp(template) register char *template; { register char *s = template; register int i = getpid(); while (*s++) ; --s; while (*--s == 'X') { *s = (i % 10) + '0'; i /= 10; } s++; i = 'A'; while (access(template, F_OK) == 0) /* file exists */ *s = i++; return (template); } #if !unix char * mktmpe(result, template) char *result; const char *template; { return mktemp(make_filename(result, TmpFilePath, template)); } #endif #ifdef ctime /* some ctime()s use sprintf with %M.Ns directives which is not supported by JOVEs sprintf. Sigh... */ # include char * ctime(time) time_t *time; { extern struct tm *localtime(); struct tm *t = localtime(time); static char result[26]; static char days[][4] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}, months[][4] = {"Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"}; sprintf(result, "%s %s %2d %02d:%02d:%02d %4d\n", days[t->tm_wday], months[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, t->tm_year + 1900); return result; } #endif /* ctime */ /* Like stat() but check if filename exists; zero out stat buffer in case of failure. */ int zstat(filename, st) const char *filename; register struct stat *st; { register const char *fname; register int result = 0; if ((fname = filename) == NULL || stat(rel_name(fname), st) != 0) { bzero(st, sizeof *st); --result; } return result; } /*====================================================================== * $Log: util.c,v $ * Revision 14.32.0.11 1994/06/03 04:16:31 tom * (CurRegion): new function, replaces ad-hoc CurMark(),DOTsave(),fixorder(). * * Revision 14.32.0.10 1994/04/22 18:24:24 tom * (make_argv): use `va_register va_list'. * * Revision 14.32.0.8 1993/11/11 01:47:31 tom * (pop_stroke): new function. * * Revision 14.32 1993/03/22 10:34:52 tom * *** empty log message *** * * Revision 14.31.0.1 1993/03/22 10:34:52 tom * fix botch in syserr(). * * Revision 14.31 1993/02/18 01:42:32 tom * simplify get_ftime(); add hh_mm() to supply cached HH:MM string; * in case[n]cmp(), replace toupper() with CEquiv(); fix type of bcmp; * remove (void) casts; lotsa random optimizations. * * Revision 14.30 1993/02/08 19:16:16 tom * cleanup whitespace; some random optimizations; add zstat(). * * Revision 14.29 1992/12/29 13:35:51 tom * more portability kludges... * * Revision 14.28 1992/10/24 01:24:23 tom * convert to "port{ansi,defs}.h" conventions. * * Revision 14.27 1992/09/22 02:03:52 tom * exit(): add `volatile' qualifier for Gnu C. * * Revision 14.26 1992/08/26 23:57:00 tom * add RCS directives. * */