1 /*        $NetBSD: cr_put.c,v 1.40 2022/10/19 06:09:27 blymn 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 #include <limits.h>
34 #include <stdlib.h>
35 #ifndef lint
36 #if 0
37 static char sccsid[] = "@(#)cr_put.c    8.3 (Berkeley) 5/4/94";
38 #else
39 __RCSID("$NetBSD: cr_put.c,v 1.40 2022/10/19 06:09:27 blymn Exp $");
40 #endif
41 #endif                                  /* not lint */
42 
43 #include <string.h>
44 
45 #include "curses.h"
46 #include "curses_private.h"
47 
48 #define   HARDTABS  8
49 
50 /*
51  * Terminal driving and line formatting routines.  Basic motion optimizations
52  * are done here as well as formatting lines (printing of control characters,
53  * line numbering and the like).
54  */
55 
56 /* Stub function for the users. */
57 int
mvcur(int ly,int lx,int y,int x)58 mvcur(int ly, int lx, int y, int x)
59 {
60           return (__mvcur(ly, lx, y, x, 0));
61 }
62 
63 static void fgoto(int);
64 static int plod(int, int);
65 static int plodput(int);
66 static int tabcol(int, int);
67 
68 static int outcol, outline, destcol, destline;
69 
70 /*
71  * Sync the position of the output cursor.  Most work here is rounding for
72  * terminal boundaries getting the column position implied by wraparound or
73  * the lack thereof and rolling up the screen to get destline on the screen.
74  */
75 int
__mvcur(int ly,int lx,int y,int x,int in_refresh)76 __mvcur(int ly, int lx, int y, int x, int in_refresh)
77 {
78           __CTRACE(__CTRACE_OUTPUT,
79               "mvcur: moving cursor from (%d, %d) to (%d, %d) in refresh %d\n",
80               ly, lx, y, x, in_refresh);
81           destcol = x;
82           destline = y;
83           outcol = lx;
84           outline = ly;
85           fgoto(in_refresh);
86           return (OK);
87 }
88 
89 static void
fgoto(int in_refresh)90 fgoto(int in_refresh)
91 {
92           int        c, l;
93           char      *cgp;
94 
95           __CTRACE(__CTRACE_OUTPUT, "fgoto: in_refresh=%d\n", in_refresh);
96           __CTRACE(__CTRACE_OUTPUT,
97               "fgoto: outcol=%d, outline=%d, destcol=%d, destline=%d\n",
98               outcol, outline, destcol, destline);
99           if (destcol >= COLS) {
100                     destline += destcol / COLS;
101                     destcol %= COLS;
102           }
103           if (outcol >= COLS) {
104                     l = (outcol + 1) / COLS;
105                     outline += l;
106                     outcol %= COLS;
107                     if (auto_left_margin == 0) {
108                               while (l > 0) {
109                                         if (__pfast) {
110                                                   if (carriage_return)
111                                                             tputs(carriage_return,
112                                                                       0, __cputchar);
113                                                   else
114                                                             __cputchar('\r');
115                                         }
116                                         if (cursor_down)
117                                                   tputs(cursor_down, 0, __cputchar);
118                                         else
119                                                   __cputchar('\n');
120                                         l--;
121                               }
122                               outcol = 0;
123                     }
124                     if (outline > LINES - 1) {
125                               destline -= outline - (LINES - 1);
126                               outline = LINES - 1;
127                     }
128           }
129           if (destline >= LINES) {
130                     l = destline;
131                     destline = LINES - 1;
132                     if (outline < LINES - 1) {
133                               c = destcol;
134                               if (__pfast == 0 && !cursor_address)
135                                         destcol = 0;
136                               fgoto(in_refresh);
137                               destcol = c;
138                     }
139                     while (l >= LINES) {
140                               /* The following linefeed (or simulation thereof) is
141                                * supposed to scroll up the screen, since we are on
142                                * the bottom line.  We make the assumption that
143                                * linefeed will scroll.  If ns is in the capability
144                                * list this won't work.  We should probably have an
145                                * sc capability but sf will generally take the place
146                                * if it works.
147                                *
148                                * Superbee glitch: in the middle of the screen have to
149                                * use esc B (down) because linefeed screws up in
150                                * "Efficient Paging" (what a joke) mode (which is
151                                * essential in some SB's because CRLF mode puts
152                                * garbage in at end of memory), but you must use
153                                * linefeed to scroll since down arrow won't go past
154                                * memory end. I turned this off after receiving Paul
155                                * Eggert's Superbee description which wins better. */
156                               if (cursor_down /* && !__tc_xb */ && __pfast)
157                                         tputs(cursor_down, 0, __cputchar);
158                               else
159                                         __cputchar('\n');
160                               l--;
161                               if (__pfast == 0)
162                                         outcol = 0;
163                     }
164           }
165           if (destline < outline && !(cursor_address || cursor_up))
166                     destline = outline;
167 
168           if (cursor_address &&
169               (cgp = tiparm(cursor_address, destline, destcol)))
170           {
171                     /*
172                      * Need this condition due to inconsistent behavior
173                      * of backspace on the last column.
174                      */
175                     __CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp);
176                     if (outcol != COLS - 1 &&
177                         plod((int) strlen(cgp), in_refresh) > 0)
178                               plod(0, in_refresh);
179                     else
180                               tputs(cgp, 0, __cputchar);
181           } else
182                     plod(0, in_refresh);
183           outline = destline;
184           outcol = destcol;
185 }
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  */
194 
195 static int plodcnt, plodflg;
196 #ifdef HAVE_WCHAR
197 static char s[MB_LEN_MAX];
198 #endif
199 
200 static int
plodput(int c)201 plodput(int c)
202 {
203           if (plodflg) {
204                     int cw;
205 
206 #ifdef HAVE_WCHAR
207                     cw = wctomb(s, c);
208                     if (cw < 0)
209                               cw = 1;
210 #else
211                     cw = 1;
212 #endif /* HAVE_WCHAR */
213 
214                     plodcnt -= cw;
215           } else
216                     __cputchar(c);
217           return 0;
218 }
219 
220 static int
plod(int cnt,int in_refresh)221 plod(int cnt, int in_refresh)
222 {
223           int        i, j, k, soutcol, soutline;
224           __LDATA  *csp;
225 
226           __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n",
227               cnt, in_refresh);
228           __CTRACE(__CTRACE_OUTPUT,
229               "plod: plodding from col %d, row %d to col %d, row %d\n",
230               outcol, outline, destcol, destline);
231           plodcnt = plodflg = cnt;
232           soutcol = outcol;
233           soutline = outline;
234 
235           /*
236            * Consider homing and moving down/right from there, vs. moving
237            * directly with local motions to the right spot.
238            */
239           if (cursor_home) {
240                     /*
241                      * i is the cost to home and tab/space to the right to get to
242                      * the proper column.  This assumes nd space costs 1 char.  So
243                      * i + destcol is cost of motion with home.
244                      */
245                     if (__GT)
246                               i = (destcol / HARDTABS) + (destcol % HARDTABS);
247                     else
248                               i = destcol;
249 
250                     /* j is cost to move locally without homing. */
251                     if (destcol >= outcol) {      /* if motion is to the right */
252                               j = destcol / HARDTABS - outcol / HARDTABS;
253                               if (__GT && j)
254                                         j += destcol % HARDTABS;
255                               else
256                                         j = destcol - outcol;
257                     } else
258                               /* leftward motion only works if we can backspace. */
259                               if (outcol - destcol <= i)
260                                         /* Cheaper to backspace. */
261                                         i = j = outcol - destcol;
262                               else
263                                         /* Impossibly expensive. */
264                                         j = i + 1;
265 
266                     /* k is the absolute value of vertical distance. */
267                     k = outline - destline;
268                     if (k < 0)
269                               k = -k;
270                     j += k;
271 
272                     /* Decision.  We may not have a choice if no up. */
273                     if (i + destline < j || (!cursor_up && destline < outline)) {
274                               /*
275                                * Cheaper to home.  Do it now and pretend it's a
276                                * regular local motion.
277                                */
278                               tputs(cursor_home, 0, plodput);
279                               outcol = outline = 0;
280                     } else
281                               if (cursor_to_ll) {
282                                         /*
283                                          * Quickly consider homing down and moving from
284                                          * there.  Assume cost of ll is 2.
285                                          */
286                                         k = (LINES - 1) - destline;
287                                         if (i + k + 2 < j && (k <= 0 || cursor_up)) {
288                                                   tputs(cursor_to_ll, 0, plodput);
289                                                   outcol = 0;
290                                                   outline = LINES - 1;
291                                         }
292                               }
293           } else
294                     /* No home and no up means it's impossible. */
295                     if (!cursor_up && destline < outline)
296                               return (-1);
297           if (__GT)
298                     i = destcol % HARDTABS + destcol / HARDTABS;
299           else
300                     i = destcol;
301 #ifdef notdef
302           if (back_tab && outcol > destcol &&
303               (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) {
304                     j *= (k = strlen(back_tab));
305                     if ((k += (destcol & 7)) > 4)
306                               j += 8 - (destcol & 7);
307                     else
308                               j += k;
309           } else
310 #endif
311                     j = outcol - destcol;
312 
313           /*
314            * If we will later need a \n which will turn into a \r\n by the
315            * system or the terminal, then don't bother to try to \r.
316            */
317           if ((__NONL || !__pfast) && outline < destline)
318                     goto dontcr;
319 
320           /*
321            * If the terminal will do a \r\n and there isn't room for it, then
322            * we can't afford a \r.
323            */
324           if (!carriage_return && outline >= destline)
325                     goto dontcr;
326 
327           /*
328            * If it will be cheaper, or if we can't back up, then send a return
329            * preliminarily.
330            */
331           if (j > i + 1 || outcol > destcol) {
332                     /*
333                      * BUG: this doesn't take the (possibly long) length of cr
334                      * into account.
335                      */
336                     if (carriage_return)
337                               tputs(carriage_return, 0, plodput);
338                     else
339                               plodput('\r');
340                     if (!carriage_return) {
341                               if (cursor_down)
342                                         tputs(cursor_down, 0, plodput);
343                               else
344                                         plodput('\n');
345                               outline++;
346                     }
347 
348                     outcol = 0;
349           }
350 dontcr:while (outline < destline) {
351                     outline++;
352                     if (cursor_down)
353                               tputs(cursor_down, 0, plodput);
354                     else
355                               plodput('\n');
356                     if (plodcnt < 0)
357                               goto out;
358                     /*
359                      * If the terminal does a CR with NL or we are in
360                      * a mode where a \n will result in an implicit \r
361                      * then adjust the outcol to match iff we actually
362                      * emitted said \n.
363                      */
364                     if ((__NONL || __pfast == 0) &&
365                         (!cursor_down || (*cursor_down == '\n')))
366                               outcol = 0;
367           }
368 #ifdef notdef
369           if (back_tab)
370                     k = (int) strlen(back_tab);
371 #endif
372           while (outcol > destcol) {
373                     if (plodcnt < 0)
374                               goto out;
375 #ifdef notdef
376                     if (back_tab && outcol - destcol > k + 4) {
377                               tputs(back_tab, 0, plodput);
378                               outcol--;
379                               outcol &= ~7;
380                               continue;
381                     }
382 #endif
383                     outcol--;
384                     if (cursor_left)
385                               tputs(cursor_left, 0, plodput);
386                     else
387                               plodput('\b');
388           }
389           while (outline > destline) {
390                     outline--;
391                     tputs(cursor_up, 0, plodput);
392                     if (plodcnt < 0)
393                               goto out;
394           }
395           if (__GT && destcol - outcol > 1) {
396                     for (;;) {
397                               i = tabcol(outcol, HARDTABS);
398                               if (i > destcol)
399                                         break;
400                               if (tab)
401                                         tputs(tab, 0, plodput);
402                               else
403                                         plodput('\t');
404                               outcol = i;
405                     }
406                     if (destcol - outcol > 4 && i < COLS) {
407                               if (tab)
408                                         tputs(tab, 0, plodput);
409                               else
410                                         plodput('\t');
411                               outcol = i;
412                               while (outcol > destcol) {
413                                         outcol--;
414                                         if (cursor_left)
415                                                   tputs(cursor_left, 0, plodput);
416                                         else
417                                                   plodput('\b');
418                               }
419                     }
420           }
421 
422 #ifdef HAVE_WCHAR
423           /*
424            * If destcol is halfway through a multicolumn
425            * wide char, we have no chance of plodding.
426            */
427           if (curscr->alines[outline]->line[outcol].cflags & CA_CONTINUATION) {
428                     plodcnt = -1;
429                     goto out;
430           }
431 #endif /* HAVE_WCHAR */
432 
433           while (outcol < destcol) {
434                     int chw;
435 
436                     csp = &curscr->alines[outline]->line[outcol];
437 #ifdef HAVE_WCHAR
438                     chw = csp->wcols;
439 #else
440                     chw = 1;
441 #endif /* HAVE_WCHAR */
442 
443 
444                     /*
445                      * Move one char to the right.  We don't use nd space because
446                      * it's better to just print the char we are moving over.
447                      */
448                     if (in_refresh)
449                               if (plodflg)        /* Avoid a complex calculation. */
450                                         plodcnt--;
451                               else {
452 #ifndef HAVE_WCHAR
453                                         i = csp->ch & __CHARTEXT;
454                                         if (csp->attr == curscr->wattr)
455                                                   __cputchar(i);
456 #else
457                                         if ((csp->attr & WA_ATTRIBUTES)
458                                             == curscr->wattr) {
459                                                   if (csp->cflags & CA_CONTINUATION)
460                                                             goto nondes;
461 
462                                                   if (csp->wcols >= 1) {
463                                                             __cputwchar(csp->ch);
464                                                             __cursesi_putnsp(csp->nsp,
465                                                                                 outline,
466                                                                                 outcol);
467                                                             __CTRACE(__CTRACE_OUTPUT,
468                                                                 "plod: (%d,%d)wcols(%d), "
469                                                                 "putwchar(%x)\n",
470                                                                 outline, outcol,
471                                                                 csp->wcols, csp->ch);
472                                                   }
473 
474                                                   if (csp->wcols == 0)
475                                                             break;
476                                         }
477 #endif /* HAVE_WCHAR */
478                                         else
479                                                   goto nondes;
480                               }
481                     else {
482                     nondes:   if (cursor_right)
483                                         tputs(cursor_right, 0, plodput);
484                               else
485                                         plodput(' ');
486                     }
487 
488                     outcol += chw;
489                     if (plodcnt < 0)
490                               goto out;
491           }
492 
493 out:      if (plodflg) {
494                     outcol = soutcol;
495                     outline = soutline;
496           }
497           __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt);
498           return plodcnt;
499 }
500 
501 /*
502  * Return the column number that results from being in column col and
503  * hitting a tab, where tabs are set every ts columns.  Work right for
504  * the case where col > COLS, even if ts does not divide COLS.
505  */
506 static int
tabcol(int col,int ts)507 tabcol(int col, int ts)
508 {
509           int        offset;
510 
511           if (col >= COLS) {
512                     offset = COLS * (col / COLS);
513                     col -= offset;
514           } else
515                     offset = 0;
516           return (col + ts - (col % ts) + offset);
517 }
518