1 /*******************************************************************************
2  * Copyright (c) 2013-2017, 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 string.c
40  * \author Andrés Martinelli <andmarti@gmail.com>
41  * \date 2017-07-18
42  * \brief TODO Write a brief file description.
43  */
44 
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>   // for isdigit
48 #include <wctype.h>  // for iswprint
49 #include <wchar.h>
50 #include <stdio.h>
51 #include "string.h"
52 #include <curses.h>
53 #include "../sc.h"
54 #include "../macros.h"
55 
56 /**
57  * \brief Remove POSICION character of a cell (zero based)
58  *
59  * \param[in] str
60  * \param[in] posicion
61  *
62  * \return 0 on success
63  * \return -1 otherwise
64  */
65 
del_char(char * str,int posicion)66 int del_char(char * str, int posicion) {
67     int i, slen = strlen(str);
68 
69     if ( posicion >= slen || posicion < 0 ) return -1;
70     for (i = posicion; i < slen - 1; i++) {
71         str[i] = str[i + 1];
72     }
73     str[--slen] = '\0';
74     return 0;
75 }
76 
77 /**
78  * \brief Remove POSICION character of a cell (zero based) (Wide char version)
79  *
80  * \param[in] srt
81  * \param[in] posicion
82  *
83  * \return 0 on success
84  * \return -1 otherwise
85  */
86 
del_wchar(wchar_t * str,int posicion)87 int del_wchar(wchar_t * str, int posicion) {
88     int i, slen = wcslen(str);
89 
90     if ( posicion >= slen || posicion < 0 ) return -1;
91     for (i = posicion; i < slen - 1; i++) {
92         str[i] = str[i + 1];
93     }
94     str[--slen] = '\0';
95     return 0;
96 }
97 
98 /**
99  * \brief Remove D to H characters range of a cell
100  *
101  * \param[in] str
102  * \param[in] d
103  * \param[in] h
104  *
105  * \return 0 on success
106  * \return -1 otherwise
107  */
108 
del_range_chars(char * str,int d,int h)109 int del_range_chars(char * str, int d, int h) {
110     int i = 0, j = 0, slen = strlen(str);
111 
112     if ( h >= slen || h < d ) return -1;
113 
114     for (j = 0; j <= (h-d); j++)
115         for (i = d; i < slen - 1; i++) {
116             str[i] = str[i+1];
117         }
118     str[slen - (h - d) - 1] = '\0';
119     return 0;
120 }
121 
122 /**
123  * \brief Remove D to H characters range of a cell. Wide char version.
124  *
125  * \param[in] str
126  * \param[in] d
127  * \param[in] h
128  *
129  * \return 0 on success
130  * \return -1 otherwise
131  */
132 
del_range_wchars(wchar_t * str,int d,int h)133 int del_range_wchars(wchar_t * str, int d, int h) {
134     int i = 0, j = 0, slen = wcslen(str);
135 
136     if ( h >= slen || h < d ) return -1;
137 
138     for (j = 0; j <= (h-d); j++)
139         for (i = d; i < slen - 1; i++) {
140             str[i] = str[i+1];
141         }
142     str[slen - (h - d) - 1] = L'\0';
143     return 0;
144 }
145 
146 /**
147  * \brief Add a C character to a cell in POSICION.
148  *
149  * \details Wide char version. STR should be
150  * previously allocated with enough memory.
151  *
152  * \param[in] str
153  * \param[in] c
154  * \param[in] posicion
155  *
156  * \return 0 on success
157  * \return -1 otherwise
158  */
159 
add_char(char * str,char c,int posicion)160 int add_char(char * str, char c, int posicion) {
161     int slen = strlen(str);
162     int len = slen - posicion;
163     if (posicion > slen) return -1;
164     while (len) {
165         str[posicion + len] = str[posicion + len -1];
166         len--;
167     }
168     str[++slen] = '\0';
169     str[posicion] = c;
170     return 0;
171 }
172 
173 /**
174  * \brief Add a C character to a cell in POSICION. Wide char version.
175  *
176  * \details STR should be previously allocated with enough memory.
177  *
178  * \param[in] str
179  * \param[in] c
180  * \param[in] posicion
181  *
182  * \return 0 on success
183  * \return -1 otherwise
184  */
185 
add_wchar(wchar_t * str,wchar_t c,int posicion)186 int add_wchar(wchar_t * str, wchar_t c, int posicion) {
187     int slen = wcslen(str);
188     int len = slen - posicion;
189     if (posicion > slen) return -1;
190     while (len) {
191         str[posicion + len] = str[posicion + len -1];
192         len--;
193     }
194     str[++slen] = L'\0';
195     str[posicion] = c;
196     return 0;
197 }
198 
199 /**
200  * \brief Replace all matches FROM character TO character
201  *
202  * \param[in] s
203  * \param[in] from
204  * \param[in] to
205  *
206  * \return none
207  */
208 
subst(char * s,char from,char to)209 void subst(char * s, char from, char to) {
210     while (*s == from) *s++ = to;
211     return;
212 }
213 
214 /**
215  * \brief Rind string B inside string S
216  *
217  * \param[in]
218  * \param[in[ b
219  *
220  * \return S position in B
221  * \return -1 otherwise
222  */
223 
str_in_str(char * s,char * b)224 int str_in_str(char * s, char * b) {
225     int slen = strlen(s);
226     int blen = strlen(b);
227 
228     if (! slen || ! blen) return -1;
229 
230     int e = 0;
231     int i;
232 
233     while ( e <= slen-blen ) {
234         for (i=0; i<blen; i++) {
235             if (s[e+i] != b[i]) {
236                 break;
237             }
238         }
239         if (i >= blen) return e;
240         else e++;
241     }
242     return -1;
243 }
244 
245 /**
246  * \brief Find string B inside string S. Wide char version.
247  *
248  * \param[in] s
249  * \param[in] b
250  *
251  * \return S position in B
252  * \return -1 otherwise
253  */
254 
wstr_in_wstr(wchar_t * s,wchar_t * b)255 int wstr_in_wstr(wchar_t * s, wchar_t * b) {
256     int slen = wcslen(s);
257     int blen = wcslen(b);
258 
259     if (! slen || ! blen) return -1;
260 
261     int e = 0;
262     int i;
263 
264     while ( e <= slen-blen ) {
265         for (i=0; i<blen; i++) {
266             if (s[e+i] != b[i]) {
267                 break;
268             }
269         }
270         if (i >= blen) return e;
271         else e++;
272     }
273     return -1;
274 }
275 
276 /**
277  * \brief Returns 1 if special control character is found
278  *
279  * \param[in] d
280  *
281  * \return 1 if special control character is found
282  */
283 
is_idchar(int d)284 int is_idchar (int d) {
285     switch (d) {
286         case OKEY_LEFT:
287         case OKEY_RIGHT:
288         case OKEY_DOWN:
289         case OKEY_UP:
290         case OKEY_TAB:
291         case OKEY_BS:
292         case OKEY_HOME:
293         case OKEY_END:
294         case OKEY_PGUP:
295         case OKEY_PGDOWN:
296         case OKEY_DEL:
297         //case OKEY_ENTER:
298         case OKEY_ESC:
299            return 1;
300     }
301     return 0;
302 }
303 
304 /**
305  * \brief TODO Document this function
306  *
307  * \param[in] string
308  * \param[in] junk
309  *
310  * \return string
311  */
312 
rtrim(char * string,char junk)313 char * rtrim(char * string, char junk) {
314     char * original = string + strlen(string);
315     while(*--original == junk);
316     *(original + 1) = '\0';
317     return string;
318 }
319 
320 /**
321  * \brief TODO Document this function
322  *
323  * \param[in] string
324  * \param[in] junk
325  *
326  * \return TODO returns
327  */
328 
ltrim(char * string,char junk)329 char * ltrim(char * string, char junk) {
330     char * original = string;
331     char * p = original;
332     int trimmed = 0;
333     do {
334         if (*original != junk || trimmed) {
335             trimmed = 1;
336             *p++ = *original;
337         }
338     } while (*original++ != '\0');
339     return string;
340 }
341 
342 /**
343  * \brief Tells if a string represents a numeric value
344  *
345  * \param[in] string
346  *
347  * \return 1 if string represents a numeric value
348  * \return 0 otherwise
349  */
350 
isnumeric(char * string)351 int isnumeric(char * string) {
352     int i, len = strlen(string);
353     int result = true;
354     bool has_dot = false;
355     bool has_sign = false;
356     bool has_digit = false;
357     bool has_exponent = false;
358     for (i=0; i<len; i++) {
359 
360         if ( string[i] == '.' && ! has_dot ) {
361             has_dot = true;
362             continue;
363         }
364 
365         if ( (string[i] == 'e' || string[i] == 'E') && ! has_exponent && len > 1) {
366             has_exponent = true;
367             has_sign = false; // allow sign for exponent
368             continue;
369         }
370 
371         if ( (string[i] == '-' || string[i] == '+') ) {
372             if (has_digit && ! has_exponent ) {
373                 result = false;
374                 break;
375             }
376             if (! has_sign) {
377                 has_sign = true;
378             } else {
379                 result = false;
380                 break;
381             }
382             continue;
383         }
384 
385         if ( isdigit(string[i]) ) {
386             has_digit = true;
387             continue;
388         }
389 
390         result = false;
391         break;
392     }
393     return result;
394 }
395 
396 /**
397  * \brief Clean \r and \n from a string
398  *
399  * \param[in] string
400  *
401  * \return 1 of changes were made
402  * \return 0 otherwise
403  */
404 
clean_carrier(char * string)405 int clean_carrier(char * string) {
406     int i, changes = 0, len = strlen(string);
407     for (i=0; i<len; i++) {
408         if ( string[i] == '\r' || string[i] == '\n' ) {
409             del_char(string, i);
410             len--;
411             changes = 1;
412         }
413     }
414     return changes;
415 }
416 
417 /**
418  * \brief strtok version that handles null fields
419  *
420  * \param[in] line
421  * \param[in] delims
422  *
423  * \return TODO returns
424  */
425 
xstrtok(char * line,char * delims)426 char * xstrtok(char * line, char * delims) {
427     static char * saveline = NULL;
428     char * p;
429     int n;
430 
431     if (line != NULL)
432        saveline = line;
433 
434  // see if we have reached the end of the line
435     if (saveline == NULL || *saveline == '\0')
436        return(NULL);
437  // return the number of characters that aren't delims
438     n = strcspn(saveline, delims);
439     p = saveline; // save start of this token
440 
441     saveline += n; // bump past the delim
442 
443     if (*saveline != '\0') // trash the delim if necessary
444        *saveline++ = '\0';
445 
446     return p;
447 }
448 
449 /**
450  * \brief Change the number of occurences of a word in s. Not used.
451  *
452  * \param[in] s
453  * \param[in] word
454  * \param[in] overlap
455  *
456  * \return c
457  */
458 
count_word_occurrences(char * s,char * word,int overlap)459 int count_word_occurrences(char * s, char * word, int overlap) {
460     int c = 0, l = strlen(word);
461 
462     while (*s != '\0') {
463         if (strncmp(s++, word, l)) continue;
464         if (!overlap) s += l - 1;
465         c++;
466     }
467     return c;
468 }
469 
470 /**
471  * \brief Search substr word inside string and replace all its occurances with replacement
472  *
473  * \param[in] string
474  * \param[in] substr
475  * \param[in] replacement
476  *
477  * \return resulting string
478  */
479 
str_replace(const char * string,const char * substr,const char * replacement)480 char * str_replace ( const char * string, const char * substr, const char * replacement){
481     char * tok = NULL;
482     char * newstr = NULL;
483     char * oldstr = NULL;
484     char * head = NULL;
485 
486     /* if either substr or replacement is NULL, duplicate string a let caller handle it */
487     if ( substr == NULL || replacement == NULL )
488         return strdup (string);
489     newstr = strdup (string);
490     head = newstr;
491     while ( (tok = strstr ( head, substr ))) {
492         oldstr = newstr;
493         newstr = malloc ( strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) + 1 );
494         /* failed to alloc mem, free old string and return NULL */
495         if ( newstr == NULL ){
496             free (oldstr);
497             return NULL;
498         }
499         memcpy ( newstr, oldstr, tok - oldstr );
500         memcpy ( newstr + (tok - oldstr), replacement, strlen ( replacement ) );
501         memcpy ( newstr + (tok - oldstr) + strlen( replacement ), tok + strlen ( substr ), strlen ( oldstr ) - strlen ( substr ) - ( tok - oldstr ) );
502         memset ( newstr + strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) , 0, 1 );
503         /* move back head right after the last replacement */
504         head = newstr + (tok - oldstr) + strlen( replacement );
505         free (oldstr);
506     }
507     return newstr;
508 }
509 
510 /**
511  * \brief TODO Document uppercase()
512  *
513  * \param[in] sPtr
514  *
515  * \return none
516  */
517 
uppercase(char * sPtr)518 void uppercase(char * sPtr) {
519     for(; *sPtr != '\0'; ++sPtr)
520          *sPtr = toupper(*sPtr);
521 }
522 
523 /**
524  * \brief TODO Document sc+isprint()
525  *
526  * \param[in] d
527  *
528  * \return TODO returns
529  */
530 
sc_isprint(int d)531 int sc_isprint(int d) {
532     if ( ((d > 31) && (d < 127)) || ((d > 127) && (d < 255)) || iswprint(d) )
533         return 1;
534     return 0;
535 }
536 
537 /**
538  * \brief Return the number of wide chars of wchar_t * s string. Needed to
539  * fill p column positions.
540  *
541  * \param[in] s
542  * \param[in] p
543  *
544  * \return int
545  */
546 
count_width_widestring(const wchar_t * s,int p)547 int count_width_widestring(const wchar_t * s, int p) {
548     int n;
549     int c_width = 0;
550 
551     for (n=0; n<wcslen(s); n++) {
552         c_width += wcwidth( s[n] );
553         if (c_width > p) break;
554     }
555     return n;
556 }
557