1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1995, by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*
28  * mvcur.c
29  *
30  * XCurses Library
31  *
32  * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved.
33  *
34  */
35 
36 #ifdef M_RCSID
37 #ifndef lint
38 static char rcsID[] = "$Header: /rd/src/libc/xcurses/rcs/mvcur.c 1.4 1995/06/15 18:56:03 ant Exp $";
39 #endif
40 #endif
41 
42 #include <private.h>
43 #include <string.h>
44 #include <stdarg.h>
45 
46 #define VECTOR_SIZE		128	/* size of strategy buffer */
47 
48 /*
49  * #define
50  * Make_seq_best(s1, s2)
51  *
52  * Make_seq_best() swaps the values of the pointers if s1->cost > s2->cost.
53  */
54 #define Make_seq_best(s1, s2)		\
55 	if (s1->cost > s2->cost) {	\
56 	    struct Sequence* temp = s1; \
57 	    s1 = s2;			\
58 	    s2 = temp;			\
59 	}
60 
61 #define zero_seq(seq)		((seq)->end = (seq)->vec, (seq)->cost = 0)
62 
63 struct Sequence {
64 	int vec[VECTOR_SIZE];	/* vector of operations */
65 	int *end;		/* end of vector */
66 	int cost;		/* cost of vector */
67 };
68 
69 static bool relative;		/* set if we really know where we are */
70 
71 /*f
72  * Add sequence 2 to sequence 1.
73  */
74 STATIC void
add_seq(seq1,seq2)75 add_seq(seq1, seq2)
76 struct Sequence	*seq1, *seq2;
77 {
78 	if (seq1->cost >= __MOVE_INFINITY || seq2->cost >= __MOVE_INFINITY)
79 		seq1->cost = __MOVE_INFINITY;
80 	else {
81 		int* vptr = seq2->vec;
82 		while (vptr != seq2->end)
83 			*(seq1->end++) = *(vptr++);
84 		seq1->cost += seq2->cost;
85 	}
86 }
87 
88 /*f
89  * add_op() adds the operator op and the appropriate
90  * number of paramaters to seq.  It also increases the
91  * cost appropriately.
92  *
93  * If op takes no parameters then p0 is taken to be a count.
94  */
95 STATIC void
add_op(seq,op,p1,p2)96 add_op(seq, op, p1, p2)
97 struct Sequence *seq;
98 int op, p1, p2;
99 {
100 	*(seq->end++) = op;
101 	*(seq->end++) = p1;
102 	*(seq->end++) = p2;
103 
104 	if (cur_term->_move[op]._seq == (char *) 0) {
105 		seq->cost = __MOVE_INFINITY;
106 	} else if (op < __MOVE_MAX_RELATIVE) {
107 		/* No parameters, total is cost * p1. */
108 		seq->cost += cur_term->_move[op]._cost * p1;
109 	} else {
110 		/* Cursor motion using parameters have fixed cost. */
111 		seq->cost = cur_term->_move[op]._cost;
112 	}
113 }
114 
115 /*f
116  * row() adds the best sequence for moving the cursor from orow
117  * to nrow to seq.
118  *
119  * row() considers row_address, parm_up/down_cursor and cursor_up/down.
120  */
121 STATIC void
row(outseq,orow,nrow)122 row(outseq, orow, nrow)
123 struct Sequence	*outseq;
124 int orow, nrow;
125 {
126 	struct Sequence	seqA, seqB;
127 	struct Sequence* best = &seqA;
128 	struct Sequence* try = &seqB;
129 	int parm_cursor, one_step, dist;
130 
131 	if (nrow == orow)
132 		return;
133 
134 	if (nrow < orow) {
135 		parm_cursor = __MOVE_N_UP;
136 		one_step = __MOVE_UP;
137 		dist = orow - nrow;
138 	} else {
139 		parm_cursor = __MOVE_N_DOWN;
140 		one_step = __MOVE_DOWN;
141 		dist = nrow - orow;
142 	}
143 
144 	/* try out direct row addressing */
145 	zero_seq(best);
146 	add_op(best, __MOVE_ROW, nrow, 0);
147 
148 	/* try out paramaterized up or down motion */
149 	zero_seq(try);
150 	add_op(try, parm_cursor, dist, 0);
151 	Make_seq_best(best, try);
152 
153 	/* try getting there one step at a time... */
154 	zero_seq(try);
155 	add_op(try, one_step, dist, 0);
156 	Make_seq_best(best, try);
157 
158 	add_seq(outseq, best);
159 }
160 
161 /*
162  * Motion indexes used in simp_col().
163  */
164 typedef struct {
165 	int _tab;	/* Tab index. */
166 	int _one;	/* Single-step index, same direction as tab. */
167 	int _opp;	/* Single-step index, opposite direction to tab. */
168 } t_steps;
169 
170 /*f
171  * simp_col(outseq, oldcol, newcol)
172  *
173  * simp_col() adds the best simple sequence for getting from oldcol
174  * to newcol to outseq. simp_col() considers (back_)tab and
175  * cursor_left/right.
176  */
177 STATIC void
simp_col(outseq,oc,nc)178 simp_col(outseq, oc, nc)
179 struct Sequence	*outseq;
180 int oc, nc;
181 {
182 	t_steps *dir;
183 	int dist, tabs, tabstop;
184 	struct Sequence	seqA, seqB, *best, *try;
185 	static t_steps right = { __MOVE_TAB, __MOVE_RIGHT, __MOVE_LEFT };
186 	static t_steps left = { __MOVE_BACK_TAB, __MOVE_LEFT, __MOVE_RIGHT };
187 
188 	if (oc == nc)
189 		return;
190 
191 	tabs = tabstop = dist = 0;
192 	best = &seqA;
193 	try = &seqB;
194 
195 	if (oc < nc) {
196 		dir = &right;
197 
198 		if (0 < init_tabs) {
199 			/* Tabstop preceeding nc. */
200 			tabstop = nc / init_tabs;
201 
202 			tabs = tabstop - oc / init_tabs;
203 			if (0 < tabs)
204 				/* Set oc to tabstop before nc : oc <= nc. */
205 				oc = tabstop * init_tabs;
206 
207 			/* Distance from next tabstop to nc in columns. */
208 			tabstop = init_tabs - nc % init_tabs;
209 		}
210 
211 		dist = nc - oc;
212 	} else {
213 		dir = &left;
214 
215 		if (0 < init_tabs) {
216 			/* Tabstop preceeding nc. */
217 			tabstop = nc / init_tabs;
218 
219 			tabs = (oc - 1) / init_tabs - tabstop;
220 			if (0 < tabs)
221 				/* Set oc to tabstop after nc : nc <= oc. */
222 				oc = (tabstop + 1) * init_tabs;
223 
224 			/* Distance from tabstop preceeding nc in columns. */
225 			tabstop = nc % init_tabs;
226 		}
227 
228 		dist = oc - nc;
229 	}
230 
231 	if (0 < tabs) {
232 		/* Tab as close as possible to nc. */
233 		zero_seq(best);
234 		add_op(best, dir->_tab, tabs, 0);
235 		add_seq(outseq, best);
236 
237 		/* If tabs alone get us there, then stop. */
238 		if (oc == nc)
239 			return;
240 	}
241 
242 	/* We're not exactly positioned yet.  Compare the worth of
243 	 * two sequences :
244 	 *   1.	single-step to location;
245 	 *   2.	over tab by one tabstop, then single-step back to location.
246 	 */
247 
248 	/* 1. Single-step to location. */
249 	zero_seq(best);
250 	add_op(best, dir->_one, dist, 0);
251 
252 	/* 2. Over tab by one tabstop, then single-step back to location. */
253 	if (0 < tabstop
254 	&& (nc < columns-init_tabs || auto_left_margin || eat_newline_glitch)) {
255 		zero_seq(try);
256 		add_op(try, dir->_tab, 1, 0);
257 
258 		/* vt100 terminals only wrap the cursor when a spacing
259 		 * character is written.  Control characters like <tab>
260 		 * will not cause a line wrap.  Adjust the number of
261 		 * columns to backup by to reflect the cursor having been
262 		 * placed in the last column.  See O'Reilly Termcap &
263 		 * Terminfo book.
264 		 */
265 		if (eat_newline_glitch && columns <= nc + tabstop)
266 			tabstop = columns - nc - 1;
267 
268 		add_op(try, dir->_opp, tabstop, 0);
269 		Make_seq_best(best, try);
270 	}
271 
272 	add_seq(outseq, best);
273 }
274 
275 /*f
276  * column() adds the best sequence for moving the cursor from oldcol
277  * to newcol to outseq.
278  *
279  * column() considers column_address, parm_left/right_cursor,
280  * simp_col() and carriage_return + simp_col().
281  */
282 STATIC void
column(outseq,ocol,ncol)283 column(outseq, ocol, ncol)
284 struct Sequence* outseq;
285 int ocol, ncol;
286 {
287 	struct Sequence	seqA, seqB;
288 	struct Sequence* best = &seqA;
289 	struct Sequence* try = &seqB;
290 	int parm_cursor, dist;
291 
292 	if (ncol == ocol)
293 		return;
294 
295 	/* try out direct column addressing */
296 	zero_seq(best);
297 	add_op(best, __MOVE_COLUMN, ncol, 0);
298 
299 	/* try out paramaterized left or right motion */
300 	if (ncol < ocol){
301 		parm_cursor = __MOVE_N_LEFT;
302 		dist = ocol - ncol;
303 	} else {
304 		parm_cursor = __MOVE_N_RIGHT;
305 		dist = ncol - ocol;
306 	}
307 	zero_seq(try);
308 	add_op(try, parm_cursor, dist, 0);
309 	Make_seq_best(best, try);
310 
311 	if (ncol < ocol || !relative) {
312 		/* try carriage_return then simp_col() */
313 		zero_seq(try);
314 		add_op(try, __MOVE_RETURN, 1, 0);
315 		simp_col(try, 0, ncol);
316 		Make_seq_best(best, try);
317 	}
318 
319 	/* try getting there by simpl_col() */
320 	zero_seq(try);
321 	simp_col(try, ocol, ncol);
322 	Make_seq_best(best, try);
323 
324 	add_seq(outseq, best);
325 }
326 
327 /*f
328  * send relevant terminal sequences to the screen
329  */
330 STATIC int
out_seq(seq,putout)331 out_seq(seq, putout)
332 struct Sequence	*seq;
333 int (*putout) ANSI((int));
334 {
335 	long p1, p2;
336 	int *ptr, op;
337 
338 	if (__MOVE_INFINITY <= seq->cost)
339 		return ERR;
340 
341 	for (ptr = seq->vec; ptr < seq->end; ) {
342 		op = *ptr++;
343 		p1 = *ptr++;
344 		p2 = *ptr++;
345 
346 		if (op < __MOVE_MAX_RELATIVE) {
347 			while (0 < p1--)
348 				(void) tputs(
349 					cur_term->_move[op]._seq, 1, putout
350 				);
351 		} else {
352 			(void) tputs(
353 				tparm(
354 					cur_term->_move[op]._seq, p1, p2,
355 					0, 0, 0, 0, 0, 0, 0
356 				), 1, putout
357 			);
358 		}
359 	}
360 
361 	return OK;
362 }
363 
364 /*f
365  * Low-level relative cursor motion.  __m_mvcur() looks for the optimal
366  * way to move the cursor from point A to point B.  If either of the
367  * coordinates for point A are -1 then only absolute addressing is used.
368  * If the coordinates are out-of-bounds then they are MODed into bounds.
369  *
370  * Since __m_mvcur() must perform output to various terminals, an API
371  * similar to tputs() and vidputs() was adopted.
372  */
373 int
374 __m_mvcur(oldrow, oldcol, newrow, newcol, putout)
375 int oldrow, oldcol, newrow, newcol, (*putout)(int);
376 {
377 	struct Sequence	seqA, seqB;	/* allocate work structures */
378 	struct Sequence col0seq;	/* sequence to get from col0 to nc */
379 	struct Sequence* best = &seqA;	/* best sequence so far */
380 	struct Sequence* try = &seqB;	/* next try */
381 
382 #ifdef M_CURSES_TRACE
383 	__m_trace(
384 		"__m_mvcur(%d, %d, %d, %d, %p)",
385 		oldrow, oldcol, newrow, newcol, putout
386 	);
387 #endif
388 
389 	newrow %= lines;
390 	newcol %= columns;
391 
392 	zero_seq(best);
393 
394 	/* try out direct cursor addressing */
395 	add_op(best, __MOVE_ROW_COLUMN, newrow, newcol);
396 
397 	if((relative = 0 <= oldrow && 0 <= oldcol)){
398 		oldrow %= lines;
399 		oldcol %= columns;
400 
401 		/* try out independent row/column addressing */
402 		zero_seq(try);
403 		row(try, oldrow, newrow);
404 		column(try, oldcol, newcol);
405 		Make_seq_best(best, try);
406 	}
407 	if (newcol < oldcol || !relative){
408 		zero_seq(&col0seq);
409 		column(&col0seq, 0, newcol);
410 		if (col0seq.cost < __MOVE_INFINITY) {
411 			/* try out homing and then row/column */
412 			if (newrow < oldrow || !relative) {
413 				zero_seq(try);
414 				add_op(try, __MOVE_HOME, 1, 0);
415 				row(try, 0, newrow);
416 				add_seq(try, &col0seq);
417 				Make_seq_best(best, try);
418 			}
419 
420 			/* try out homing to last line  and then row/column */
421 			if (newrow > oldrow || !relative) {
422 				zero_seq(try);
423 				add_op(try, __MOVE_LAST_LINE, 1, 0);
424 				row(try, lines - 1, newrow);
425 				add_seq(try, &col0seq);
426 				Make_seq_best(best, try);
427 			}
428 		}
429 	}
430 
431 	return __m_return_code("__m_mvcur", out_seq(best, putout));
432 }
433 
434 /*
435  * A do nothing output function for tputs().
436  */
437 STATIC int
nilout(ch)438 nilout(ch)
439 int ch;
440 {
441 	return ch;
442 }
443 
444 /*
445  * Initialize an entry in cur_term->_move[] with parameters p1 and p2.
446  * Note that some capabilities will ignore their parameters.
447  */
448 STATIC void
cost(cap,index,p1,p2)449 cost(cap, index, p1, p2)
450 char *cap;
451 int index, p1, p2;
452 {
453 	cur_term->_move[index]._seq = cap;
454 
455 	if (cap == (char *) 0 || cap[0] == '\0') {
456 		cur_term->_move[index]._cost = __MOVE_INFINITY;
457 	} else {
458 		cur_term->_move[index]._cost = tputs(
459 			tparm(cap, (long) p1, (long) p2, 0, 0, 0, 0, 0, 0, 0),
460 			1, nilout
461 		);
462 
463 		if (cap == cursor_down && strchr(cap, '\n') != (char *) 0)
464 			cur_term->_move[index]._cost = __MOVE_INFINITY;
465 	}
466 }
467 
468 void
__m_mvcur_cost()469 __m_mvcur_cost()
470 {
471 	/* Relative cursor motion that will be costed on a per
472 	 * character basis in __m_mvcur().
473 	 */
474 	cost(cursor_up, __MOVE_UP, 0, 0);
475 	cost(cursor_down, __MOVE_DOWN, 0, 0);
476 	cost(cursor_left, __MOVE_LEFT, 0, 0);
477 	cost(cursor_right, __MOVE_RIGHT, 0, 0);
478 	cost(dest_tabs_magic_smso ? (char *) 0 : tab, __MOVE_TAB, 0, 0);
479 	cost(
480 		dest_tabs_magic_smso ? (char *) 0
481 		: back_tab, __MOVE_BACK_TAB, 0, 0
482 	);
483 
484 	/* Absolute cursor motion with fixed cost. */
485 	cost(cursor_home, __MOVE_HOME, 0, 0);
486 	cost(cursor_to_ll, __MOVE_LAST_LINE, 0, 0);
487 	cost(carriage_return, __MOVE_RETURN, 0, 0);
488 
489 	/* Parameter cursor motion with worst case cost. */
490 	cost(row_address, __MOVE_ROW, lines-1, 0);
491 	cost(parm_up_cursor, __MOVE_N_UP, lines-1, 0);
492 	cost(parm_down_cursor, __MOVE_N_DOWN, lines-1, 0);
493 	cost(column_address, __MOVE_COLUMN, columns-1, 0);
494 	cost(parm_left_cursor, __MOVE_N_LEFT, columns-1, 0);
495 	cost(parm_right_cursor, __MOVE_N_RIGHT, columns-1, 0);
496 	cost(cursor_address, __MOVE_ROW_COLUMN, lines-1, columns-1);
497 }
498 
499 int
500 (mvcur)(oy, ox, ny, nx)
501 int oy, ox, ny, nx;
502 {
503 #ifdef M_CURSES_TRACE
504 	__m_trace("mvcur(%d, %d, %d, %d)", oy, ox, ny, nx);
505 #endif
506 
507 	return __m_return_code("mvcur", __m_mvcur(oy, ox, ny, nx, __m_outc));
508 }
509 
510