1 #include "vterm_internal.h"
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <stdarg.h>
6 #include <string.h>
7 
8 /*****************
9  * API functions *
10  *****************/
11 
default_malloc(size_t size,void * allocdata)12 static void *default_malloc(size_t size, void *allocdata)
13 {
14   void *ptr = malloc(size);
15   if(ptr)
16     memset(ptr, 0, size);
17   return ptr;
18 }
19 
default_free(void * ptr,void * allocdata)20 static void default_free(void *ptr, void *allocdata)
21 {
22   free(ptr);
23 }
24 
25 static VTermAllocatorFunctions default_allocator = {
26   .malloc = &default_malloc,
27   .free   = &default_free,
28 };
29 
vterm_new(int rows,int cols)30 VTerm *vterm_new(int rows, int cols)
31 {
32   return vterm_new_with_allocator(rows, cols, &default_allocator, NULL);
33 }
34 
vterm_new_with_allocator(int rows,int cols,VTermAllocatorFunctions * funcs,void * allocdata)35 VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
36 {
37   /* Need to bootstrap using the allocator function directly */
38   VTerm *vt = (*funcs->malloc)(sizeof(VTerm), allocdata);
39 
40   vt->allocator = funcs;
41   vt->allocdata = allocdata;
42 
43   vt->rows = rows;
44   vt->cols = cols;
45 
46   vt->parser.state = NORMAL;
47 
48   vt->parser.callbacks = NULL;
49   vt->parser.cbdata    = NULL;
50 
51   vt->parser.strbuffer_len = 64;
52   vt->parser.strbuffer_cur = 0;
53   vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.strbuffer_len);
54 
55   vt->outfunc = NULL;
56   vt->outdata = NULL;
57 
58   vt->outbuffer_len = 64;
59   vt->outbuffer_cur = 0;
60   vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
61 
62   vt->tmpbuffer_len = 64;
63   vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
64 
65   return vt;
66 }
67 
vterm_free(VTerm * vt)68 void vterm_free(VTerm *vt)
69 {
70   if(vt->screen)
71     vterm_screen_free(vt->screen);
72 
73   if(vt->state)
74     vterm_state_free(vt->state);
75 
76   vterm_allocator_free(vt, vt->parser.strbuffer);
77   vterm_allocator_free(vt, vt->outbuffer);
78   vterm_allocator_free(vt, vt->tmpbuffer);
79 
80   vterm_allocator_free(vt, vt);
81 }
82 
vterm_allocator_malloc(VTerm * vt,size_t size)83 INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
84 {
85   return (*vt->allocator->malloc)(size, vt->allocdata);
86 }
87 
vterm_allocator_free(VTerm * vt,void * ptr)88 INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
89 {
90   (*vt->allocator->free)(ptr, vt->allocdata);
91 }
92 
vterm_get_size(const VTerm * vt,int * rowsp,int * colsp)93 void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
94 {
95   if(rowsp)
96     *rowsp = vt->rows;
97   if(colsp)
98     *colsp = vt->cols;
99 }
100 
vterm_set_size(VTerm * vt,int rows,int cols)101 void vterm_set_size(VTerm *vt, int rows, int cols)
102 {
103   vt->rows = rows;
104   vt->cols = cols;
105 
106   if(vt->parser.callbacks && vt->parser.callbacks->resize)
107     (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
108 }
109 
vterm_get_utf8(const VTerm * vt)110 int vterm_get_utf8(const VTerm *vt)
111 {
112   return vt->mode.utf8;
113 }
114 
vterm_set_utf8(VTerm * vt,int is_utf8)115 void vterm_set_utf8(VTerm *vt, int is_utf8)
116 {
117   vt->mode.utf8 = is_utf8;
118 }
119 
vterm_output_set_callback(VTerm * vt,VTermOutputCallback * func,void * user)120 void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
121 {
122   vt->outfunc = func;
123   vt->outdata = user;
124 }
125 
vterm_push_output_bytes(VTerm * vt,const char * bytes,size_t len)126 INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
127 {
128   if(vt->outfunc) {
129     (vt->outfunc)(bytes, len, vt->outdata);
130     return;
131   }
132 
133   if(len > vt->outbuffer_len - vt->outbuffer_cur)
134     return;
135 
136   memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
137   vt->outbuffer_cur += len;
138 }
139 
vterm_push_output_vsprintf(VTerm * vt,const char * format,va_list args)140 INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
141 {
142   size_t len = vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len,
143       format, args);
144 
145   vterm_push_output_bytes(vt, vt->tmpbuffer, len);
146 }
147 
vterm_push_output_sprintf(VTerm * vt,const char * format,...)148 INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
149 {
150   va_list args;
151   va_start(args, format);
152   vterm_push_output_vsprintf(vt, format, args);
153   va_end(args);
154 }
155 
vterm_push_output_sprintf_ctrl(VTerm * vt,unsigned char ctrl,const char * fmt,...)156 INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
157 {
158   size_t cur;
159 
160   if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
161     cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
162         ESC_S "%c", ctrl - 0x40);
163   else
164     cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
165         "%c", ctrl);
166 
167   if(cur >= vt->tmpbuffer_len)
168     return;
169 
170   va_list args;
171   va_start(args, fmt);
172   cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
173       fmt, args);
174   va_end(args);
175 
176   if(cur >= vt->tmpbuffer_len)
177     return;
178 
179   vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
180 }
181 
vterm_push_output_sprintf_dcs(VTerm * vt,const char * fmt,...)182 INTERNAL void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...)
183 {
184   size_t cur = 0;
185 
186   cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
187       vt->mode.ctrl8bit ? "\x90" : ESC_S "P"); // DCS
188 
189   if(cur >= vt->tmpbuffer_len)
190     return;
191 
192   va_list args;
193   va_start(args, fmt);
194   cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
195       fmt, args);
196   va_end(args);
197 
198   if(cur >= vt->tmpbuffer_len)
199     return;
200 
201   cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
202       vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
203 
204   if(cur >= vt->tmpbuffer_len)
205     return;
206 
207   vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
208 }
209 
vterm_output_get_buffer_size(const VTerm * vt)210 size_t vterm_output_get_buffer_size(const VTerm *vt)
211 {
212   return vt->outbuffer_len;
213 }
214 
vterm_output_get_buffer_current(const VTerm * vt)215 size_t vterm_output_get_buffer_current(const VTerm *vt)
216 {
217   return vt->outbuffer_cur;
218 }
219 
vterm_output_get_buffer_remaining(const VTerm * vt)220 size_t vterm_output_get_buffer_remaining(const VTerm *vt)
221 {
222   return vt->outbuffer_len - vt->outbuffer_cur;
223 }
224 
vterm_output_read(VTerm * vt,char * buffer,size_t len)225 size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
226 {
227   if(len > vt->outbuffer_cur)
228     len = vt->outbuffer_cur;
229 
230   memcpy(buffer, vt->outbuffer, len);
231 
232   if(len < vt->outbuffer_cur)
233     memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
234 
235   vt->outbuffer_cur -= len;
236 
237   return len;
238 }
239 
vterm_get_attr_type(VTermAttr attr)240 VTermValueType vterm_get_attr_type(VTermAttr attr)
241 {
242   switch(attr) {
243     case VTERM_ATTR_BOLD:       return VTERM_VALUETYPE_BOOL;
244     case VTERM_ATTR_UNDERLINE:  return VTERM_VALUETYPE_INT;
245     case VTERM_ATTR_ITALIC:     return VTERM_VALUETYPE_BOOL;
246     case VTERM_ATTR_BLINK:      return VTERM_VALUETYPE_BOOL;
247     case VTERM_ATTR_REVERSE:    return VTERM_VALUETYPE_BOOL;
248     case VTERM_ATTR_STRIKE:     return VTERM_VALUETYPE_BOOL;
249     case VTERM_ATTR_FONT:       return VTERM_VALUETYPE_INT;
250     case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
251     case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
252 
253     case VTERM_N_ATTRS: return 0;
254   }
255   return 0; /* UNREACHABLE */
256 }
257 
vterm_get_prop_type(VTermProp prop)258 VTermValueType vterm_get_prop_type(VTermProp prop)
259 {
260   switch(prop) {
261     case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
262     case VTERM_PROP_CURSORBLINK:   return VTERM_VALUETYPE_BOOL;
263     case VTERM_PROP_ALTSCREEN:     return VTERM_VALUETYPE_BOOL;
264     case VTERM_PROP_TITLE:         return VTERM_VALUETYPE_STRING;
265     case VTERM_PROP_ICONNAME:      return VTERM_VALUETYPE_STRING;
266     case VTERM_PROP_REVERSE:       return VTERM_VALUETYPE_BOOL;
267     case VTERM_PROP_CURSORSHAPE:   return VTERM_VALUETYPE_INT;
268     case VTERM_PROP_MOUSE:         return VTERM_VALUETYPE_INT;
269 
270     case VTERM_N_PROPS: return 0;
271   }
272   return 0; /* UNREACHABLE */
273 }
274 
vterm_scroll_rect(VTermRect rect,int downward,int rightward,int (* moverect)(VTermRect src,VTermRect dest,void * user),int (* eraserect)(VTermRect rect,int selective,void * user),void * user)275 void vterm_scroll_rect(VTermRect rect,
276     int downward,
277     int rightward,
278     int (*moverect)(VTermRect src, VTermRect dest, void *user),
279     int (*eraserect)(VTermRect rect, int selective, void *user),
280     void *user)
281 {
282   VTermRect src;
283   VTermRect dest;
284 
285   if(abs(downward)  >= rect.end_row - rect.start_row ||
286      abs(rightward) >= rect.end_col - rect.start_col) {
287     /* Scroll more than area; just erase the lot */
288     (*eraserect)(rect, 0, user);
289     return;
290   }
291 
292   if(rightward >= 0) {
293     /* rect: [XXX................]
294      * src:     [----------------]
295      * dest: [----------------]
296      */
297     dest.start_col = rect.start_col;
298     dest.end_col   = rect.end_col   - rightward;
299     src.start_col  = rect.start_col + rightward;
300     src.end_col    = rect.end_col;
301   }
302   else {
303     /* rect: [................XXX]
304      * src:  [----------------]
305      * dest:    [----------------]
306      */
307     int leftward = -rightward;
308     dest.start_col = rect.start_col + leftward;
309     dest.end_col   = rect.end_col;
310     src.start_col  = rect.start_col;
311     src.end_col    = rect.end_col - leftward;
312   }
313 
314   if(downward >= 0) {
315     dest.start_row = rect.start_row;
316     dest.end_row   = rect.end_row   - downward;
317     src.start_row  = rect.start_row + downward;
318     src.end_row    = rect.end_row;
319   }
320   else {
321     int upward = -downward;
322     dest.start_row = rect.start_row + upward;
323     dest.end_row   = rect.end_row;
324     src.start_row  = rect.start_row;
325     src.end_row    = rect.end_row - upward;
326   }
327 
328   if(moverect)
329     (*moverect)(dest, src, user);
330 
331   if(downward > 0)
332     rect.start_row = rect.end_row - downward;
333   else if(downward < 0)
334     rect.end_row = rect.start_row - downward;
335 
336   if(rightward > 0)
337     rect.start_col = rect.end_col - rightward;
338   else if(rightward < 0)
339     rect.end_col = rect.start_col - rightward;
340 
341   (*eraserect)(rect, 0, user);
342 }
343 
vterm_copy_cells(VTermRect dest,VTermRect src,void (* copycell)(VTermPos dest,VTermPos src,void * user),void * user)344 void vterm_copy_cells(VTermRect dest,
345     VTermRect src,
346     void (*copycell)(VTermPos dest, VTermPos src, void *user),
347     void *user)
348 {
349   int downward  = src.start_row - dest.start_row;
350   int rightward = src.start_col - dest.start_col;
351 
352   int init_row, test_row, init_col, test_col;
353   int inc_row, inc_col;
354 
355   if(downward < 0) {
356     init_row = dest.end_row - 1;
357     test_row = dest.start_row - 1;
358     inc_row = -1;
359   }
360   else /* downward >= 0 */ {
361     init_row = dest.start_row;
362     test_row = dest.end_row;
363     inc_row = +1;
364   }
365 
366   if(rightward < 0) {
367     init_col = dest.end_col - 1;
368     test_col = dest.start_col - 1;
369     inc_col = -1;
370   }
371   else /* rightward >= 0 */ {
372     init_col = dest.start_col;
373     test_col = dest.end_col;
374     inc_col = +1;
375   }
376 
377   VTermPos pos;
378   for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
379     for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
380       VTermPos srcpos = { pos.row + downward, pos.col + rightward };
381       (*copycell)(pos, srcpos, user);
382     }
383 }
384 
vterm_check_version(int major,int minor)385 void vterm_check_version(int major, int minor)
386 {
387   if(major != VTERM_VERSION_MAJOR) {
388     fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n",
389         major, VTERM_VERSION_MAJOR);
390     exit(1);
391   }
392 
393   if(minor > VTERM_VERSION_MINOR) {
394     fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n",
395         minor, VTERM_VERSION_MINOR);
396     exit(1);
397   }
398 
399   // Happy
400 }
401