1 #if	!defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: random.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
4 
5 /*
6  * ========================================================================
7  * Copyright 2006-2007 University of Washington
8  * Copyright 2013-2021 Eduardo Chappa
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  */
18 
19 /*
20  * Program:	Random routines
21  *
22  * This file contains the command processing functions for a number of random
23  * commands. There is no functional grouping here, for sure.
24  */
25 
26 #include	"headers.h"
27 
28 #include "osdep/terminal.h"
29 
30 int	worthit(int *);
31 
32 int     tabsize;                        /* Tab size (0: use real tabs)  */
33 
34 
35 /*
36  * Display the current position of the cursor, in origin 1 X-Y coordinates,
37  * the character that is under the cursor (in octal), and the fraction of the
38  * text that is before the cursor. The displayed column is not the current
39  * column, but the column that would be used on an infinite width display.
40  * Normally this is bound to "C-X =".
41  */
42 int
showcpos(int f,int n)43 showcpos(int f, int n)
44 {
45     register LINE   *clp;
46     register long   nch;
47     register int    cbo;
48     register long   nbc;
49     register int    lines;
50     register int    thisline = 0;
51     char     buffer[100];
52 
53     clp = lforw(curbp->b_linep);            /* Grovel the data.     */
54     cbo = 0;
55     nch = 0L;
56     lines = 0;
57     for (;;) {
58 	if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
59 	    thisline = lines;
60 	    nbc = nch;
61 	}
62 	if (cbo == llength(clp)) {
63 	    if (clp == curbp->b_linep)
64 	      break;
65 	    clp = lforw(clp);
66 	    cbo = 0;
67 	    lines++;
68 	} else
69 	  ++cbo;
70 	++nch;
71     }
72 
73     snprintf(buffer,sizeof(buffer),"line %d of %d (%d%%%%), character %ld of %ld (%d%%%%)",
74 	    thisline+1, lines+1, (int)((100L*(thisline+1))/(lines+1)),
75 	    nbc, nch, (nch) ? (int)((100L*nbc)/nch) : 0);
76 
77     emlwrite(buffer, NULL);
78     return (TRUE);
79 }
80 
81 
82 /*
83  * Return current column.  Stop at first non-blank given TRUE argument.
84  */
85 int
getccol(int bflg)86 getccol(int bflg)
87 {
88     UCS c;
89     int i, col;
90 
91     col = 0;
92     for (i=0; i<curwp->w_doto; ++i) {
93 	c = lgetc(curwp->w_dotp, i).c;
94 	if (c!=' ' && c!='\t' && bflg)
95 	  break;
96 
97 	if (c == '\t'){
98 	    col |= 0x07;
99 	    ++col;
100 	}
101 	else if (ISCONTROL(c)){
102 	    col += 2;
103 	}
104 	else{
105 	    int ww;
106 
107 	    ww = wcellwidth(c);
108 	    col += (ww >= 0 ? ww : 1);
109 	}
110     }
111 
112     return(col);
113 }
114 
115 
116 
117 /*
118  * Set tab size if given non-default argument (n <> 1).  Otherwise, insert a
119  * tab into file.  If given argument, n, of zero, change to true tabs.
120  * If n > 1, simulate tab stop every n-characters using spaces. This has to be
121  * done in this slightly funny way because the tab (in ASCII) has been turned
122  * into "C-I" (in 10 bit code) already. Bound to "C-I".
123  */
124 int
tab(int f,int n)125 tab(int f, int n)
126 {
127     if (n < 0)
128       return (FALSE);
129 
130     if (n == 0 || n > 1) {
131 	tabsize = n;
132 	return(TRUE);
133     }
134 
135     if (! tabsize)
136       return(linsert(1, '\t'));
137 
138     return(linsert(tabsize - (getccol(FALSE) % tabsize), ' '));
139 }
140 
141 
142 /*
143  * Insert a newline. Bound to "C-M".
144  */
145 int
newline(int f,int n)146 newline(int f, int n)
147 {
148     register int    s;
149 
150     if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
151       return(rdonly());	/* we are in read only mode	*/
152 
153     if (n < 0)
154       return (FALSE);
155 
156     if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
157 	int l;
158 
159 	if(worthit(&l)){
160 	    if(curwp->w_doto != 0)
161 	      l++;
162 	    scrolldown(curwp, l, n);
163 	}
164     }
165 
166     /* if we are in C mode and this is a default <NL> */
167     /* pico's never in C mode */
168 
169     if(Pmaster && Pmaster->allow_flowed_text && curwp->w_doto
170        && ucs4_isspace(lgetc(curwp->w_dotp, curwp->w_doto - 1).c)
171        && !(curwp->w_doto == 3
172 	    && lgetc(curwp->w_dotp, 0).c == '-'
173 	    && lgetc(curwp->w_dotp, 1).c == '-'
174 	    && lgetc(curwp->w_dotp, 2).c == ' ')){
175 	/*
176 	 * flowed mode, make the newline a hard one by
177 	 * stripping trailing space.
178 	 */
179 	int i, dellen;
180 	for(i = curwp->w_doto - 1;
181 	    i && ucs4_isspace(lgetc(curwp->w_dotp, i - 1).c);
182 	    i--);
183 	dellen = curwp->w_doto - i;
184 	curwp->w_doto = i;
185 	ldelete(dellen, NULL);
186     }
187     /* insert some lines */
188     while (n--) {
189 	if ((s=lnewline()) != TRUE)
190 	  return (s);
191     }
192     return (TRUE);
193 }
194 
195 
196 
197 /*
198  * Delete forward. This is real easy, because the basic delete routine does
199  * all of the work. Watches for negative arguments, and does the right thing.
200  * If any argument is present, it kills rather than deletes, to prevent loss
201  * of text if typed with a big argument. Normally bound to "C-D".
202  */
203 int
forwdel(int f,int n)204 forwdel(int f, int n)
205 {
206     if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
207       return(rdonly());	/* we are in read only mode	*/
208 
209     if (n < 0)
210       return (backdel(f, -n));
211 
212     if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
213 	int l;
214 
215 	if(worthit(&l) && curwp->w_doto == llength(curwp->w_dotp))
216 	  scrollup(curwp, l+1, 1);
217     }
218 
219     if (f != FALSE) {                       /* Really a kill.       */
220 	if ((lastflag&CFKILL) == 0)
221 	  kdelete();
222 	thisflag |= CFKILL;
223     }
224 
225     return (ldelete((long) n, f ? kinsert : NULL));
226 }
227 
228 
229 
230 /*
231  * Delete backwards. This is quite easy too, because it's all done with other
232  * functions. Just move the cursor back, and delete forwards. Like delete
233  * forward, this actually does a kill if presented with an argument. Bound to
234  * both "RUBOUT" and "C-H".
235  */
236 int
backdel(int f,int n)237 backdel(int f, int n)
238 {
239     register int    s;
240 
241     if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
242       return(rdonly());	/* we are in read only mode	*/
243 
244     if (n < 0)
245       return (forwdel(f, -n));
246 
247     if(TERM_OPTIMIZE && curwp->w_dotp != curwp->w_bufp->b_linep){
248 	int l;
249 
250 	if(worthit(&l) && curwp->w_doto == 0 &&
251 	   lback(curwp->w_dotp) != curwp->w_bufp->b_linep){
252 	    if(l == curwp->w_toprow)
253 	      scrollup(curwp, l+1, 1);
254 	    else if(llength(lback(curwp->w_dotp)) == 0)
255 	      scrollup(curwp, l-1, 1);
256 	    else
257 	      scrollup(curwp, l, 1);
258 	}
259     }
260 
261     if (f != FALSE) {                       /* Really a kill.       */
262 	if ((lastflag&CFKILL) == 0)
263 	  kdelete();
264 
265 	thisflag |= CFKILL;
266     }
267 
268     if ((s=backchar(f, n)) == TRUE)
269       s = ldelete((long) n, f ? kinsert : NULL);
270 
271     return (s);
272 }
273 
274 
275 
276 /*
277  * killtext - delete the line that the cursor is currently in.
278  *	      a greatly pared down version of its former self.
279  */
280 int
killtext(int f,int n)281 killtext(int f, int n)
282 {
283     register int chunk;
284     int		 opt_scroll = 0;
285 
286     if (curbp->b_mode&MDVIEW)		/* don't allow this command if  */
287       return(rdonly());			/* we are in read only mode     */
288 
289     if ((lastflag&CFKILL) == 0)		/* Clear kill buffer if */
290       kdelete();			/* last wasn't a kill.  */
291 
292     if(gmode & MDDTKILL){		/*  */
293 	if((chunk = llength(curwp->w_dotp) - curwp->w_doto) == 0){
294 	    chunk = 1;
295 	    if(TERM_OPTIMIZE)
296 	      opt_scroll = 1;
297 	}
298     }
299     else{
300 	gotobol(FALSE, 1);		/* wack from bol past newline */
301 	chunk = llength(curwp->w_dotp) + 1;
302 	if(TERM_OPTIMIZE)
303 	  opt_scroll = 1;
304     }
305 
306     /* optimize what motion we can */
307     if(opt_scroll && (curwp->w_dotp != curwp->w_bufp->b_linep)){
308 	int l;
309 
310 	if(worthit(&l))
311 	  scrollup(curwp, l, 1);
312     }
313 
314     thisflag |= CFKILL;
315     return(ldelete((long) chunk, kinsert));
316 }
317 
318 
319 /*
320  * Yank text back from the kill buffer. This is really easy. All of the work
321  * is done by the standard insert routines. All you do is run the loop, and
322  * check for errors. Bound to "C-Y".
323  */
324 int
yank(int f,int n)325 yank(int f, int n)
326 {
327     int    c, i;
328     REGION region, *added_region;
329     LINE  *dotp;
330 
331     if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
332       return(rdonly());	/* we are in read only mode	*/
333 
334     if (n < 0)
335       return (FALSE);
336 
337     if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
338 	int l;
339 
340 	if(worthit(&l) && !(lastflag&CFFILL)){
341 	    register int  t = 0;
342 	    register int  i = 0;
343 	    register int  ch;
344 
345 	    while((ch=fremove(i++)) >= 0)
346 	      if(ch == '\n')
347 		t++;
348 	    if(t+l < curwp->w_toprow+curwp->w_ntrows)
349 	      scrolldown(curwp, l, t);
350 	}
351     }
352 
353     if(lastflag & CFFILL){		/* if last command was fillpara() */
354 	if(lastflag & CFFLBF){
355 	    gotoeob(FALSE, 1);
356 	    dotp = curwp->w_dotp;
357 	    gotobob(FALSE, 1);
358 	    curwp->w_doto = 0;
359 	    getregion(&region, dotp, llength(dotp));
360 	}
361 	else{
362 	    added_region = get_last_region_added();
363 	    if(added_region){
364 		curwp->w_dotp = added_region->r_linep;
365 		curwp->w_doto = added_region->r_offset;
366 		region = (*added_region);
367 	    }
368 	    else
369 	      return(FALSE);
370 	}
371 
372 	if(!ldelete(region.r_size, NULL))
373 	  return(FALSE);
374     }					/* then splat out the saved buffer */
375 
376     while (n--) {
377 	i = 0;
378 	while ((c = ((lastflag&CFFILL)
379 		     ? ((lastflag & CFFLBF) ? kremove(i) : fremove(i))
380 		     : kremove(i))) >= 0) {
381 	    if (c == '\n') {
382 		if (lnewline() == FALSE)
383 		  return (FALSE);
384 	    } else {
385 		if (linsert(1, c) == FALSE)
386 		  return (FALSE);
387 	    }
388 
389 	    ++i;
390 	}
391     }
392 
393     if(lastflag&CFFLPA){            /* if last command was fill paragraph */
394 	curwp->w_dotp = lforw(curwp->w_dotp);
395 	curwp->w_doto = 0;
396 
397 	curwp->w_flag |= WFMODE;
398 
399 	if(!Pmaster){
400 	    sgarbk = TRUE;
401 	    emlwrite("", NULL);
402 	}
403     }
404 
405     return (TRUE);
406 }
407 
408 
409 
410 /*
411  * worthit - generic sort of test to roughly gage usefulness of using
412  *           optimized scrolling.
413  *
414  * note:
415  *	returns the line on the screen, l, that the dot is currently on
416  */
417 int
worthit(int * l)418 worthit(int *l)
419 {
420     int i;			/* l is current line */
421     unsigned below;		/* below is avg # of ch/line under . */
422 
423     *l = doton(&i, &below);
424     below = (i > 0) ? below/(unsigned)i : 0;
425 
426     return(below > 3);
427 }
428