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