1 /*******************************************************************************
2 * Copyright (c) 2013-2021, Andrés Martinelli <andmarti@gmail.com> *
3 * All rights reserved. *
4 * *
5 * This file is a part of SC-IM *
6 * *
7 * SC-IM is a spreadsheet program that is based on SC. The original authors *
8 * of SC are James Gosling and Mark Weiser, and mods were later added by *
9 * Chuck Martin. *
10 * *
11 * Redistribution and use in source and binary forms, with or without *
12 * modification, are permitted provided that the following conditions are met: *
13 * 1. Redistributions of source code must retain the above copyright *
14 * notice, this list of conditions and the following disclaimer. *
15 * 2. Redistributions in binary form must reproduce the above copyright *
16 * notice, this list of conditions and the following disclaimer in the *
17 * documentation and/or other materials provided with the distribution. *
18 * 3. All advertising materials mentioning features or use of this software *
19 * must display the following acknowledgement: *
20 * This product includes software developed by Andrés Martinelli *
21 * <andmarti@gmail.com>. *
22 * 4. Neither the name of the Andrés Martinelli nor the *
23 * names of other contributors may be used to endorse or promote products *
24 * derived from this software without specific prior written permission. *
25 * *
26 * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''AS IS'' AND ANY *
27 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
28 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
29 * DISCLAIMED. IN NO EVENT SHALL ANDRES MARTINELLI BE LIABLE FOR ANY *
30 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;*
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE *
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
36 *******************************************************************************/
37
38 /**
39 * \file maps.c
40 * \author Andrés Martinelli <andmarti@gmail.com>
41 * \date 2017-07-18
42 * \brief file that contain the different functions to support mappings
43 */
44
45 #include <stdlib.h>
46 #include <ctype.h>
47 #include <string.h>
48 #include "maps.h"
49 #include "string.h"
50 #include "macros.h"
51 #include "color.h"
52 #include "conf.h"
53 #include "sc.h"
54 #include "block.h"
55 #include "utils/string.h"
56
57 static map * maps;
58 static int mapdepth = 0;
59 int len_maps = 0;
60
61
62 /**
63 * \brief could_be_mapping
64 * a buffer may contain a valid command (for instance, just a letter in insert mode)
65 * but that buffer beginning may also be a possible mapping
66 *
67 * \param[in] struct block * (b)
68 * \return 0 if false. 1 otherwise
69 */
70
could_be_mapping(struct block * b)71 int could_be_mapping(struct block * b) {
72 // Traverse session mappings
73 map * m = maps;
74
75 while (m != NULL) {
76 // Check if 'b' buffer might be in mapping
77 if (block_in_block(m->in, b) == 0 && m->mode == curmode)
78 return 1;
79 m = m->psig;
80 }
81 return 0;
82 }
83
84
85 /**
86 * \brief replace_maps() inside a struct block * list.
87 * \param[in] b
88 * \return r
89 * return 0 when no mapping replaced
90 * return -1 when recursive mapping was detected
91 * return 1 when no recursive mapping was applied
92 * return 2 when recursive mapping was applied
93 */
replace_maps(struct block * b)94 int replace_maps (struct block * b) {
95 int r = 0;
96
97 if (++mapdepth == 1000) {
98 sc_error("recursive mapping");
99 flush_buf(b);
100 mapdepth = 0;
101 return 0;
102 }
103
104 // Traverse session mappings
105 map * m = maps;
106 while (m != NULL) {
107 // Check if a mapping already exists in 'b' buffer
108 int pos = block_in_block(b, m->in);
109 if (pos != -1 && m->mode == curmode) {
110
111 // Replace m->in with m->out in 'b' list
112 if (replace_block_in_block(b, m->in, m->out) == -1) {
113 sc_error("error replacing maps");
114 return -1;
115 }
116 r = 1;
117 break;
118 }
119 m = m->psig;
120 }
121
122 if (r && m->recursive) { replace_maps(b); return 2; } // recursive mapping here!
123 return r;
124 }
125
126 /**
127 * \brief Create list of blocks based on map strings
128 * \param[in] str
129 * \return buffer
130 */
131
get_mapbuf_str(char * str)132 struct block * get_mapbuf_str (char * str) {
133
134 struct block * buffer = create_buf();
135 unsigned short i, j;
136 unsigned short is_specialkey = 0;
137 char sk[MAXSC+1];
138 sk[0] = '\0';
139 const size_t size = strlen(str) + 1;
140 wchar_t wc[BUFFERSIZE] = { L'\0' };
141 size_t result = mbstowcs(wc, str, size);
142 if (result == (size_t)-1 ) return buffer;
143 unsigned short l = wcslen(wc);
144
145 for (i=0; i<l; i++) {
146
147 // Add special keys
148 if (str[i] == '<') {
149 is_specialkey = 1;
150
151 } else if (str[i] == '>') {
152 is_specialkey = 0;
153 if (! strcasecmp(sk, "CR")) // CR - ENTER key
154 addto_buf(buffer, OKEY_ENTER);
155 else if (! strcasecmp(sk, "ESC")) // ESC
156 addto_buf(buffer, OKEY_ESC);
157 else if (! strcasecmp(sk, "TAB")) // TAB
158 addto_buf(buffer, OKEY_TAB);
159 else if (! strcasecmp(sk, "SPACE")) // SPACE
160 addto_buf(buffer, OKEY_SPACE);
161 else if (! strcasecmp(sk, "LEFT")) // LEFT
162 addto_buf(buffer, OKEY_LEFT);
163 else if (! strcasecmp(sk, "RIGHT")) // RIGHT
164 addto_buf(buffer, OKEY_RIGHT);
165 else if (! strcasecmp(sk, "DOWN")) // DOWN
166 addto_buf(buffer, OKEY_DOWN);
167 else if (! strcasecmp(sk, "UP")) // UP
168 addto_buf(buffer, OKEY_UP);
169 else if (! strcasecmp(sk, "DEL")) // DEL
170 addto_buf(buffer, OKEY_DEL);
171 else if (! strcasecmp(sk, "BS")) // BS
172 addto_buf(buffer, OKEY_BS);
173 else if (! strcasecmp(sk, "HOME")) // HOME
174 addto_buf(buffer, OKEY_HOME);
175 else if (! strcasecmp(sk, "END")) // END
176 addto_buf(buffer, OKEY_END);
177 else if (! strcasecmp(sk, "PGDOWN")) // PGDOWN
178 addto_buf(buffer, OKEY_PGDOWN);
179 else if (! strcasecmp(sk, "PGUP")) // PGUP
180 addto_buf(buffer, OKEY_PGUP);
181 else if (! strncmp(sk, "C-", 2) && strlen(sk) == 3 // C-X
182 && ( (sk[2] > 64 && sk[2] < 91) || (sk[2] > 96 && sk[2] < 123)) )
183 addto_buf(buffer, ctl(tolower(sk[2])));
184
185 sk[0]='\0';
186
187 } else if (is_specialkey && strlen(sk) < MAXSC-1) {
188 add_char(sk, str[i], strlen(sk));
189
190 // Add some other characters
191 } else {
192 addto_buf(buffer, (wint_t) wc[i]);
193 }
194 }
195
196 // If the buffer lacks the trailing '>', insert it
197 if (is_specialkey && i == l) {
198 j = strlen(sk);
199 addto_buf(buffer, '<');
200 for (i=0; i<j; i++) addto_buf(buffer, (int) str[l-j+i]);
201 }
202 return buffer;
203 }
204
205 /**
206 * \brief Remove mappings and free corresponding memory
207 * \return none
208 */
209
del_maps()210 void del_maps () {
211 map * m = maps;
212 map * e = m;
213 while (m != NULL) {
214 e = m->psig;
215 erase_buf(m->out);
216 erase_buf(m->in);
217 free(m);
218 m = e;
219 }
220 return;
221 }
222
223 /**
224 * \brief Removes the last mapping of the current session
225 * \return none
226 */
227
get_last_map()228 map * get_last_map() {
229 map * m = maps;
230 map * e = m;
231
232 while (m != NULL) {
233 e = m;
234 m = m->psig;
235 }
236 return e;
237 }
238
239 /**
240 * \brief Returns the position of a mapping for some mode if it already exists, -1 otherwise
241 *
242 * \param[in] in
243 * \param[in] mode
244 *
245 * \return position of a mapping for some mode if it already exists; -1 otherwise
246 */
247
exists_map(char * in,int mode)248 int exists_map(char * in, int mode) {
249 map * m = maps;
250 char str_in[MAXMAPITEM] = "";
251 int pos = -1;
252
253 while (m != NULL) {
254 pos++;
255 get_mapstr_buf(m->in, str_in);
256 if ( ! strcmp(in, str_in) && m->mode == mode) {
257 return pos;
258 }
259 m = m->psig;
260 }
261 return -1;
262 }
263
264 /**
265 * \brief Ammend a mapping to the current session
266 *
267 * \param[in] in
268 * \param[in] out
269 * \param[in] mode
270 * \param[in] recursive
271 *
272 * \return none
273 */
274
add_map(char * in,char * out,int mode,short recursive)275 void add_map(char * in, char * out, int mode, short recursive) {
276 map * m;
277
278 // If the mapping already exists, replace its content, saving it position
279 int exists = exists_map(in, mode);
280 if (exists == -1) {
281 m = (map *) malloc (sizeof(map));
282 } else {
283 m = maps;
284 while (exists--) m = m->psig;
285 erase_buf(m->in);
286 erase_buf(m->out);
287 exists = TRUE;
288 }
289
290 m->out = (struct block *) get_mapbuf_str(out);
291 m->in = (struct block *) get_mapbuf_str(in);
292 m->mode = mode;
293 m->recursive = recursive;
294
295 if (exists == TRUE) return; // in case a map was updated and not created!
296
297 // Insert at the beginning
298 // m->psig = maps == NULL ? NULL : maps;
299 // maps = m;
300
301 // Insert at the end
302 m->psig= NULL;
303
304 if (maps == NULL) maps = m;
305 else
306 ((map *) get_last_map())->psig = m;
307
308 len_maps++;
309
310 return;
311 }
312
313 /**
314 * \brief Remove a mapping from a specific MODE
315 *
316 * \param[in] in
317 * \param[in] mode
318 *
319 * \return none
320 */
321
del_map(char * in,int mode)322 void del_map(char * in, int mode) {
323 map * ant;
324 map * m = maps;
325
326 if (m == NULL) return;
327
328 // If the node is the fist one
329 char strin [MAXSC * get_bufsize(m->in)];
330 get_mapstr_buf(m->in, strin);
331 if ( ! strcmp(in, strin) && mode == m->mode) {
332 maps = m->psig;
333 erase_buf(m->out);
334 erase_buf(m->in);
335 free(m);
336 len_maps--;
337 return;
338 }
339
340 // If the node is in the middle or at the end
341 ant = m;
342 m = m->psig;
343 while (m != NULL) {
344 char strin [MAXSC * get_bufsize(m->in)];
345 get_mapstr_buf(m->in, strin);
346 if ( ! strcmp(in, strin) && mode == m->mode) {
347 ant->psig = m->psig;
348 erase_buf(m->out);
349 erase_buf(m->in);
350 free(m);
351 m = ant->psig;
352 len_maps--;
353 return;
354 }
355 ant = m;
356 m = m->psig;
357 }
358 return;
359 }
360
361 /**
362 * \brief Translate a block into a string
363 *
364 * \details Translate a block in to a string. Special characters
365 * are in the <CR> form.
366 *
367 * \param[in] b
368 * \param[in] str
369 *
370 * \return none
371 */
372
get_mapstr_buf(struct block * b,char * str)373 void get_mapstr_buf (struct block * b, char * str) {
374 struct block * a = b;
375 int i, len = get_bufsize(a);
376
377 str[0]='\0';
378 for (i=0; i < len; i++) {
379 if (a->value == OKEY_ENTER) {
380 strcat(str, "<CR>"); // CR - ENTER
381 } else if (a->value == OKEY_TAB) {
382 strcat(str, "<TAB>"); // TAB
383 } else if (a->value == OKEY_SPACE) {
384 strcat(str, "<SPACE>"); // SPACE
385 } else if (a->value == OKEY_LEFT) {
386 strcat(str, "<LEFT>"); // LEFT
387 } else if (a->value == OKEY_RIGHT) {
388 strcat(str, "<RIGHT>"); // RIGHT
389 } else if (a->value == OKEY_DOWN) {
390 strcat(str, "<DOWN>"); // DOWN
391 } else if (a->value == OKEY_UP) {
392 strcat(str, "<UP>"); // UP
393 } else if (a->value == OKEY_DEL) {
394 strcat(str, "<DEL>"); // DEL
395 } else if (a->value == OKEY_BS || a->value == OKEY_BS2) {
396 strcat(str, "<BS>"); // BS
397 } else if (a->value == OKEY_HOME) {
398 strcat(str, "<HOME>"); // HOME
399 } else if (a->value == OKEY_END) {
400 strcat(str, "<END>"); // END
401 } else if (a->value == OKEY_PGDOWN) {
402 strcat(str, "<PGDOWN>"); // PGDOWN
403 } else if (a->value == OKEY_PGUP) {
404 strcat(str, "<PGUP>"); // PGUP
405 } else if (a->value == OKEY_ESC) {
406 strcat(str, "<ESC>"); // ESC
407 } else if (sc_isprint(a->value)) {
408 sprintf(str + strlen(str), "%lc", a->value); // ISPRINT
409 } else if ( a->value == (uncl(a->value) & 0x1f)) {
410 sprintf(str + strlen(str), "<C-%c>", uncl(a->value));// C-x
411 }
412 a = a->pnext;
413 }
414 return;
415 }
416
417 // Save mapping's details in a char*
418 /**
419 * \brief Save mapping's details in a char*
420 * \param[in] salida
421 * \return none
422 */
423
get_mappings(char * salida)424 void get_mappings(char * salida) {
425 salida[0]='\0';
426 if (maps == NULL) return;
427 char min[MAXMAPITEM] = "";
428 char mout[MAXMAPITEM] = "";
429 char nore[5] = "";
430
431 char mode = '\0';
432 int i = 0;
433 map * m = maps;
434
435 while (m != NULL) {
436 i++;
437 nore[0] = '\0';
438 switch (m->mode) {
439 case NORMAL_MODE:
440 mode = 'n';
441 break;
442
443 case INSERT_MODE:
444 mode = 'i';
445 break;
446
447 case VISUAL_MODE:
448 mode = 'v';
449 break;
450
451 case COMMAND_MODE:
452 mode = 'c';
453 break;
454 }
455 get_mapstr_buf(m->in, min);
456 get_mapstr_buf(m->out, mout);
457 if (!m->recursive) strcpy(nore, "nore");
458 sprintf(salida + strlen(salida), "%3d + %c%smap \"%s\" = \"%s\"\n", i, mode, nore, min, mout);
459 m = m->psig;
460 }
461 return;
462 }
463