1 /* GNUPLOT - screenbuf.c
2 
3    Data structure and methods to implement a screen buffer.
4    Implementation as a circular buffer.
5 
6    (NB: sb->head == sb->tail means NO element stored)
7 */
8 
9 /*
10 Copyright (c) 2011,2016 Bastian Maerkisch. All rights reserved.
11 
12 Redistribution and use in source and binary forms, with or without modification, are
13 permitted provided that the following conditions are met:
14 
15    1. Redistributions of source code must retain the above copyright notice, this list of
16       conditions and the following disclaimer.
17 
18    2. Redistributions in binary form must reproduce the above copyright notice, this list
19       of conditions and the following disclaimer in the documentation and/or other materials
20       provided with the distribution.
21 
22 THIS SOFTWARE IS PROVIDED BY Bastian Maerkisch ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
25 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 #ifdef _WIN32
34 # include <windows.h>
35 #else
36 typedef unsigned char BYTE;
37 #endif
38 #include <assert.h>
39 #include <memory.h>
40 #include <wchar.h>
41 #include "stdfn.h"
42 #include "screenbuf.h"
43 #include "winmain.h"
44 
45 static uint sb_internal_length(LPSB sb);
46 static LPLB sb_internal_get(LPSB sb, uint index);
47 static void lb_free(LPLB lb);
48 static void lb_copy(LPLB dest, LPLB src);
49 
50 
51 /* ------------------------------------ */
52 /*         line buffer functions        */
53 /* ------------------------------------ */
54 
55 
56 /*  lb_init:
57  *  initialize a line buffer, mark as free
58  */
59 void
lb_init(LPLB lb)60 lb_init(LPLB lb)
61 {
62     assert(lb != NULL);
63 
64     lb->str = NULL;
65     lb->attr = NULL;
66     lb->size = 0;
67     lb->len = 0;
68 }
69 
70 
71 /*  lb_free:
72  *  free members of a line buffer, mark as free
73  */
74 static void
lb_free(LPLB lb)75 lb_free(LPLB lb)
76 {
77     assert(lb != NULL);
78 
79     free(lb->str);
80     free(lb->attr);
81     lb_init(lb);
82 }
83 
84 
85 /*  lb_copy:
86  *  copy a line buffer from <src> to <dest>
87  */
88 static void
lb_copy(LPLB dest,LPLB src)89 lb_copy(LPLB dest, LPLB src)
90 {
91     assert(dest!= NULL);
92     assert(src != NULL);
93 
94     dest->str = src->str;
95     dest->attr = src->attr;
96     dest->size = src->size;
97     dest->len = src->len;
98     dest->def_attr = src->def_attr;
99 }
100 
101 
102 uint
lb_length(LPLB lb)103 lb_length(LPLB lb)
104 {
105     assert(lb != NULL);
106     return lb->len;
107 }
108 
109 
110 /*  lb_insert_char:
111  *  insert a character <ch> at position <pos> into the line buffer,
112  *  increase the size of the line buffer if necessary,
113  *  fill gaps with spaces
114  */
115 void
lb_insert_char(LPLB lb,uint pos,WCHAR ch)116 lb_insert_char(LPLB lb, uint pos, WCHAR ch)
117 {
118     lb_insert_str(lb, pos, &ch, 1);
119 }
120 
121 
122 /*  lb_insert_str:
123  *  (actually this is a misnomer as it overwrites any previous text)
124  *  insert a string <s> at position <pos> into the line buffer,
125  *  increase the size of the line buffer if necessary,
126  *  fill gaps with spaces
127  */
128 void
lb_insert_str(LPLB lb,uint pos,LPCWSTR s,uint count)129 lb_insert_str(LPLB lb, uint pos, LPCWSTR s, uint count)
130 {
131     assert(lb != NULL);
132 
133     /* enlarge string buffer if necessary */
134     if (lb->size <= (pos + count)) {
135 	LPWSTR newstr;
136 	PBYTE  newattr;
137 	uint newsize = (((pos + count + 8) / 8) * 8 + 32);
138 	newstr = (LPWSTR) realloc(lb->str, newsize * sizeof(WCHAR));
139 	newattr = (PBYTE) realloc(lb->attr, newsize * sizeof(BYTE));
140 	if (newstr && newattr) {
141 	    lb->str = newstr;
142 	    lb->attr = newattr;
143 	    lb->size = newsize;
144 	} else {
145 	    /* memory allocation failed */
146 	    if (pos < lb->size)
147 		return;
148 	    else
149 		count = lb->size - pos - 1;
150 	}
151     }
152 
153     /* fill up with spaces */
154     if (pos > lb->len) {
155 	wmemset(lb->str + lb->len, L' ', pos - lb->len);
156 	memset(lb->attr + lb->len, lb->def_attr, pos - lb->len);
157     }
158 
159     /* copy characters */
160     wmemcpy(lb->str + pos, s, count);
161     memset(lb->attr + pos, lb->def_attr, count);
162     lb->len = GPMAX(pos + count, lb->len);
163     lb->str[lb->len] = NUL;
164     lb->attr[lb->len] = NUL;
165 }
166 
167 
168 /*  lb_substr:
169  *  get a substring from the line buffer,
170  *  this string has to bee free'd afterwards!
171  */
172 LPWSTR
lb_substr(LPLB lb,uint offset,uint count)173 lb_substr(LPLB lb, uint offset, uint count)
174 {
175     uint len;
176     LPWSTR retval;
177 
178     len = (lb != NULL) ? lb->len : 0;
179 
180     /* allocate return string */
181     retval = (LPWSTR) malloc((count + 1) * sizeof(WCHAR));
182     if (retval == NULL)
183 	return NULL;
184 
185     if (offset >= len) {
186 	wmemset(retval, L' ', count);
187     } else {
188 	if (len >= (count + offset)) {
189 	    wmemcpy(retval, lb->str + offset, count);
190 	} else {
191 	    wmemcpy(retval, lb->str + offset, len - offset);
192 	    wmemset(retval + len - offset, L' ', count + offset - len);
193 	}
194     }
195     retval[count] = NUL;
196     return retval;
197 }
198 
199 
200 /*  lb_subattr:
201  *  get a sub-range of attribute from the line buffer,
202  *  this result has to bee free'd afterwards!
203  */
204 PBYTE
lb_subattr(LPLB lb,uint offset,uint count)205 lb_subattr(LPLB lb, uint offset, uint count)
206 {
207     uint len;
208     PBYTE retval;
209 
210     len = (lb != NULL) ? lb->len : 0;
211 
212     /* allocate return string */
213     retval = (PBYTE) malloc((count + 1) * sizeof(BYTE));
214     if (retval == NULL)
215 	return NULL;
216 
217     if (offset >= len) {
218 	memset(retval, lb->def_attr, count);
219     } else {
220 	if (len >= (count + offset)) {
221 	    memcpy(retval, lb->attr + offset, count);
222 	} else {
223 	    memcpy(retval, lb->attr + offset, len - offset);
224 	    memset(retval + len - offset, lb->def_attr, count + offset - len);
225 	}
226     }
227     retval[count] = NUL;
228     return retval;
229 }
230 
231 
232 void
lb_set_attr(LPLB lb,BYTE attr)233 lb_set_attr(LPLB lb, BYTE attr)
234 {
235     lb->def_attr = attr;
236 }
237 
238 
239 /* ------------------------------------ */
240 /*       screen buffer functions        */
241 /* ------------------------------------ */
242 
243 
244 /*  sb_init:
245  *  initialize a screen buffer with <size> line buffer elements
246  */
247 void
sb_init(LPSB sb,uint size)248 sb_init(LPSB sb, uint size)
249 {
250     assert(sb != NULL);
251 
252     sb->head = sb->tail = 0;
253     sb->wrap_at = 0;
254     sb->lb = (LPLB) calloc(size + 1, sizeof(LB));
255     sb->size = (sb->lb != NULL) ? size + 1 : 0;
256     sb->current_line = (LPLB) malloc(sizeof(LB));
257     sb->length = 0;
258     sb->last_line = 0;
259     sb->last_line_index = 0;
260 }
261 
262 
263 /*  sb_free:
264  *  free all line buffers of a screen buffer
265  */
266 void
sb_free(LPSB sb)267 sb_free(LPSB sb)
268 {
269     uint idx, len;
270 
271     assert(sb != NULL);
272     assert(sb->lb != NULL);
273 
274     /* free all line buffers */
275     len = sb_internal_length(sb);
276     for(idx = 0; idx < len; idx++)
277 	lb_free(&(sb->lb[idx]));
278 
279     free(sb->lb);
280     sb->lb = NULL;
281     sb->head = sb->tail = 0;
282     sb->size = 0;
283 }
284 
285 
286 /*  sb_internal_get:
287  *  retrieve line buffer according to index
288  */
289 LPLB
sb_internal_get(LPSB sb,uint index)290 sb_internal_get(LPSB sb, uint index)
291 {
292     LPLB line = NULL;
293 
294     assert(sb != NULL);
295     assert(index < sb->size);
296     assert(sb->lb != NULL);
297 
298     if (index < sb_internal_length(sb))
299 	line = &(sb->lb[(sb->head + index) % sb->size]);
300     return line;
301 }
302 
303 
304 /*  sb_get:
305  *  retrieve (wrapped) line buffer
306  */
307 LPLB
sb_get(LPSB sb,uint index)308 sb_get(LPSB sb, uint index)
309 {
310     LPLB line = NULL;
311 
312     assert(sb != NULL); assert((index < sb->size) || (sb->wrap_at > 0));
313     assert(sb->lb != NULL);
314 
315     if (sb->wrap_at == 0) {
316 	if (index < sb_internal_length(sb))
317 	    line = &(sb->lb[(sb->head + index) % sb->size]);
318     } else {
319 	/* count number of wrapped lines */
320 	uint line_count;
321 	uint idx;
322 	uint internal_length = sb_internal_length(sb);
323 
324 	if (sb->last_line <= index) {
325 	    /* use cached values */
326 	    line_count = sb->last_line;
327 	    idx = sb->last_line_index;
328 	} else {
329 	    line_count = 0;
330 	    idx = 0;
331 	}
332 	for ( ; (idx < internal_length); idx++) {
333 	    line_count += sb_lines(sb, sb_internal_get(sb, idx));
334 	    if (line_count > index) break;
335 	}
336 
337 	if (idx < internal_length) {
338 	    uint wrapped_lines;
339 	    uint len;
340 	    LPLB lb;
341 	    uint start, count;
342 
343 	    /* get last line buffer */
344 	    lb = sb_internal_get(sb, idx);
345 	    len = lb_length(lb);
346 	    wrapped_lines = sb_lines(sb, lb);
347 	    line_count -= wrapped_lines;
348 
349 	    /* cache current index */
350 	    sb->last_line_index = idx;
351 	    sb->last_line = line_count;
352 
353 	    /* index into current line buffer */
354 	    start = (index - line_count) * sb->wrap_at;
355 	    count = GPMIN(len - start, sb->wrap_at);
356 
357 	    /* copy substring from current line buffer */
358 	    lb_init(sb->current_line);
359 	    if (lb->str) {
360 		sb->current_line->len = count;
361 		sb->current_line->str = lb->str + start;
362 		sb->current_line->attr = lb->attr + start;
363 		//lb_insert_str(sb->current_line, 0, lb->str + start, count);
364 	    }
365 
366 	    /* return temporary buffer */
367 	    line = sb->current_line;
368 	}
369     }
370     return line;
371 }
372 
373 
374 /*  sb_get_last:
375  *  retrieve last line buffer
376  */
377 LPLB
sb_get_last(LPSB sb)378 sb_get_last(LPSB sb)
379 {
380     uint last;
381 
382     assert(sb != NULL);
383 
384     last = sb_internal_length(sb) - 1;
385     return sb_internal_get(sb, last);
386 }
387 
388 
389 /*  sb_append:
390  *  append a line buffer at the end of the screen buffer,
391  *  if the screen buffer is full discard the first line;
392  *  the line is _copied_ to the screen buffer
393  */
394 int
sb_append(LPSB sb,LPLB lb)395 sb_append(LPSB sb, LPLB lb)
396 {
397     uint idx;
398     int y_correction = 0;
399 
400     assert(sb != NULL);
401     assert(lb != NULL);
402 
403     idx = sb->tail;
404     sb->tail = (sb->tail + 1) % sb->size;
405     if (sb->tail == sb->head) {
406 	y_correction = sb_lines(sb, &(sb->lb[sb->head]));
407 	lb_free(&(sb->lb[sb->head]));
408 	sb->head = (sb->head + 1) % sb->size;
409     }
410     lb_copy(&(sb->lb[idx]), lb);
411 
412     sb->length += sb_lines(sb, lb) - y_correction;
413     return y_correction;
414 }
415 
416 
417 /*  sb_internal_length:
418  *  return number of entries (line buffers) of the screen buffer
419  */
420 uint
sb_internal_length(LPSB sb)421 sb_internal_length(LPSB sb)
422 {
423     uint lines;
424     assert(sb != NULL);
425 
426     if (sb->head <= sb->tail)
427 	lines = sb->tail - sb->head;
428     else
429 	lines = sb->size - 1;
430 
431     return lines;
432 }
433 
434 
435 
436 /*  sb_length:
437  *  get the current number of lines in the screen buffer
438  */
439 uint
sb_length(LPSB sb)440 sb_length(LPSB sb)
441 {
442     return sb->length;
443 }
444 
445 
446 /*  sb_length:
447  *  get the current number of lines in the screen buffer
448  */
449 uint
sb_calc_length(LPSB sb)450 sb_calc_length(LPSB sb)
451 {
452     int lines;
453     assert(sb != NULL);
454 
455     if (sb->wrap_at == 0) {
456 	lines = sb_internal_length(sb);
457     } else {
458 	uint idx;
459 
460 	/* count number of wrapped lines */
461         for(idx=0, lines=0; idx < sb_internal_length(sb); idx++)
462     	    lines += sb_lines(sb, sb_internal_get(sb, idx));
463     }
464     return lines;
465 }
466 
467 
468 /*  sb_resize:
469  *  change the maximum number of lines in the screen buffer to <size>
470  *  discard lines at the top if necessary
471  */
472 void
sb_resize(LPSB sb,uint size)473 sb_resize(LPSB sb, uint size)
474 {
475     LPLB lb;
476     uint sidx, idx, count;
477     uint len;
478 
479     /* allocate new buffer */
480     lb = (LPLB) calloc(size + 1, sizeof(LB));
481     if (lb == NULL) /* memory allocation failed */
482 	return;
483 
484     len = sb_internal_length(sb);
485     sidx = (size > len) ? 0 : (len - size);
486     count = (size > len) ? len : size;
487 
488     /* free elements if necessary */
489     for(idx = 0; idx < sidx; idx++)
490 	lb_free(sb_internal_get(sb, idx));
491 
492     /* copy elements to new buffer */
493     for(idx = 0; idx < count; idx++, sidx++)
494 	lb_copy(&(lb[idx]), sb_internal_get(sb, sidx));
495 
496     /* replace old buffer by new one */
497     free(sb->lb);
498     sb->lb = lb;
499     sb->size = size + 1;
500     sb->head = 0;
501     sb->tail = count;
502 }
503 
504 
505 /*  sb_lines:
506  *  return the number of (wrapped) text lines
507  */
508 uint
sb_lines(LPSB sb,LPLB lb)509 sb_lines(LPSB sb, LPLB lb)
510 {
511     if (sb->wrap_at != 0)
512 	return (lb_length(lb) + sb->wrap_at) / sb->wrap_at;
513     else
514 	return 1;
515 }
516 
517 
518 /*  sb_max_line_length:
519  *  determine maximum length of a single text line
520  */
521 uint
sb_max_line_length(LPSB sb)522 sb_max_line_length(LPSB sb)
523 {
524     uint idx;
525     uint len;
526     uint count;
527 
528     len = 0;
529     count = sb_internal_length(sb);
530     for(idx = 0; idx < count; idx++)
531 	len = GPMAX(lb_length(sb_internal_get(sb, idx)), len);
532 
533     if ((sb->wrap_at != 0) && (len > sb->wrap_at))
534 	len = sb->wrap_at;
535 
536     return len;
537 }
538 
539 
540 /*  sb_find_new_pos:
541  *  determine new x,y position after a change of the wrapping position
542  */
543 void
sb_find_new_pos(LPSB sb,uint x,uint y,uint new_wrap_at,uint * new_x,uint * new_y)544 sb_find_new_pos(LPSB sb, uint x, uint y, uint new_wrap_at, uint * new_x, uint * new_y)
545 {
546     uint internal_length;
547     uint line_count;
548     uint old_wrap_at;
549     uint idx, xofs;
550     uint i;
551 
552     /* determine index of corresponding internal line */
553     internal_length = sb_internal_length(sb);
554     for (idx = line_count = 0; idx < internal_length; idx++) {
555 	uint lines = sb_lines(sb, sb_internal_get(sb, idx));
556 	if (line_count + lines > y) break;
557 	line_count += lines;
558     }
559 
560     if (line_count == 0) {
561 	*new_x = *new_y = 0;
562 	return;
563     }
564 
565     /* calculate x offset within this line */
566     xofs = x + (y - line_count) * sb->wrap_at;
567 
568     if (new_wrap_at) {
569 	/* temporarily switch wrapping */
570 	old_wrap_at = sb->wrap_at;
571 	sb->wrap_at = new_wrap_at;
572 
573 	/* count lines with new wrapping */
574 	for (i = line_count = 0; i < idx; i++)
575 	    line_count += sb_lines(sb, sb_internal_get(sb, i));
576 
577 	/* determine new position */
578 	*new_x = xofs % new_wrap_at;
579 	*new_y = line_count + (xofs / new_wrap_at);
580 
581 	/* switch wrapping back */
582         sb->wrap_at = old_wrap_at;
583     } else {
584 	*new_x = xofs;
585 	*new_y = idx;
586     }
587 }
588 
589 
590 void
sb_wrap(LPSB sb,uint wrap_at)591 sb_wrap(LPSB sb, uint wrap_at)
592 {
593     sb->wrap_at = wrap_at;
594 
595     /* invalidate line cache */
596     sb->last_line = 0;
597     sb->last_line_index = 0;
598 
599     /* update length cache */
600     sb->length = sb_calc_length(sb);
601 }
602 
603 
604 /*  sb_last_insert_str:
605  *  call lb_insert str for the last line,
606  *  adjust total number of lines accordingly
607  */
608 void
sb_last_insert_str(LPSB sb,uint pos,LPCWSTR s,uint count)609 sb_last_insert_str(LPSB sb, uint pos, LPCWSTR s, uint count)
610 {
611     LPLB lb;
612     uint len;
613 
614     lb = sb_get_last(sb);
615     len = sb_lines(sb, lb);
616     lb_insert_str(lb, pos, s, count);
617     /* check if total length of sb has changed */
618     sb->length += sb_lines(sb, lb) - len;
619 }
620