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