1 /* $NetBSD: cr_put.c,v 1.31 2011/10/03 12:32:15 roy Exp $ */
2
3 /*
4 * Copyright (c) 1981, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)cr_put.c 8.3 (Berkeley) 5/4/94";
36 #else
37 __RCSID("$NetBSD: cr_put.c,v 1.31 2011/10/03 12:32:15 roy Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include <string.h>
42
43 #include "curses.h"
44 #include "curses_private.h"
45
46 #define HARDTABS 8
47
48 /*
49 * Terminal driving and line formatting routines. Basic motion optimizations
50 * are done here as well as formatting lines (printing of control characters,
51 * line numbering and the like).
52 */
53
54 /* Stub function for the users. */
55 int
mvcur(int ly,int lx,int y,int x)56 mvcur(int ly, int lx, int y, int x)
57 {
58 return (__mvcur(ly, lx, y, x, 0));
59 }
60
61 static void fgoto __P((int));
62 static int plod __P((int, int));
63 static int plodput __P((int));
64 static int tabcol __P((int, int));
65
66 static int outcol, outline, destcol, destline;
67
68 /*
69 * Sync the position of the output cursor. Most work here is rounding for
70 * terminal boundaries getting the column position implied by wraparound or
71 * the lack thereof and rolling up the screen to get destline on the screen.
72 */
73 int
__mvcur(int ly,int lx,int y,int x,int in_refresh)74 __mvcur(int ly, int lx, int y, int x, int in_refresh)
75 {
76 #ifdef DEBUG
77 __CTRACE(__CTRACE_OUTPUT,
78 "mvcur: moving cursor from (%d, %d) to (%d, %d)\n", ly, lx, y, x);
79 #endif
80 destcol = x;
81 destline = y;
82 outcol = lx;
83 outline = ly;
84 fgoto(in_refresh);
85 return (OK);
86 }
87
88 static void
fgoto(in_refresh)89 fgoto(in_refresh)
90 int in_refresh;
91 {
92 int c, l;
93 char *cgp;
94
95 #ifdef DEBUG
96 __CTRACE(__CTRACE_OUTPUT, "fgoto: in_refresh=%d\n", in_refresh);
97 #endif /* DEBUG */
98 if (destcol >= COLS) {
99 destline += destcol / COLS;
100 destcol %= COLS;
101 }
102 if (outcol >= COLS) {
103 l = (outcol + 1) / COLS;
104 outline += l;
105 outcol %= COLS;
106 if (auto_left_margin == 0) {
107 while (l > 0) {
108 if (__pfast) {
109 if (carriage_return)
110 tputs(carriage_return,
111 0, __cputchar);
112 else
113 __cputchar('\r');
114 }
115 if (cursor_down)
116 tputs(cursor_down, 0, __cputchar);
117 else
118 __cputchar('\n');
119 l--;
120 }
121 outcol = 0;
122 }
123 if (outline > LINES - 1) {
124 destline -= outline - (LINES - 1);
125 outline = LINES - 1;
126 }
127 }
128 if (destline >= LINES) {
129 l = destline;
130 destline = LINES - 1;
131 if (outline < LINES - 1) {
132 c = destcol;
133 if (__pfast == 0 && !cursor_address)
134 destcol = 0;
135 fgoto(in_refresh);
136 destcol = c;
137 }
138 while (l >= LINES) {
139 /* The following linefeed (or simulation thereof) is
140 * supposed to scroll up the screen, since we are on
141 * the bottom line. We make the assumption that
142 * linefeed will scroll. If ns is in the capability
143 * list this won't work. We should probably have an
144 * sc capability but sf will generally take the place
145 * if it works.
146 *
147 * Superbee glitch: in the middle of the screen have to
148 * use esc B (down) because linefeed screws up in
149 * "Efficient Paging" (what a joke) mode (which is
150 * essential in some SB's because CRLF mode puts
151 * garbage in at end of memory), but you must use
152 * linefeed to scroll since down arrow won't go past
153 * memory end. I turned this off after recieving Paul
154 * Eggert's Superbee description which wins better. */
155 if (cursor_down /* && !__tc_xb */ && __pfast)
156 tputs(cursor_down, 0, __cputchar);
157 else
158 __cputchar('\n');
159 l--;
160 if (__pfast == 0)
161 outcol = 0;
162 }
163 }
164 if (destline < outline && !(cursor_address || cursor_up))
165 destline = outline;
166
167 if (cursor_address &&
168 (cgp = tiparm(cursor_address, destline, destcol)))
169 {
170 /*
171 * Need this condition due to inconsistent behavior
172 * of backspace on the last column.
173 */
174 #ifdef DEBUG
175 __CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp);
176 #endif /* DEBUG */
177 if (outcol != COLS - 1 &&
178 plod((int) strlen(cgp), in_refresh) > 0)
179 plod(0, in_refresh);
180 else
181 tputs(cgp, 0, __cputchar);
182 } else
183 plod(0, in_refresh);
184 outline = destline;
185 outcol = destcol;
186 }
187 /*
188 * Move (slowly) to destination.
189 * Hard thing here is using home cursor on really deficient terminals.
190 * Otherwise just use cursor motions, hacking use of tabs and overtabbing
191 * and backspace.
192 *
193 * XXX this needs to be revisited for wide characters since we may output
194 * XXX more than one byte for a character.
195 */
196
197 static int plodcnt, plodflg;
198
199 static int
plodput(c)200 plodput(c)
201 int c;
202 {
203 if (plodflg)
204 --plodcnt;
205 else
206 __cputchar(c);
207 return (0);
208 }
209
210 static int
plod(cnt,in_refresh)211 plod(cnt, in_refresh)
212 int cnt, in_refresh;
213 {
214 int i, j, k, soutcol, soutline;
215
216 #ifdef DEBUG
217 __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n",
218 cnt, in_refresh);
219 #endif /* DEBUG */
220 plodcnt = plodflg = cnt;
221 soutcol = outcol;
222 soutline = outline;
223
224 /*
225 * Consider homing and moving down/right from there, vs. moving
226 * directly with local motions to the right spot.
227 */
228 if (cursor_home) {
229 /*
230 * i is the cost to home and tab/space to the right to get to
231 * the proper column. This assumes nd space costs 1 char. So
232 * i + destcol is cost of motion with home.
233 */
234 if (__GT)
235 i = (destcol / HARDTABS) + (destcol % HARDTABS);
236 else
237 i = destcol;
238
239 /* j is cost to move locally without homing. */
240 if (destcol >= outcol) { /* if motion is to the right */
241 j = destcol / HARDTABS - outcol / HARDTABS;
242 if (__GT && j)
243 j += destcol % HARDTABS;
244 else
245 j = destcol - outcol;
246 } else
247 /* leftward motion only works if we can backspace. */
248 if (outcol - destcol <= i)
249 /* Cheaper to backspace. */
250 i = j = outcol - destcol;
251 else
252 /* Impossibly expensive. */
253 j = i + 1;
254
255 /* k is the absolute value of vertical distance. */
256 k = outline - destline;
257 if (k < 0)
258 k = -k;
259 j += k;
260
261 /* Decision. We may not have a choice if no up. */
262 if (i + destline < j || (!cursor_up && destline < outline)) {
263 /*
264 * Cheaper to home. Do it now and pretend it's a
265 * regular local motion.
266 */
267 tputs(cursor_home, 0, plodput);
268 outcol = outline = 0;
269 } else
270 if (cursor_to_ll) {
271 /*
272 * Quickly consider homing down and moving from
273 * there. Assume cost of ll is 2.
274 */
275 k = (LINES - 1) - destline;
276 if (i + k + 2 < j && (k <= 0 || cursor_up)) {
277 tputs(cursor_to_ll, 0, plodput);
278 outcol = 0;
279 outline = LINES - 1;
280 }
281 }
282 } else
283 /* No home and no up means it's impossible. */
284 if (!cursor_up && destline < outline)
285 return (-1);
286 if (__GT)
287 i = destcol % HARDTABS + destcol / HARDTABS;
288 else
289 i = destcol;
290 #ifdef notdef
291 if (back_tab && outcol > destcol &&
292 (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) {
293 j *= (k = strlen(back_tab));
294 if ((k += (destcol & 7)) > 4)
295 j += 8 - (destcol & 7);
296 else
297 j += k;
298 } else
299 #endif
300 j = outcol - destcol;
301
302 /*
303 * If we will later need a \n which will turn into a \r\n by the
304 * system or the terminal, then don't bother to try to \r.
305 */
306 if ((__NONL || !__pfast) && outline < destline)
307 goto dontcr;
308
309 /*
310 * If the terminal will do a \r\n and there isn't room for it, then
311 * we can't afford a \r.
312 */
313 if (!carriage_return && outline >= destline)
314 goto dontcr;
315
316 /*
317 * If it will be cheaper, or if we can't back up, then send a return
318 * preliminarily.
319 */
320 if (j > i + 1 || outcol > destcol) {
321 /*
322 * BUG: this doesn't take the (possibly long) length of cr
323 * into account.
324 */
325 if (carriage_return)
326 tputs(carriage_return, 0, plodput);
327 else
328 plodput('\r');
329 if (!carriage_return) {
330 if (cursor_down)
331 tputs(cursor_down, 0, plodput);
332 else
333 plodput('\n');
334 outline++;
335 }
336 outcol = 0;
337 }
338 dontcr:while (outline < destline) {
339 outline++;
340 if (cursor_down)
341 tputs(cursor_down, 0, plodput);
342 else
343 plodput('\n');
344 if (plodcnt < 0)
345 goto out;
346 if (__NONL || __pfast == 0)
347 outcol = 0;
348 }
349 if (back_tab)
350 k = (int) strlen(back_tab);
351 while (outcol > destcol) {
352 if (plodcnt < 0)
353 goto out;
354 #ifdef notdef
355 if (back_tab && outcol - destcol > k + 4) {
356 tputs(back_tab, 0, plodput);
357 outcol--;
358 outcol &= ~7;
359 continue;
360 }
361 #endif
362 outcol--;
363 if (cursor_left)
364 tputs(cursor_left, 0, plodput);
365 else
366 plodput('\b');
367 }
368 while (outline > destline) {
369 outline--;
370 tputs(cursor_up, 0, plodput);
371 if (plodcnt < 0)
372 goto out;
373 }
374 if (__GT && destcol - outcol > 1) {
375 for (;;) {
376 i = tabcol(outcol, HARDTABS);
377 if (i > destcol)
378 break;
379 if (tab)
380 tputs(tab, 0, plodput);
381 else
382 plodput('\t');
383 outcol = i;
384 }
385 if (destcol - outcol > 4 && i < COLS) {
386 if (tab)
387 tputs(tab, 0, plodput);
388 else
389 plodput('\t');
390 outcol = i;
391 while (outcol > destcol) {
392 outcol--;
393 if (cursor_left)
394 tputs(cursor_left, 0, plodput);
395 else
396 plodput('\b');
397 }
398 }
399 }
400 while (outcol < destcol) {
401 /*
402 * Move one char to the right. We don't use nd space because
403 * it's better to just print the char we are moving over.
404 */
405 if (in_refresh)
406 if (plodflg) /* Avoid a complex calculation. */
407 plodcnt--;
408 else {
409 #ifndef HAVE_WCHAR
410 i = curscr->alines[outline]->line[outcol].ch
411 & __CHARTEXT;
412 if (curscr->alines[outline]->line[outcol].attr
413 == curscr->wattr)
414 __cputchar(i);
415 #else
416 if ((curscr->alines[outline]->line[outcol].attr
417 & WA_ATTRIBUTES)
418 == curscr->wattr) {
419 switch (WCOL(curscr->alines[outline]->line[outcol])) {
420 case 1:
421 __cputwchar(curscr->alines[outline]->line[outcol].ch);
422 __cursesi_putnsp(curscr->alines[outline]->line[outcol].nsp,
423 outline,
424 outcol);
425 #ifdef DEBUG
426 __CTRACE(__CTRACE_OUTPUT,
427 "plod: (%d,%d)WCOL(%d), "
428 "putwchar(%x)\n",
429 outline, outcol,
430 WCOL(curscr->alines[outline]->line[outcol]),
431 curscr->alines[outline]->line[outcol].ch);
432 #endif /* DEBUG */
433 /*FALLTHROUGH*/
434 case 0:
435 break;
436 default:
437 goto nondes;
438 }
439 }
440 #endif /* HAVE_WCHAR */
441 else
442 goto nondes;
443 }
444 else
445 nondes: if (cursor_right)
446 tputs(cursor_right, 0, plodput);
447 else
448 plodput(' ');
449 outcol++;
450 if (plodcnt < 0)
451 goto out;
452 }
453
454 out: if (plodflg) {
455 outcol = soutcol;
456 outline = soutline;
457 }
458 #ifdef DEBUG
459 __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt);
460 #endif /* DEBUG */
461 return (plodcnt);
462 }
463 /*
464 * Return the column number that results from being in column col and
465 * hitting a tab, where tabs are set every ts columns. Work right for
466 * the case where col > COLS, even if ts does not divide COLS.
467 */
468 static int
tabcol(col,ts)469 tabcol(col, ts)
470 int col, ts;
471 {
472 int offset;
473
474 if (col >= COLS) {
475 offset = COLS * (col / COLS);
476 col -= offset;
477 } else
478 offset = 0;
479 return (col + ts - (col % ts) + offset);
480 }
481