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