This is dmnormal.c in view mode; [Download] [Up]
/* dmnormal.c */ /* Copyright 1995 by Steve Kirkendall */ char id_dmnormal[] = "$Id: dmnormal.c,v 2.29 1996/09/21 01:21:36 steve Exp $"; #include "elvis.h" #if USE_PROTOTYPES static DMINFO *init(WINDOW win); static void term(DMINFO *info); static MARK move(WINDOW w, MARK from, long linedelta, long column, BOOLEAN cmd); static MARK wordmove(MARK cursor, long count, BOOLEAN backward, BOOLEAN whitespace); static long mark2col(WINDOW w, MARK mark, BOOLEAN cmd); static MARK setup(MARK top, long cursor, MARK bottom, DMINFO *info); static MARK image(WINDOW w, MARK line, DMINFO *info, void (*draw)(CHAR *p, long qty, _char_ font, long offset)); static void indent(WINDOW w, MARK line, long linedelta); static CHAR *tagatcursor(WINDOW win, MARK cursor); static MARK tagload(CHAR *tagname, MARK from); #endif /* start the mode, and allocate modeinfo */ static DMINFO *init(win) WINDOW win; { return NULL; } /* end the mode, and free the modeinfo */ static void term(info) DMINFO *info; /* window-specific info about mode */ { } /* Move vertically, and to a given column (or as close to column as possible) */ static MARK move(w, from, linedelta, column, cmd) WINDOW w; /* window where buffer is shown */ MARK from; /* old position */ long linedelta; /* change in line number */ long column; /* desired column */ BOOLEAN cmd; /* if True, we're in command mode; else input mode */ { static MARKBUF tmp; long col, lnum; long offset; CHAR *cp; assert(w != NULL || column == 0); /* move forward/back to the start of the line + linedelta */ lnum = markline(from) + linedelta; if (lnum < 1) lnum = 1; else if (lnum > o_buflines(markbuffer(from))) lnum = o_buflines(markbuffer(from)); offset = lowline(bufbufinfo(markbuffer(from)), lnum); /* now move to the left far enough to find the desired column */ (void)scanalloc(&cp, marktmp(tmp, markbuffer(from), offset)); for (col = 0; w && cp && *cp != '\n' && col <= column; offset++, scannext(&cp)) { /* add the width of this character */ if (*cp == '\t' && (!o_list(w) || w->state->acton)) { col = col + o_tabstop(markbuffer(w->cursor)) - (col % o_tabstop(markbuffer(w->cursor))); } else if (*cp < ' ' || *cp == 127) { col += 2; } else { col++; } } /* The above loop normally exits when we've PASSED the desired column, * so we normally want to back up one character. Two important * exceptions: In input mode, if we break out of that loop because we * hit '\n' then we want to leave the cursor on the '\n' character. * If we hit the end of the buffer, then we want to leave the cursor * on the last character of the buffer but we also need to be careful * about empty buffers. */ if (col > 0 && !(!cmd && (!cp || *cp == '\n') && col <= column)) { offset--; } /* return the mark */ scanfree(&cp); return marktmp(tmp, markbuffer(from), offset); } /* Convert a mark to a column number */ static long mark2col(w, mark, cmd) WINDOW w; /* window where buffer is shown */ MARK mark; /* mark to be converted */ BOOLEAN cmd; /* if True, we're in command mode; else input mode */ { long col; CHAR *cp; MARK front; long nchars; /* if the buffer is empty, the column must be 0 */ if (o_bufchars(markbuffer(mark)) == 0) { return 0; } /* find the front of the line */ front = move(w, mark, 0, 0, cmd); nchars = markoffset(mark) - markoffset(front); /* in command mode, we leave the cursor on the last cell of any * wide characters such as tabs. To accomplish this, we'll find * the column of the following character, and then subtract 1. */ if (cmd) { nchars++; } /* count character widths until we find the requested mark */ for (scanalloc(&cp, front), col = 0; cp && nchars > 0; nchars--, scannext(&cp)) { if (*cp == '\t' && (!o_list(w) || w->state->acton)) { col = col + o_tabstop(markbuffer(w->cursor)) - (col % o_tabstop(markbuffer(w->cursor))); } else if (*cp == '\n') { #if 1 col++; #else /* no change to col */ #endif } else if (*cp < ' ' || *cp == 127) { col += 2; } else { col++; } } scanfree(&cp); /* the other half of the "cmd" hack */ if (cmd && col > 0) { col--; } return col; } /* This function implements most of the logic for the visual <b>, <e>, and * <w> commands. If it succedes, it adjusts the starting mark and returns * it; if it fails, it returns NULL and leaves the starting mark unchanged. */ static MARK wordmove(cursor, count, backward, whitespace) MARK cursor; /* starting position */ long count; /* number of words to move by */ BOOLEAN backward; /* if True, move backward; else forward */ BOOLEAN whitespace; /* if True, trailing whitespace is included */ { BOOLEAN inword, inpunct; CHAR *cp; long offset; long end; /* start the scan */ scanalloc(&cp, cursor); offset = markoffset(cursor); assert(cp != NULL); end = o_bufchars(markbuffer(cursor)); /* figure out if we're in the middle of a word */ if (backward || !whitespace || isspace(*cp)) { inword = inpunct = False; } else if (isalnum(*cp) || *cp == '_') { inword = True; inpunct = False; } else { inword = False; inpunct = True; } /* continue... */ if (backward) { /* move backward until we hit the top of the buffer, or * the start of the desired word. */ while (count > 0 && offset > 0) { scanprev(&cp); assert(cp != NULL); if (isspace(*cp)) { if (inword || inpunct) { count--; } inpunct = inword = False; } else if (isalnum(*cp) || *cp == '_') { if (inpunct) { count--; } inword = True; inpunct = False; } else { if (inword) { count--; } inword = False; inpunct = True; } if (count > 0) { offset--; } } /* if we hit offset==0 and were in a word, we found the start * of the first word. Count it here. */ if (offset == 0 && (inword || inpunct)) { count--; } } else { /* move forward until we hit the end of the buffer, or * the start of the desired word. */ while (count > 0 && offset < end - 1) { scannext(&cp); assert(cp != NULL); if (isspace(*cp)) { if ((inword || inpunct) && !whitespace) { count--; } inword = inpunct = False; if (count > 0) { offset++; } } else if (isalnum(*cp) || *cp == '_') { if ((!inword && whitespace) || inpunct) { count--; } inword = True; inpunct = False; if (count > 0 || whitespace) { offset++; } } else { if ((!inpunct && whitespace) || inword) { count--; } inword = False; inpunct = True; if (count > 0 || whitespace) { offset++; } } } } /* cleanup */ scanfree(&cp); /* If we were moving forward and had only one more word to go, then * move the cursor to the last character in the buffer -- usually a * newline character. Pretend the count was decremented to 0. */ if (count == 1 && !backward && whitespace && end > 0) { offset = end - 1; count = 0; } /* if the count didn't reach 0, we failed */ if (count > 0) { return NULL; } /* else set the cursor's offset */ assert(offset < end && offset >= 0); marksetoffset(cursor, offset); return cursor; } /* Choose a line to appear at the top of the screen, and return its mark. * Also, initialize the info for the next line. */ static MARK setup(top, cursor, bottom, info) MARK top; /* where previous image started */ long cursor; /* offset of cursor position */ MARK bottom; /* where previous image ended */ DMINFO *info; /* window-specific info about mode */ { static MARKBUF tmp; long topoff; long bottomoff; long other; long i; /* if the cursor is still on the screen (or very near the bottom) * then use the same top. */ topoff = markoffset(move((WINDOW)0, top, 0, 0, True)); bottomoff = markoffset(move((WINDOW)0, bottom, o_nearscroll, 0, True)); if (cursor >= topoff && (cursor < bottomoff || bottomoff < markoffset(bottom) + 1)) { return marktmp(tmp, markbuffer(top), topoff); } /* if the cursor is on the line before the top, then scroll back */ if (topoff > 0) { for (i = 1; i < o_nearscroll; i++) { other = markoffset(move((WINDOW)0, top, -i, 0, True)); if (cursor >= other && cursor < topoff) { return marktmp(tmp, markbuffer(top), other); } } } /* else try to center the line in the window */ other = cursor - (bottomoff - topoff) / 2; if (other < 0) { other = 0; } other = markoffset(move((WINDOW)0, marktmp(tmp, markbuffer(top), other), 0, 0, True)); return marktmp(tmp, markbuffer(top), other); } static MARK image(w, line, info, draw) WINDOW w; /* window where drawing will go */ MARK line; /* start of line to draw next */ DMINFO *info; /* window-specific info about mode */ void (*draw)P_((CHAR *p, long qty, _char_ font, long offset)); /* function for drawing a single character */ { int col; CHAR *cp; CHAR tmpchar; long offset; static MARKBUF tmp; long startoffset; /* offset of first contiguous normal char */ int qty; /* number of contiguous normal chars */ CHAR buf[100]; /* buffer, holds the contiguous normal chars */ int i; /* initialize startoffset just to silence a compiler warning */ startoffset = 0; /* for each character in the line... */ qty = 0; for (col = 0, offset = markoffset(line), scanalloc(&cp, line); cp && *cp != '\n'; offset++, scannext(&cp)) { /* some characters are handled specially */ if (*cp == '\f' && markoffset(w->cursor) == o_bufchars(markbuffer(w->cursor))) { /* when printing, a formfeed ends the line (and page) */ break; } else if (*cp == '\t' && (!o_list(w) || w->state->acton)) { /* output any preceding normal characters */ if (qty > 0) { (*draw)(buf, qty, 'n', startoffset); qty = 0; } /* display the tab character as a bunch of spaces */ i = o_tabstop(markbuffer(w->cursor)); i -= (col % i); tmpchar = ' '; (*draw)(&tmpchar, -i, 'n', offset); col += i; } else if (*cp < ' ' || *cp == 127) { /* output any preceding normal characters */ if (qty > 0) { (*draw)(buf, qty, 'n', startoffset); qty = 0; } /* control characters */ tmpchar = '^'; (*draw)(&tmpchar, 1, 'n', offset); tmpchar = *cp ^ 0x40; (*draw)(&tmpchar, 1, 'n', offset); col += 2; } else { /* starting a new string of contiguous normal chars? */ if (qty == 0) { startoffset = offset; } /* add this char to the string */ buf[qty++] = *cp; col++; /* if buf[] is full, flush it now */ if (qty == QTY(buf)) { (*draw)(buf, qty, 'n', startoffset); qty = 0; } } } /* output any normal chars from the end of the line */ if (qty > 0) { (*draw)(buf, qty, 'n', startoffset); qty = 0; } /* end the line */ if (o_list(w) && !w->state->acton && *cp == '\n') { (*draw)(toCHAR("$"), 1, 'n', offset); } (*draw)(cp ? cp : toCHAR("\n"), 1, 'n', offset); if (cp) { offset++; } else { offset = o_bufchars(markbuffer(w->cursor)); } scanfree(&cp); return marktmp(tmp, markbuffer(w->cursor), offset); } /* This function implements autoindent. Given the MARK of a newly created * line, insert a copy of the indentation from another line. The line whose * indentation is to be copied is specified as a line delta. Usually, this * will be -1 so the new line has the same indentation as a previous line. * The <Shift-O> command uses a linedelta of 1 so the new line will have the * same indentation as the following line. */ static void indent(w, line, linedelta) WINDOW w; /* windows whose options are used */ MARK line; /* new line to adjust */ long linedelta; /* -1 to copy from previous line, etc. */ { MARKBUF from, to; /* bounds of whitespace in source line */ MARKBUF bline; /* copy of the "line" argument */ CHAR *cp; /* used for scanning whitespace */ BOOLEAN srcblank; /* is source indentation from a blank line? */ assert(o_autoindent(markbuffer(line))); assert(markbuffer(w->cursor) == markbuffer(line)); /*assert(scanchar(w->cursor) == '\n');*/ /* find the start of the source line */ bline = *line; from = *dispmove(w, linedelta, 0); if (markoffset(&from) == markoffset(&bline)) { /* can't find source line -- at edge of buffer, maybe? */ return; } /* find the end of the source line's whitespace */ for (scanalloc(&cp, &from); cp && (*cp == ' ' || *cp == '\t'); scannext(&cp)) { } if (!cp) { /* hit end of buffer without finding end of line -- do nothing */ scanfree(&cp); return; } to = *scanmark(&cp); srcblank = (BOOLEAN)(*cp == '\n'); scanfree(&cp); if (markoffset(&from) != markoffset(&to)) { /* copy the source whitespace into the new line */ bufpaste(&bline, &from, &to); /* if the source line was blank, then delete its whitespace */ if (srcblank) { if (linedelta > 0L) { /* tweak from & to, due to bufpaste() */ markaddoffset(&to, markoffset(&to) - markoffset(&from)); marksetoffset(&to, markoffset(&from)); } else { /* tweak bline for the following bufreplace() */ markaddoffset(&bline, markoffset(&from) - markoffset(&to)); } bufreplace(&from, &to, NULL, 0L); } /* tweak the argument mark, to leave cursor after whitespace */ marksetoffset(line, markoffset(&bline) + markoffset(&to) - markoffset(&from)); bline = *line; } /* if the line had some other indentation before, then delete that */ for (scanalloc(&cp, line); cp && (*cp == ' ' || *cp == '\t'); scannext(&cp)) { } if (cp) { bufreplace(&bline, scanmark(&cp), NULL, 0); } scanfree(&cp); } /* Return a dynamically-allocated string containing the name of the tag at the * cursor, or NULL if the cursor isn't on a tag. */ static CHAR *tagatcursor(win, cursor) WINDOW win; MARK cursor; { MARKBUF curscopy; /* a copy of the cursor */ MARK word; /* mark for the front of the word */ /* find the ends of the word */ curscopy = *cursor; word = wordatcursor(&curscopy); /* if not on a word, then return NULL */ if (!word) return NULL; /* copy the word into RAM, and return it */ return bufmemory(word, &curscopy); } /* Lookup a tag name, load the file where that tag was defined, and return * the MARK of its position within that buffer. If the tag can't be found * or loaded for any reason, then issue an error message and return NULL. */ static MARK tagload(tagname, from) CHAR *tagname; /* name of tag to lookup */ MARK from; /* initial position of the cursor */ { static MARKBUF retmark; /* the return value */ BUFFER buf; /* the buffer containing the tag */ CHAR tagline[1000]; /* a line from the tags file */ int bytes; /* number of bytes in I/O buffer */ char *pathname; /* full pathname of current "tags" file */ int len; /* significant length of tagname */ BOOLEAN fulllen; /* len is the full length of the tag */ BOOLEAN allnext; /* is the whole next line in the buffer? */ char *loadname; /* name of file to load */ EXINFO xinfb; /* dummy ex command, for parsing tag address */ BOOLEAN wasmagic; /* stores the normal value of o_magic */ CHAR *src, *dst; /* find the significant length of tagname */ len = CHARlen(tagname); fulllen = True; if (len > o_taglength && o_taglength > 0) { len = o_taglength; fulllen = False; } /* for each tags file named in the "tags" option... */ for (pathname = iopath(tochar8(o_tags), "tags", True); pathname; pathname = iopath(NULL, "tags", True)) { /* open the tags file */ if (!ioopen(pathname, 'r', False, False, False)) { return NULL; } /* Compare the tag of each line against the tagname */ bytes = ioread(tagline, QTY(tagline) - 1); while (bytes > len && (CHARncmp(tagname, tagline, (size_t)len) || (fulllen && !isspace(tagline[len])))) { /* delete this line from tagline[] */ for (src = tagline; src < &tagline[bytes] && *src != '\n'; src++) { } for (dst = tagline, src++, allnext = False; src < &tagline[bytes]; ) { if (*src == '\n') allnext = True; *dst++ = *src++; } bytes = (int)(dst - tagline); /* if the next line is incomplete, read some more text * from the tags file. */ if (!allnext || bytes <= len) { bytes += ioread(dst, (int)QTY(tagline) - bytes - 1); } } (void)ioclose(); /* did we find the tag? */ if (bytes > len) { goto Found; } } msg(MSG_ERROR, "[S]tag $1 not found", tagname); return NULL; Found: /* skip past the tagname to find the start of the filename */ for (src = tagline; src < &tagline[bytes] && !isspace(*src); src++) { } for ( ; src < &tagline[bytes] && isspace(*src); src++) { } /* locate the end of the filename, and mark it with a NUL byte */ for (dst = src; dst < &tagline[bytes] && !isspace(*dst); dst++) { } *dst++ = '\0'; /* if the filename isn't a full pathname, then assume it is in the * same directory as the "tags" file. */ if (!isalnum(*src)) { loadname = tochar8(src); } else { loadname = dirpath(dirdir(pathname), tochar8(src)); } /* find a buffer containing the file, or load the file into a buffer */ buf = buffind(toCHAR(loadname)); if (!buf) { if (dirperm(loadname) == DIR_NEW) { msg(MSG_ERROR, "[s]$1 doesn't exist", loadname); return NULL; } buf = bufload(NULL, loadname, False); if (!buf) { /* bufload() already gave error message */ return NULL; } } if (o_bufchars(buf) == 0) { goto NotFound; } /* locate the tag address field */ for (src = dst; src < &tagline[bytes] && isspace(*src); src++) { } for (dst = src; dst < &tagline[bytes] && *dst != '\n'; dst++) { } *dst = '\0'; /* convert the tag address into a line number */ scanstring(&dst, src); memset((char *)&xinfb, 0, sizeof xinfb); (void)marktmp(xinfb.defaddr, buf, 0); wasmagic = o_magic; o_magic = False; if (!exparseaddress(&dst, &xinfb)) { scanfree(&dst); o_magic = wasmagic; goto NotFound; } scanfree(&dst); o_magic = wasmagic; (void)marktmp(retmark, buf, lowline(bufbufinfo(buf), xinfb.to)); exfree(&xinfb); return &retmark; NotFound: msg(MSG_WARNING, "tag address out of date"); return marktmp(retmark, buf, 0L); } DISPMODE dmnormal = { "normal", "Standard vi", True, /* display generating can be optimized */ True, /* should use normal wordwrap */ 0, /* no window options */ NULL, /* no descriptions of window options */ 0, /* no global options */ NULL, /* no descriptions of global options */ NULL, /* no values of global options */ init, term, mark2col, move, wordmove, setup, image, NULL, /* doesn't need a header */ indent, tagatcursor, tagload, NULL /* no tagnext() function */ };
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.