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