Annotation of src/usr.bin/mg/basic.c, Revision 1.52
1.52 ! guenther 1: /* $OpenBSD: basic.c,v 1.51 2022/05/24 16:42:19 op Exp $ */
1.19 kjell 2:
3: /* This file is in the public domain */
1.4 niklas 4:
1.1 deraadt 5: /*
6: * Basic cursor motion commands.
7: *
8: * The routines in this file are the basic
9: * command functions for moving the cursor around on
10: * the screen, setting mark, and swapping dot with
11: * mark. Only moves between lines, which might make the
12: * current buffer framing bad, are hard.
13: */
14:
1.44 bcallah 15: #include <sys/queue.h>
1.14 vincent 16: #include <ctype.h>
1.43 guenther 17: #include <limits.h>
1.44 bcallah 18: #include <signal.h>
19: #include <stdio.h>
20: #include <stdlib.h>
21:
22: #include "def.h"
1.1 deraadt 23:
1.48 lum 24: #define percint(n1, n2) ((n1 * (int) n2) * 0.1)
25:
1.1 deraadt 26: /*
27: * Go to beginning of line.
28: */
1.3 millert 29: int
1.12 vincent 30: gotobol(int f, int n)
1.1 deraadt 31: {
1.47 lum 32: if (n == 0)
33: return (TRUE);
34:
1.3 millert 35: curwp->w_doto = 0;
1.1 deraadt 36: return (TRUE);
37: }
38:
39: /*
40: * Move cursor backwards. Do the
41: * right thing if the count is less than
42: * 0. Error if you try to move back from
43: * the beginning of the buffer.
44: */
1.3 millert 45: int
1.12 vincent 46: backchar(int f, int n)
1.1 deraadt 47: {
1.21 deraadt 48: struct line *lp;
1.1 deraadt 49:
1.3 millert 50: if (n < 0)
1.18 db 51: return (forwchar(f, -n));
1.1 deraadt 52: while (n--) {
53: if (curwp->w_doto == 0) {
1.25 kjell 54: if ((lp = lback(curwp->w_dotp)) == curbp->b_headp) {
1.50 lum 55: if (!(f & FFRAND))
56: (void)dobeep_msg("Beginning "
57: "of buffer");
1.1 deraadt 58: return (FALSE);
59: }
1.3 millert 60: curwp->w_dotp = lp;
61: curwp->w_doto = llength(lp);
1.30 kjell 62: curwp->w_rflag |= WFMOVE;
1.23 kjell 63: curwp->w_dotline--;
1.1 deraadt 64: } else
65: curwp->w_doto--;
66: }
1.18 db 67: return (TRUE);
1.1 deraadt 68: }
69:
70: /*
71: * Go to end of line.
72: */
1.3 millert 73: int
1.12 vincent 74: gotoeol(int f, int n)
1.1 deraadt 75: {
1.47 lum 76: if (n == 0)
77: return (TRUE);
78:
1.3 millert 79: curwp->w_doto = llength(curwp->w_dotp);
1.1 deraadt 80: return (TRUE);
81: }
82:
83: /*
84: * Move cursor forwards. Do the
85: * right thing if the count is less than
86: * 0. Error if you try to move forward
87: * from the end of the buffer.
88: */
1.3 millert 89: int
1.12 vincent 90: forwchar(int f, int n)
1.1 deraadt 91: {
1.3 millert 92: if (n < 0)
1.18 db 93: return (backchar(f, -n));
1.1 deraadt 94: while (n--) {
95: if (curwp->w_doto == llength(curwp->w_dotp)) {
1.3 millert 96: curwp->w_dotp = lforw(curwp->w_dotp);
1.25 kjell 97: if (curwp->w_dotp == curbp->b_headp) {
1.1 deraadt 98: curwp->w_dotp = lback(curwp->w_dotp);
1.50 lum 99: if (!(f & FFRAND))
100: (void)dobeep_msg("End of buffer");
1.18 db 101: return (FALSE);
1.1 deraadt 102: }
1.3 millert 103: curwp->w_doto = 0;
1.23 kjell 104: curwp->w_dotline++;
1.30 kjell 105: curwp->w_rflag |= WFMOVE;
1.1 deraadt 106: } else
107: curwp->w_doto++;
108: }
1.18 db 109: return (TRUE);
1.1 deraadt 110: }
111:
112: /*
1.48 lum 113: * Go to the beginning of the buffer. Setting WFFULL is conservative,
114: * but almost always the case. A universal argument of higher than 9
115: * puts the cursor back to the end of buffer.
1.1 deraadt 116: */
1.3 millert 117: int
1.12 vincent 118: gotobob(int f, int n)
1.1 deraadt 119: {
1.49 lum 120: if (!curwp->w_markp)
121: (void) setmark(f, n);
1.26 kjell 122: curwp->w_dotp = bfirstlp(curbp);
1.3 millert 123: curwp->w_doto = 0;
1.30 kjell 124: curwp->w_rflag |= WFFULL;
1.23 kjell 125: curwp->w_dotline = 1;
1.48 lum 126: if (f & FFOTHARG && n > 0) {
127: if (n > 9)
128: gotoeob(0, 0);
129: else
130: forwline(f, percint(curwp->w_bufp->b_lines, n) - 1);
131: }
1.18 db 132: return (TRUE);
1.1 deraadt 133: }
134:
135: /*
1.37 lum 136: * Go to the end of the buffer. Leave dot 3 lines from the bottom of the
137: * window if buffer length is longer than window length; same as emacs.
1.48 lum 138: * Setting WFFULL is conservative, but almost always the case. A universal
139: * argument of higher than 9 puts the cursor back to the start of buffer.
1.1 deraadt 140: */
1.3 millert 141: int
1.12 vincent 142: gotoeob(int f, int n)
1.1 deraadt 143: {
1.48 lum 144: int ln;
1.37 lum 145: struct line *lp;
1.46 jasper 146:
1.49 lum 147: if (!curwp->w_markp)
148: (void) setmark(f, n);
1.26 kjell 149: curwp->w_dotp = blastlp(curbp);
1.3 millert 150: curwp->w_doto = llength(curwp->w_dotp);
1.23 kjell 151: curwp->w_dotline = curwp->w_bufp->b_lines;
1.37 lum 152:
153: lp = curwp->w_dotp;
1.48 lum 154: ln = curwp->w_ntrows - 3;
1.37 lum 155:
1.48 lum 156: if (ln < curwp->w_bufp->b_lines && ln >= 3) {
157: while (ln--)
1.37 lum 158: curwp->w_dotp = lback(curwp->w_dotp);
159:
160: curwp->w_linep = curwp->w_dotp;
161: curwp->w_dotp = lp;
162: }
1.48 lum 163: if (f & FFOTHARG && n > 0) {
164: if (n > 9)
165: gotobob(0, 0);
166: else
167: backline(f, percint(curwp->w_bufp->b_lines, n));
168: }
169:
1.30 kjell 170: curwp->w_rflag |= WFFULL;
1.18 db 171: return (TRUE);
1.1 deraadt 172: }
173:
174: /*
175: * Move forward by full lines.
176: * If the number of lines to move is less
177: * than zero, call the backward line function to
178: * actually do it. The last command controls how
179: * the goal column is set.
180: */
1.3 millert 181: int
1.12 vincent 182: forwline(int f, int n)
1.1 deraadt 183: {
1.21 deraadt 184: struct line *dlp;
1.1 deraadt 185:
186: if (n < 0)
1.18 db 187: return (backline(f | FFRAND, -n));
1.41 florian 188: if ((dlp = curwp->w_dotp) == curbp->b_headp) {
1.50 lum 189: if (!(f & FFRAND))
190: (void)dobeep_msg("End of buffer");
1.23 kjell 191: return(TRUE);
1.41 florian 192: }
1.14 vincent 193: if ((lastflag & CFCPCN) == 0) /* Fix goal. */
1.1 deraadt 194: setgoal();
195: thisflag |= CFCPCN;
1.3 millert 196: if (n == 0)
1.18 db 197: return (TRUE);
1.23 kjell 198: while (n--) {
1.1 deraadt 199: dlp = lforw(dlp);
1.25 kjell 200: if (dlp == curbp->b_headp) {
1.23 kjell 201: curwp->w_dotp = lback(dlp);
202: curwp->w_doto = llength(curwp->w_dotp);
1.30 kjell 203: curwp->w_rflag |= WFMOVE;
1.50 lum 204: if (!(f & FFRAND))
205: (void)dobeep_msg("End of buffer");
1.23 kjell 206: return (TRUE);
1.1 deraadt 207: }
1.23 kjell 208: curwp->w_dotline++;
1.1 deraadt 209: }
1.30 kjell 210: curwp->w_rflag |= WFMOVE;
1.23 kjell 211: curwp->w_dotp = dlp;
212: curwp->w_doto = getgoal(dlp);
213:
1.18 db 214: return (TRUE);
1.1 deraadt 215: }
216:
217: /*
218: * This function is like "forwline", but
219: * goes backwards. The scheme is exactly the same.
220: * Check for arguments that are less than zero and
221: * call your alternate. Figure out the new line and
222: * call "movedot" to perform the motion.
223: */
1.3 millert 224: int
1.12 vincent 225: backline(int f, int n)
1.1 deraadt 226: {
1.21 deraadt 227: struct line *dlp;
1.1 deraadt 228:
1.3 millert 229: if (n < 0)
1.18 db 230: return (forwline(f | FFRAND, -n));
1.14 vincent 231: if ((lastflag & CFCPCN) == 0) /* Fix goal. */
1.1 deraadt 232: setgoal();
233: thisflag |= CFCPCN;
234: dlp = curwp->w_dotp;
1.41 florian 235: if (lback(dlp) == curbp->b_headp) {
1.50 lum 236: if (!(f & FFRAND))
237: (void)dobeep_msg("Beginning of buffer");
1.41 florian 238: return(TRUE);
239: }
1.25 kjell 240: while (n-- && lback(dlp) != curbp->b_headp) {
1.1 deraadt 241: dlp = lback(dlp);
1.23 kjell 242: curwp->w_dotline--;
1.41 florian 243: }
1.50 lum 244: if (n > 0 && !(f & FFRAND))
245: (void)dobeep_msg("Beginning of buffer");
1.3 millert 246: curwp->w_dotp = dlp;
247: curwp->w_doto = getgoal(dlp);
1.30 kjell 248: curwp->w_rflag |= WFMOVE;
1.18 db 249: return (TRUE);
1.1 deraadt 250: }
251:
252: /*
1.3 millert 253: * Set the current goal column, which is saved in the external variable
254: * "curgoal", to the current cursor column. The column is never off
255: * the edge of the screen; it's more like display then show position.
1.1 deraadt 256: */
1.6 art 257: void
1.12 vincent 258: setgoal(void)
1.3 millert 259: {
1.39 florian 260: curgoal = getcolpos(curwp); /* Get the position. */
1.3 millert 261: /* we can now display past end of display, don't chop! */
1.1 deraadt 262: }
263:
264: /*
265: * This routine looks at a line (pointed
266: * to by the LINE pointer "dlp") and the current
267: * vertical motion goal column (set by the "setgoal"
268: * routine above) and returns the best offset to use
269: * when a vertical motion is made into the line.
270: */
1.3 millert 271: int
1.21 deraadt 272: getgoal(struct line *dlp)
1.3 millert 273: {
1.14 vincent 274: int c, i, col = 0;
1.24 kjell 275: char tmp[5];
276:
1.14 vincent 277:
278: for (i = 0; i < llength(dlp); i++) {
279: c = lgetc(dlp, i);
1.1 deraadt 280: if (c == '\t'
281: #ifdef NOTAB
1.3 millert 282: && !(curbp->b_flag & BFNOTAB)
1.1 deraadt 283: #endif
1.14 vincent 284: ) {
285: col |= 0x07;
286: col++;
287: } else if (ISCTRL(c) != FALSE) {
288: col += 2;
289: } else if (isprint(c))
290: col++;
291: else {
1.24 kjell 292: col += snprintf(tmp, sizeof(tmp), "\\%o", c);
1.14 vincent 293: }
294: if (col > curgoal)
1.1 deraadt 295: break;
296: }
1.14 vincent 297: return (i);
1.1 deraadt 298: }
299:
300: /*
301: * Scroll forward by a specified number
302: * of lines, or by a full page if no argument.
303: * The "2" is the window overlap (this is the default
304: * value from ITS EMACS). Because the top line in
305: * the window is zapped, we have to do a hard
306: * update and get it back.
307: */
1.3 millert 308: int
1.12 vincent 309: forwpage(int f, int n)
1.1 deraadt 310: {
1.21 deraadt 311: struct line *lp;
1.1 deraadt 312:
313: if (!(f & FFARG)) {
1.3 millert 314: n = curwp->w_ntrows - 2; /* Default scroll. */
315: if (n <= 0) /* Forget the overlap */
316: n = 1; /* if tiny window. */
1.1 deraadt 317: } else if (n < 0)
1.18 db 318: return (backpage(f | FFRAND, -n));
1.31 lum 319:
1.1 deraadt 320: lp = curwp->w_linep;
1.31 lum 321: while (n--)
1.34 lum 322: if ((lp = lforw(lp)) == curbp->b_headp) {
1.50 lum 323: (void)dobeep_msg("End of buffer");
1.31 lum 324: return(TRUE);
1.34 lum 325: }
1.1 deraadt 326: curwp->w_linep = lp;
1.30 kjell 327: curwp->w_rflag |= WFFULL;
1.31 lum 328:
1.1 deraadt 329: /* if in current window, don't move dot */
1.32 lum 330: for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp))
1.3 millert 331: if (lp == curwp->w_dotp)
1.18 db 332: return (TRUE);
1.31 lum 333:
1.27 kjell 334: /* Advance the dot the slow way, for line nos */
335: while (curwp->w_dotp != curwp->w_linep) {
336: curwp->w_dotp = lforw(curwp->w_dotp);
337: curwp->w_dotline++;
338: }
1.3 millert 339: curwp->w_doto = 0;
1.18 db 340: return (TRUE);
1.1 deraadt 341: }
342:
343: /*
344: * This command is like "forwpage",
345: * but it goes backwards. The "2", like above,
346: * is the overlap between the two windows. The
347: * value is from the ITS EMACS manual. The
348: * hard update is done because the top line in
349: * the window is zapped.
350: */
1.3 millert 351: int
1.12 vincent 352: backpage(int f, int n)
1.1 deraadt 353: {
1.33 lum 354: struct line *lp, *lp2;
1.1 deraadt 355:
356: if (!(f & FFARG)) {
1.3 millert 357: n = curwp->w_ntrows - 2; /* Default scroll. */
358: if (n <= 0) /* Don't blow up if the */
1.35 lum 359: return (backline(f, 1));/* window is tiny. */
1.1 deraadt 360: } else if (n < 0)
1.18 db 361: return (forwpage(f | FFRAND, -n));
1.33 lum 362:
363: lp = lp2 = curwp->w_linep;
364:
1.25 kjell 365: while (n-- && lback(lp) != curbp->b_headp) {
1.1 deraadt 366: lp = lback(lp);
1.34 lum 367: }
1.50 lum 368: if (lp == curwp->w_linep)
369: (void)dobeep_msg("Beginning of buffer");
370:
1.1 deraadt 371: curwp->w_linep = lp;
1.30 kjell 372: curwp->w_rflag |= WFFULL;
1.33 lum 373:
1.1 deraadt 374: /* if in current window, don't move dot */
1.25 kjell 375: for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp))
1.3 millert 376: if (lp == curwp->w_dotp)
1.18 db 377: return (TRUE);
1.33 lum 378:
379: lp2 = lforw(lp2);
380:
1.27 kjell 381: /* Move the dot the slow way, for line nos */
1.33 lum 382: while (curwp->w_dotp != lp2) {
1.36 lum 383: if (curwp->w_dotline <= curwp->w_ntrows)
1.45 florian 384: goto out;
1.27 kjell 385: curwp->w_dotp = lback(curwp->w_dotp);
386: curwp->w_dotline--;
387: }
1.45 florian 388: out:
1.1 deraadt 389: curwp->w_doto = 0;
1.18 db 390: return (TRUE);
1.1 deraadt 391: }
392:
1.3 millert 393: /*
394: * These functions are provided for compatibility with Gosling's Emacs. They
395: * are used to scroll the display up (or down) one line at a time.
1.1 deraadt 396: */
1.7 deraadt 397: int
1.12 vincent 398: forw1page(int f, int n)
1.1 deraadt 399: {
1.3 millert 400: if (!(f & FFARG)) {
401: n = 1;
1.1 deraadt 402: f = FFUNIV;
403: }
1.3 millert 404: forwpage(f | FFRAND, n);
1.18 db 405: return (TRUE);
1.1 deraadt 406: }
407:
1.7 deraadt 408: int
1.12 vincent 409: back1page(int f, int n)
1.1 deraadt 410: {
411: if (!(f & FFARG)) {
1.3 millert 412: n = 1;
1.1 deraadt 413: f = FFUNIV;
414: }
1.3 millert 415: backpage(f | FFRAND, n);
1.18 db 416: return (TRUE);
1.1 deraadt 417: }
418:
419: /*
420: * Page the other window. Check to make sure it exists, then
421: * nextwind, forwpage and restore window pointers.
422: */
1.3 millert 423: int
1.12 vincent 424: pagenext(int f, int n)
1.1 deraadt 425: {
1.21 deraadt 426: struct mgwin *wp;
1.1 deraadt 427:
1.50 lum 428: if (wheadp->w_wndp == NULL)
429: return(dobeep_msg("No other window"));
430:
1.1 deraadt 431: wp = curwp;
1.6 art 432: (void) nextwind(f, n);
433: (void) forwpage(f, n);
1.1 deraadt 434: curwp = wp;
435: curbp = wp->w_bufp;
1.18 db 436: return (TRUE);
1.1 deraadt 437: }
438:
439: /*
440: * Internal set mark routine, used by other functions (daveb).
441: */
1.6 art 442: void
1.12 vincent 443: isetmark(void)
1.1 deraadt 444: {
445: curwp->w_markp = curwp->w_dotp;
446: curwp->w_marko = curwp->w_doto;
1.23 kjell 447: curwp->w_markline = curwp->w_dotline;
1.1 deraadt 448: }
449:
450: /*
451: * Set the mark in the current window
452: * to the value of dot. A message is written to
453: * the echo line. (ewprintf knows about macros)
454: */
1.3 millert 455: int
1.12 vincent 456: setmark(int f, int n)
1.1 deraadt 457: {
458: isetmark();
459: ewprintf("Mark set");
1.29 kjell 460: return (TRUE);
461: }
462:
463: /* Clear the mark, if set. */
464: int
465: clearmark(int f, int n)
466: {
467: if (!curwp->w_markp)
468: return (FALSE);
469:
470: curwp->w_markp = NULL;
471: curwp->w_marko = 0;
472: curwp->w_markline = 0;
473:
1.18 db 474: return (TRUE);
1.1 deraadt 475: }
476:
477: /*
478: * Swap the values of "dot" and "mark" in
479: * the current window. This is pretty easy, because
480: * all of the hard work gets done by the standard routine
481: * that moves the mark about. The only possible
482: * error is "no mark".
483: */
1.3 millert 484: int
1.12 vincent 485: swapmark(int f, int n)
1.1 deraadt 486: {
1.21 deraadt 487: struct line *odotp;
1.23 kjell 488: int odoto, odotline;
1.1 deraadt 489:
1.50 lum 490: if (curwp->w_markp == NULL)
491: return(dobeep_msg("No mark in this window"));
492:
1.1 deraadt 493: odotp = curwp->w_dotp;
494: odoto = curwp->w_doto;
1.23 kjell 495: odotline = curwp->w_dotline;
1.3 millert 496: curwp->w_dotp = curwp->w_markp;
497: curwp->w_doto = curwp->w_marko;
1.23 kjell 498: curwp->w_dotline = curwp->w_markline;
1.1 deraadt 499: curwp->w_markp = odotp;
500: curwp->w_marko = odoto;
1.23 kjell 501: curwp->w_markline = odotline;
1.30 kjell 502: curwp->w_rflag |= WFMOVE;
1.18 db 503: return (TRUE);
1.1 deraadt 504: }
505:
506: /*
507: * Go to a specific line, mostly for
508: * looking up errors in C programs, which give the
509: * error a line number. If an argument is present, then
510: * it is the line number, else prompt for a line number
511: * to use.
512: */
1.3 millert 513: int
1.12 vincent 514: gotoline(int f, int n)
1.1 deraadt 515: {
1.23 kjell 516: char buf[32], *bufp;
517: const char *err;
1.1 deraadt 518:
519: if (!(f & FFARG)) {
1.20 kjell 520: if ((bufp = eread("Goto line: ", buf, sizeof(buf),
521: EFNUL | EFNEW | EFCR)) == NULL)
1.24 kjell 522: return (ABORT);
523: if (bufp[0] == '\0')
1.18 db 524: return (ABORT);
1.23 kjell 525: n = (int)strtonum(buf, INT_MIN, INT_MAX, &err);
1.50 lum 526: if (err)
1.51 op 527: return(dobeep_msgs("Line number", err));
1.1 deraadt 528: }
1.38 jasper 529: return(setlineno(n));
530: }
531:
532: /*
533: * Set the line number and switch to it.
534: */
535: int
536: setlineno(int n)
537: {
538: struct line *clp;
539:
1.5 millert 540: if (n >= 0) {
1.23 kjell 541: if (n == 0)
542: n++;
543: curwp->w_dotline = n;
1.25 kjell 544: clp = lforw(curbp->b_headp); /* "clp" is first line */
1.1 deraadt 545: while (--n > 0) {
1.25 kjell 546: if (lforw(clp) == curbp->b_headp) {
1.23 kjell 547: curwp->w_dotline = curwp->w_bufp->b_lines;
1.3 millert 548: break;
1.23 kjell 549: }
1.1 deraadt 550: clp = lforw(clp);
551: }
552: } else {
1.23 kjell 553: curwp->w_dotline = curwp->w_bufp->b_lines + n;
1.25 kjell 554: clp = lback(curbp->b_headp); /* "clp" is last line */
1.1 deraadt 555: while (n < 0) {
1.25 kjell 556: if (lback(clp) == curbp->b_headp) {
1.23 kjell 557: curwp->w_dotline = 1;
1.3 millert 558: break;
1.23 kjell 559: }
1.1 deraadt 560: clp = lback(clp);
561: n++;
562: }
563: }
564: curwp->w_dotp = clp;
565: curwp->w_doto = 0;
1.30 kjell 566: curwp->w_rflag |= WFMOVE;
1.18 db 567: return (TRUE);
1.1 deraadt 568: }