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