1 /*--------------------------------------------------------------------
2  *
3  *	Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4  *	See LICENSE.TXT file for copying and redistribution conditions.
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU Lesser General Public License as published by
8  *	the Free Software Foundation; version 3 or any later version.
9  *
10  *	This program is distributed in the hope that it will be useful,
11  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *	GNU Lesser General Public License for more details.
14  *
15  *	Contact info: www.generic-mapping-tools.org
16  *--------------------------------------------------------------------*/
17 /*
18  * gmt_common_string.c contains code shared between GMT and PSL
19  *
20  * Author:  Florian Wobbe
21  * Date:    3-MAR-2012
22  * Version: 5
23  *
24  * Modules in this file:
25  *
26  *  gmt_chop                Chops off any CR or LF at end of string
27  *  gmt_chop_ext            Chops off the trailing .xxx (file extension)
28  *  gmt_get_ext             Returns a pointer to the tailing .xxx (file extension)
29  *  gmt_get_word            Return the specified word entry from a list
30  *  gmt_strdup_noquote		Duplicates a string but removes any surrounding single or double quotes
31  *  gmt_strstrip            Strip leading and trailing whitespace from string
32  *  gmt_strlshift           Left shift a string by n characters
33  *  gmt_strrepc             Replaces all occurrences of a char in the string
34  *  gmt_strrep              Replaces all occurrences of a string s2 in the string s1 by s3
35  *  gmt_strlcmp             Compares strings (ignoring case) until first reaches null character
36  *  gmt_strtok              Reiterant replacement of strtok
37  *  gmt_strtok_m            A Matlab style strtok
38  *  gmt_strrstr				A strstr but for last occurrence
39  *  gmt_dos_path_fix        Turn /c/dir/... paths into c:/dir/...
40  *  str(n)casecmp           Case-insensitive string comparison functions
41  *  strtok_r                Reentrant string tokenizer from Gnulib (LGPL)
42  *  strsep                  Reentrant string tokenizer that handles empty fields
43  *  strsepz                 Like strsep but ignores empty fields
44  *  stresep                 Like strsep but takes an additional argument esc in order
45  *                          to ignore escaped chars (from NetBSD)
46  *  basename                Extract the base portion of a pathname
47  */
48 
49 /* CMake definitions: This must be first! */
50 #include "gmt_config.h"
51 
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <string.h>
55 #include <assert.h>
56 
57 #include <limits.h>
58 #include <errno.h>
59 
60 #include "gmt_notposix.h"
61 #include "gmt_common_string.h"
62 
63 #define BUF_SIZE 4096
64 
gmt_strdup_noquote(const char * file)65 char *gmt_strdup_noquote (const char *file) {
66 	size_t last;
67 	if (file == NULL) return NULL;	/* No string given */
68 	if (file[0] == '\0') return strdup (file);	/* Return empty string */
69 	last = strlen (file) - 1;	/* We know here that the string is at least 1 character long, so len is >= 0 */
70 	if ((file[0] == '\'' || file[0] == '\"') && (file[last] == '\'' || file[last] == '\"'))	/* Quoted file name */
71 		return (strndup (&file[1], last-1));
72 	else
73 		return (strdup (file));
74 }
75 
gmt_chop_ext(char * string)76 char *gmt_chop_ext (char *string) {
77 	/* Chops off the filename extension (e.g., .ps) in the string by replacing the last
78 	 * '.' with '\0' and returns a pointer to the extension or NULL if not found. */
79 	char *p;
80 	assert (string != NULL); /* NULL pointer */
81     if ((p = strrchr(string, '.')) == NULL) return NULL; /* No extension found */
82     if (strchr (p, '/')) return NULL; /* Found a directory with a period */
83 #ifdef WIN32
84     if (strchr (p, '\\')) return NULL; /* Found a directory with a period */
85 #endif
86     *p = '\0';
87     return (p + 1);
88 }
89 
gmt_chop(char * string)90 void gmt_chop (char *string) {
91 	/* Chops off any CR or LF and terminates string */
92 	char *p;
93 	assert (string != NULL); /* NULL pointer */
94   /* if (string == NULL) return; / NULL pointer */
95 	if ((p = strpbrk (string, "\r\n")))
96 		/* Overwrite 1st CR or LF with terminate string */
97 		*p = '\0';
98 }
99 
gmt_get_ext(const char * string)100 char *gmt_get_ext (const char *string) {
101 	/* Returns a pointer to the filename extension (e.g., .ps)  or NULL if not found. */
102 	char *p;
103 	assert (string != NULL); /* NULL pointer */
104 	if ((p = strrchr(string, '.'))) {
105 		return (p + 1);
106 	}
107 	return NULL;
108 }
109 
gmt_strstrip(char * string,bool strip_leading)110 void gmt_strstrip(char *string, bool strip_leading) {
111 	/* Strip leading and trailing whitespace from string */
112 	char *start = string;
113 	char *end;
114 
115 	assert (string != NULL); /* NULL pointer */
116 
117 	if (strip_leading) {
118 		/* Skip over leading whitespace */
119 		while ((*start) && isspace(*start))
120 			++start;
121 		/* Is string just whitespace? */
122 		if (!(*start)) {
123 			*string = '\0'; /* Truncate entire string */
124 			return;
125 		}
126 	}
127 
128 	/* Find end of string */
129 	end = start;
130 	while (*end)
131 		++end;
132 
133 	/* Step backward until first non-whitespace */
134 	while ((--end != start) && isspace(*end));
135 
136 	/* Chop off trailing whitespace */
137 	*(end + 1) = '\0';
138 
139 	/* If leading whitespace, then move entire string back */
140 	if (string != start)
141 		memmove(string, start, end-start+2);
142 }
143 
gmt_strlshift(char * string,size_t n)144 void gmt_strlshift (char *string, size_t n) {
145 	/* Left shift a string by n characters */
146 	size_t len;
147 	assert (string != NULL); /* NULL pointer */
148 
149 	if ((len = strlen(string)) <= n ) {
150 		/* String shorter than shift width */
151 		*string = '\0'; /* Truncate entire string */
152 		return;
153 	}
154 
155 	/* Move entire string back */
156 	memmove(string, string + n, len + 1);
157 }
158 
gmt_strrepc(char * string,int c,int r)159 void gmt_strrepc (char *string, int c, int r) {
160 	/* Replaces all occurrences of c in the string with r */
161 	assert (string != NULL); /* NULL pointer */
162 	do {
163 		if (*string == c)
164 			*string = (char)r;
165 	} while (*(++string)); /* repeat until \0 reached */
166 }
167 
gmt_strlcmp(char * str1,char * str2)168 size_t gmt_strlcmp (char *str1, char *str2) {
169 	/* Compares str1 with str2 but only until str1 reaches the
170 	 * null-terminator character while case is ignored.
171 	 * When the strings match until that point, the routine returns the
172 	 * length of str1, otherwise it returns 0.
173 	 */
174 	size_t i = 0;
175 	while (str1[i] && tolower((unsigned char) str1[i]) == tolower((unsigned char) str2[i])) ++i;
176 	if (str1[i]) return 0;
177 	return i;
178 }
179 
gmt_strtok(const char * string,const char * sep,unsigned int * pos,char * token)180 unsigned int gmt_strtok (const char *string, const char *sep, unsigned int *pos, char *token) {
181 	/* Reentrant replacement for strtok that uses no static variables.
182 	 * Breaks string into tokens separated by one of more separator
183 	 * characters (in sep).  Set *pos to 0 before first call. Unlike
184 	 * strtok, always pass the original string as first argument.
185 	 * Returns 1 if it finds a token and 0 if no more tokens left.
186 	 * pos is updated and token is returned.  char *token must point
187 	 * to memory of length >= strlen (string).
188 	 * string is not changed by gmt_strtok.
189 	 */
190 
191 	size_t i, j, string_len;
192 
193 	string_len = strlen (string);
194 
195 	/* Wind up *pos to first non-separating character: */
196 	while (string[*pos] && strchr (sep, (int)string[*pos])) (*pos)++;
197 
198 	token[0] = 0;	/* Initialize token to NULL in case we are at end */
199 
200 	if (*pos >= string_len || string_len == 0) return 0;	/* Got NULL string or no more string left to search */
201 
202 	/* Search for next non-separating character */
203 	i = *pos; j = 0;
204 	while (string[i] && !strchr (sep, (int)string[i])) token[j++] = string[i++];
205 	token[j] = 0;	/* Add terminating \0 */
206 
207 	/* Wind up *pos to next non-separating character */
208 	while (string[i] && strchr (sep, (int)string[i])) i++;
209 	*pos = (unsigned int)i;
210 
211 	return 1;
212 }
213 
gmt_strtok_m(char * in,char ** token,char ** remain,char * sep)214 void gmt_strtok_m (char *in, char **token, char **remain, char *sep) {
215 	/* A Matlab style strtok. Note that 'token' and 'remain' must be virgin pointers,
216 	   otherwise the memory they point to will be leaked because they are allocated here
217 	   with strdup. For that reason the caller is responsible to free them after being consumed.
218 	 */
219 	unsigned int pos = 0;
220 	char *p, *s;
221 
222 	if (sep == NULL)
223 		s = " \t";
224 	else
225 		s = sep;
226 
227 	token[0] = NULL;		remain[0] = NULL;
228 
229 	p = calloc(strlen(in)+1, sizeof (char));
230 	if (gmt_strtok (in, s, &pos, p)) {
231 		token[0] = strdup(p);
232 		if (gmt_strtok (in, s, &pos, p))
233 			remain[0] = strdup(p);
234 	}
235 	free(p);
236 }
237 
gmt_strrstr(const char * s,const char * m)238 char *gmt_strrstr (const char *s, const char *m) {
239 	/* Find last occurrence of m in s */
240     char *last = NULL;
241     size_t n = strlen(m);
242 
243     while ((s = strchr(s, *m)) != NULL) {
244         if (!strncmp(s, m, n))
245             last = (char *)s;
246         if (*s++ == '\0')
247             break;
248     }
249     return last;
250 }
251 
gmt_get_word(char * list,char * sep,unsigned int col)252 char *gmt_get_word (char *list, char *sep, unsigned int col) {
253 	/* Return word number col in the list with separator sep */
254 	char *word, *trail, *orig, *retval;
255 	unsigned int k = 0;
256 	if (list == NULL || sep == NULL) return (NULL);
257 	orig = strdup (list);
258 	trail = orig;
259 	while ((word = strsep (&trail, sep)) != NULL && k < col) k++;
260 	retval = (k == col) ? strdup (word) : NULL;
261 	free (orig);
262 	return (retval);
263 }
264 
gmt_get_modifier(const char * string,char modifier,char * token)265 unsigned int gmt_get_modifier (const char *string, char modifier, char *token) {
266 	/* Looks for modifier string in the form +<modifier>[arg] and if found
267 	   returns 1 and places arg in token, else return 0.  Must ignore any
268 	   +<modifier> found inside quotes as part of text. If token is NULL
269 	   then we only return 1 or 0 and no string.
270 	*/
271 	bool quoted = false;
272 	size_t k, len, start = 0;
273 
274 	if (!string || string[0] == 0) return 0;	/* No hope */
275 	len = strlen (string);
276 	for (k = 0; start == 0 && k < (len-1); k++) {
277 		if (string[k] == '\"' || string[k] == '\'') quoted = !quoted;	/* Initially false, becomes true at start of quote, then false when exit quote */
278 		if (quoted) continue;		/* Not look inside quoted strings */
279 		if (string[k] == '+' && string[k+1] == modifier)	/* Found the start */
280 			start = k+2;
281 	}
282 	if (start == 0) return 0;	/* Not found */
283 	for (k = start; k < len; k++) {
284 		if (string[k] == '\"' || string[k] == '\'') quoted = !quoted;	/* Initially false, becomes true at start of quote, then false when exit quote */
285 		if (quoted) continue;	/* Not look inside quoted strings */
286 		if (string[k] == '+')	/* Found the end */
287 			break;
288 	}
289 	len = k - start;
290 	if (token) {	/* Only pass back when token is not NULL */
291 		if (len) strncpy (token, &string[start], len);
292 		token[len] = '\0';
293 	}
294 	return 1;
295 }
296 
297 #ifdef WIN32
298 /* Turn '/c/dir/...' paths into 'c:/dir/...'
299  * Must do it in a loop since dir may be several ';'-separated dirs */
gmt_dos_path_fix(char * dir)300 void gmt_dos_path_fix (char *dir) {
301 	size_t n, k;
302 
303 	if (!dir || (n = strlen (dir)) < 2U)
304 		/* Given NULL or too short dir to work */
305 		return;
306 
307 	if (!strncmp (dir, "/cygdrive/", 10U))
308 		/* May happen for example when Cygwin sets GMT_SHAREDIR */
309 		gmt_strlshift (dir, 9); /* Chop '/cygdrive' */
310 
311 	/* Replace dumb backslashes with slashes */
312 	gmt_strrepc (dir, '\\', '/');
313 
314 	/* If dir begins with '/' and is 2 long, as in '/c', replace with 'c:' */
315 	if (n == 2U && dir[0] == '/') {
316 		dir[0] = dir[1];
317 		dir[1] = ':';
318 		return;
319 	}
320 
321 	/* If dir is longer than 2 and, e.g., '/c/', replace with 'c:/' */
322 	if (n > 2U && dir[0] == '/' && dir[2] == '/' && isalpha ((int)dir[1])) {
323 		dir[0] = dir[1];
324 		dir[1] = ':';
325 	}
326 
327 	/* Do the same with dirs separated by ';' but do not replace '/c/j/...' with 'c:j:/...' */
328 	for (k = 4; k < n-2; k++) {
329 		if ( (dir[k-1] == ';' && dir[k] == '/' && dir[k+2] == '/' && isalpha ((int)dir[k+1])) ) {
330 			dir[k] = dir[k+1];
331 			dir[k+1] = ':';
332 		}
333 	}
334 
335 	/* Replace ...:C:/... by ...;C:/... as that was a multi-path set by an e.g. bash shell (msys or cygwin) */
336 	for (k = 4; k < n-2; k++) {
337 		if ((dir[k-1] == ':' && dir[k+1] == ':' && dir[k+2] == '/' && isalpha ((int)dir[k])) )
338 			dir[k-1] = ';';
339 		else if ((dir[k-1] == ':' && dir[k] == '/' && dir[k+2] == '/' && isalpha ((int)dir[k+1])) ) {
340 			/* The form ...:/C/... will become ...;C:/... */
341 			dir[k-1] = ';';
342 			dir[k] = dir[k+1];
343 			dir[k+1] = ':';
344 		}
345 	}
346 }
347 #endif
348 
349 #if !defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)
350 /* Case-insensitive string comparison function.
351    Copyright (C) 1998-1999, 2005-2007, 2009-2012 Free Software Foundation, Inc.
352 
353    This program is free software; you can redistribute it and/or modify
354    it under the terms of the GNU General Public License as published by
355    the Free Software Foundation; either version 2, or (at your option)
356    any later version.
357 
358    This program is distributed in the hope that it will be useful,
359    but WITHOUT ANY WARRANTY; without even the implied warranty of
360    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
361    GNU General Public License for more details.
362 
363    You should have received a copy of the GNU General Public License
364    along with this program; if not, see <http://www.gnu.org/licenses/>.  */
365 
366 #ifndef TOLOWER
367 #	define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
368 #endif
369 
370 /* Compare strings S1 and S2, ignoring case, returning less than, equal to or
371    greater than zero if S1 is lexicographically less than, equal to or greater
372    than S2.
373    Note: This function does not work with multibyte strings!  */
374 
strcasecmp(const char * s1,const char * s2)375 int strcasecmp (const char *s1, const char *s2) {
376 	const unsigned char *p1 = (const unsigned char *) s1;
377 	const unsigned char *p2 = (const unsigned char *) s2;
378 	unsigned char c1, c2;
379 
380 	if (p1 == p2) return 0;
381 
382 	do {
383 		c1 = TOLOWER (*p1);
384 		c2 = TOLOWER (*p2);
385 
386 		if (c1 == '\0')
387 			break;
388 
389 		++p1;
390 		++p2;
391 	}
392 	while (c1 == c2);
393 
394 	if (UCHAR_MAX <= INT_MAX)
395 		return c1 - c2;
396 	else
397 		/* On machines where 'char' and 'int' are types of the same size, the
398 		difference of two 'unsigned char' values - including the sign bit -
399 		doesn't fit in an 'int'.  */
400 		return (c1 > c2 ? 1 : c1 < c2 ? -1 : 0);
401 }
402 #endif /* !defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP) */
403 
404 #if !defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)
405 /* strncasecmp.c -- case insensitive string comparator
406    Copyright (C) 1998-1999, 2005-2007, 2009-2012 Free Software Foundation, Inc.
407 
408    This program is free software; you can redistribute it and/or modify
409    it under the terms of the GNU General Public License as published by
410    the Free Software Foundation; either version 2, or (at your option)
411    any later version.
412 
413    This program is distributed in the hope that it will be useful,
414    but WITHOUT ANY WARRANTY; without even the implied warranty of
415    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
416    GNU General Public License for more details.
417 
418    You should have received a copy of the GNU General Public License
419    along with this program; if not, see <http://www.gnu.org/licenses/>.  */
420 
421 #ifndef TOLOWER
422 #	define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
423 #endif
424 
425 /* Compare no more than N bytes of strings S1 and S2, ignoring case,
426    returning less than, equal to or greater than zero if S1 is
427    lexicographically less than, equal to or greater than S2.
428    Note: This function cannot work correctly in multibyte locales.  */
429 
strncasecmp(const char * s1,const char * s2,size_t n)430 int strncasecmp (const char *s1, const char *s2, size_t n) {
431   register const unsigned char *p1 = (const unsigned char *) s1;
432   register const unsigned char *p2 = (const unsigned char *) s2;
433   unsigned char c1, c2;
434 
435   if (p1 == p2 || n == 0)
436     return 0;
437 
438   do
439     {
440       c1 = TOLOWER (*p1);
441       c2 = TOLOWER (*p2);
442 
443       if (--n == 0 || c1 == '\0')
444         break;
445 
446       ++p1;
447       ++p2;
448     }
449   while (c1 == c2);
450 
451   if (UCHAR_MAX <= INT_MAX)
452     return c1 - c2;
453   else
454     /* On machines where 'char' and 'int' are types of the same size, the
455        difference of two 'unsigned char' values - including the sign bit -
456        doesn't fit in an 'int'.  */
457     return (c1 > c2 ? 1 : c1 < c2 ? -1 : 0);
458 }
459 #endif /* !defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP) */
460 
461 #if !defined(HAVE_STRTOK_R) && !defined(HAVE_STRTOK_S)
462 /* Reentrant string tokenizer.  Generic version.
463    Copyright (C) 1991, 1996-1999, 2001, 2004, 2007, 2009-2012 Free Software
464    Foundation, Inc.
465    This file is part of the GNU C Library.
466 
467    This program is free software: you can redistribute it and/or modify
468    it under the terms of the GNU Lesser General Public License as published by
469    the Free Software Foundation; either version 3 of the License, or
470    (at your option) any later version.
471 
472    This program is distributed in the hope that it will be useful,
473    but WITHOUT ANY WARRANTY; without even the implied warranty of
474    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
475    GNU Lesser General Public License for more details.
476 
477    You should have received a copy of the GNU Lesser General Public License
478    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
479 
480 #if 0
481 /* Parse S into tokens separated by characters in DELIM. */
482    If S is NULL, the saved pointer in SAVE_PTR is used as
483    the next starting point.  For example:
484         char s[] = "-abc-=-def";
485         char *sp;
486         x = strtok_r(s, "-", &sp);      /* x = "abc", sp = "=-def"	*/
487         x = strtok_r(NULL, "-=", &sp);  /* x = "def", sp = NULL		*/
488         x = strtok_r(NULL, "=", &sp);   /* x = NULL					*/
489                                         /* s = "abc\0-def\0"		*/
490 #endif
strtok_r(char * s,const char * delim,char ** save_ptr)491 char *strtok_r (char *s, const char *delim, char **save_ptr) {
492   char *token;
493 
494   if (s == NULL)
495     s = *save_ptr;
496 
497   /* Scan leading delimiters.  */
498   s += strspn (s, delim);
499   if (*s == '\0')
500     {
501       *save_ptr = s;
502       return NULL;
503     }
504 
505   /* Find the end of the token.  */
506   token = s;
507   s = strpbrk (token, delim);
508   if (s == NULL)
509     /* This token finishes the string.  */
510     *save_ptr = strchr (token, '\0');
511   else
512     {
513       /* Terminate the token and make *SAVE_PTR point past it.  */
514       *s = '\0';
515       *save_ptr = s + 1;
516     }
517   return token;
518 }
519 #endif /* !defined(HAVE_STRTOK_R) && !defined(HAVE_STRTOK_S) */
520 
521 #ifndef HAVE_STRSEP
522 /* Copyright (C) 2004, 2007, 2009-2012 Free Software Foundation, Inc.
523 
524    Written by Yoann Vandoorselaere <yoann@prelude-ids.org>.
525 
526    This program is free software: you can redistribute it and/or modify
527    it under the terms of the GNU Lesser General Public License as published by
528    the Free Software Foundation; either version 3 of the License, or
529    (at your option) any later version.
530 
531    This program is distributed in the hope that it will be useful,
532    but WITHOUT ANY WARRANTY; without even the implied warranty of
533    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
534    GNU Lesser General Public License for more details.
535 
536    You should have received a copy of the GNU Lesser General Public License
537    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
538 
strsep(char ** stringp,const char * delim)539 char * strsep (char **stringp, const char *delim) {
540   char *start = *stringp;
541   char *ptr;
542 
543   if (start == NULL)
544     return NULL;
545 
546   /* Optimize the case of no delimiters.  */
547   if (delim[0] == '\0')
548     {
549       *stringp = NULL;
550       return start;
551     }
552 
553   /* Optimize the case of one delimiter.  */
554   if (delim[1] == '\0')
555     ptr = strchr (start, delim[0]);
556   else
557     /* The general case.  */
558     ptr = strpbrk (start, delim);
559   if (ptr == NULL)
560     {
561       *stringp = NULL;
562       return start;
563     }
564 
565   *ptr = '\0';
566   *stringp = ptr + 1;
567 
568   return start;
569 }
570 #endif /* ifndef HAVE_STRSEP */
571 
572 /* Like strsep but ignores empty fields.
573  * PW: Made two near-identical version of strsepz:
574  *   strsepz:  As it was, but with an unused 3rd argument.
575  *   strsepzp: 3rd argument returns position in original stringp.
576  */
577 
strsepz(char ** stringp,const char * delim,size_t * pos)578 char *strsepz (char **stringp, const char *delim, size_t *pos) {
579 	char *c;
580 	(void)(pos);
581 	while ( (c = strsep(stringp, delim)) != NULL && *c == '\0' );
582 	return c;
583 }
584 
strsepzp(char ** stringp,const char * delim,size_t * pos)585 char *strsepzp (char **stringp, const char *delim, size_t *pos) {
586 	char *c;
587 	while ( (c = strsep(stringp, delim)) != NULL && *c == '\0' )
588 		(*pos)++;
589 	if (c) (*pos) += strlen (c) + 1;
590 	return c;
591 }
592 
593 /* $NetBSD: stresep.c,v 1.2 2007/12/06 22:07:07 seb Exp $
594  *
595  * Copyright (c) 1990, 1993
596  * The Regents of the University of California.  All rights reserved.
597  *
598  * Redistribution and use in source and binary forms, with or without
599  * modification, are permitted provided that the following conditions
600  * are met:
601  * 1. Redistributions of source code must retain the above copyright
602  *    notice, this list of conditions and the following disclaimer.
603  * 2. Redistributions in binary form must reproduce the above copyright
604  *    notice, this list of conditions and the following disclaimer in the
605  *    documentation and/or other materials provided with the distribution.
606  * 3. Neither the name of the University nor the names of its contributors
607  *    may be used to endorse or promote products derived from this software
608  *    without specific prior written permission.
609  *
610  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
611  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
612  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
613  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
614  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
615  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
616  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
617  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
618  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
619  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
620  * SUCH DAMAGE.
621  */
622 
623 /*
624  * Get next token from string *stringp, where tokens are possibly-empty
625  * strings separated by characters from delim. If esc is not NUL, then
626  * the characters followed by esc are ignored and are not taken into account
627  * when splitting the string.
628  *
629  * Writes NULs into the string at *stringp to end tokens.
630  * delim need not remain constant from call to call.
631  * On return, *stringp points past the last NUL written (if there might
632  * be further tokens), or is NULL (if there are definitely no more tokens).
633  *
634  * If *stringp is NULL, stresep returns NULL.
635  */
636 
stresep(char ** stringp,const char * delim,int esc)637 char *stresep(char **stringp, const char *delim, int esc) {
638 	char *s;
639 	const char *spanp;
640 	int c, sc;
641 	char *tok;
642 
643 	assert(delim != NULL);
644 
645 	if ((s = *stringp) == NULL)
646 		return NULL;
647 	for (tok = s;;) {
648 		c = *s++;
649 		while (esc != '\0' && c == esc) {
650 			(void)strcpy(s - 1, s);
651 			c = *s++;
652 		}
653 		spanp = delim;
654 		do {
655 			if ((sc = *spanp++) == c) {
656 				if (c == 0)
657 					s = NULL;
658 				else
659 					s[-1] = 0;
660 				*stringp = s;
661 				return tok;
662 			}
663 		} while (sc != 0);
664 	}
665 }
666 
667 /* $OpenBSD: basename.c,v 1.14 2005/08/08 08:05:33 espie Exp $
668  *
669  * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
670  *
671  * Permission to use, copy, modify, and distribute this software for any
672  * purpose with or without fee is hereby granted, provided that the above
673  * copyright notice and this permission notice appear in all copies.
674  *
675  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
676  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
677  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
678  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
679  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
680  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
681  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
682  */
683 
684 /* The basename() function returns the last component from the pathname
685  * pointed to by path, deleting any trailing `/' characters.  If path consists
686  * entirely of `/' characters, a pointer to the string "/" is returned.  If
687  * path is a null pointer or the empty string, a pointer to the string "." is
688  * returned.
689  *
690  * On successful completion, basename() returns a pointer to the last
691  * component of path.
692  *
693  * If basename() fails, a null pointer is returned and the global variable
694  * errno is set to indicate the error.
695  *
696  * The basename() function returns a pointer to internal static storage
697  * space that will be overwritten by subsequent calls (not thread safe).  The
698  * function does not modify the string pointed to by path. */
699 
700 #ifndef HAVE_BASENAME
basename(char * path)701 char *basename(char *path) {
702 #ifdef WIN32
703 	static char path_fixed[PATH_MAX];
704 #endif
705 	static char bname[PATH_MAX];
706 	size_t len;
707 	const char *endp, *startp;
708 
709 	/* Empty or NULL string gets treated as "." */
710 	if (path == NULL || *path == '\0') {
711 		bname[0] = '.';
712 		bname[1] = '\0';
713 		return (bname);
714 	}
715 
716 #ifdef WIN32
717 	if (strchr (path, '\\')) {
718 		char *fixedp = path_fixed;
719 		if (strlen (path) >= PATH_MAX) {
720 			errno = ENAMETOOLONG;
721 			return (NULL);
722 		}
723 		/* Replace backslashes with slashes */
724 		while (*path) { /* repeat until \0 reached */
725 			if (*path == '\\')
726 				*fixedp = '/';
727 			else
728 				*fixedp = *path;
729 			++path, ++fixedp;
730 		}
731 		*fixedp = '\0'; /* terminate string */
732 		path = path_fixed;
733 	}
734 #endif
735 
736 	/* Strip any trailing slashes */
737 	endp = path + strlen(path) - 1;
738 	while (endp > path && *endp == '/')
739 		endp--;
740 
741 	/* All slashes becomes "/" */
742 	if (endp == path && *endp == '/') {
743 		bname[0] = '/';
744 		bname[1] = '\0';
745 		return (bname);
746 	}
747 
748 	/* Find the start of the base */
749 	startp = endp;
750 	while (startp > path && *(startp - 1) != '/')
751 		startp--;
752 
753 	len = endp - startp + 1;
754 	if (len >= PATH_MAX) {
755 		errno = ENAMETOOLONG;
756 		return (NULL);
757 	}
758 	memcpy(bname, startp, len);
759 	bname[len] = '\0';
760 	return (bname);
761 }
762 #endif
763 
764 /*
765  * strrep.c - C substring replacement.
766  *
767  * Written in 2011 by Drew Hess <dhess-src@bothan.net>.
768  * https://gist.github.com/dhess/975639
769  *
770  * To the extent possible under law, the author(s) have dedicated all
771  * copyright and related and neighboring rights to this software to
772  * the public domain worldwide. This software is distributed without
773  * any warranty.
774  *
775  * For the full statement of the dedication, see the Creative Commons
776  * CC0 Public Domain Dedication at
777  * <http://creativecommons.org/publicdomain/zero/1.0/>.
778  */
779 
780  /*
781   * This file includes a main() function so that the file can be
782   * compiled into an executable, in order to run some simple test cases
783   * on the included strrep() function. If you want to use strrep in
784   * your own project, make sure you cut or comment out the main()
785   * function below.
786   *
787   * This function returns string s1 if string s2 is an empty string, or
788   * if s2 is not found in s1. If s2 is found in s1, the function
789   * returns a new null-terminated string whose contents are identical
790   * to s1, except that all occurrences of s2 in the original string s1
791   * are, in the new string, replaced by the string s3. The caller owns
792   * the new string.
793   *
794   * Strings s1, s2, and s3 must all be null-terminated strings. If any
795   * of s1, s2, or s3 are NULL, the function returns NULL, indicating an
796   * error condition. If any other error occurs, the function returns NULL.
797   *
798   * This code is written pedantically, primarily so that asserts can be
799   * used liberally. The code could certainly be optimized and/or made
800   * less verbose, and I encourage you to do that if you use strstr in
801   * your production code, once you're comfortable that it functions as
802   * intended. Each assert makes plain an invariant condition that is
803   * assumed to be true by the statement(s) that immediately follow the
804   * assert.  Some of the asserts are trivially true, as written, but
805   * they're included, nonetheless, in case you, in the process of
806   * optimizing or adapting the code for your own purposes, make a
807   * change that breaks an assumption made downstream by the original code.
808   */
809 
gmt_strrep(const char * s1,const char * s2,const char * s3)810  char *gmt_strrep(const char *s1, const char *s2, const char *s3) {
811 	 size_t s1_len, s2_len, s3_len, count, s1_without_s2_len, newstr_len, i, substr_len, remains;
812 	 const char *p, *start_substr, *end_substr;
813 	 char *newstr, *dst;
814 	 if (!s1 || !s2 || !s3)
815 		 return 0;
816 	 s1_len = strlen(s1);
817 	 if (!s1_len)
818 		 return (char *)s1;
819 	 s2_len = strlen(s2);
820 	 if (!s2_len)
821 		 return (char *)s1;
822 
823 	 /*
824 	  * Two-pass approach: figure out how much space to allocate for
825 	  * the new string, pre-allocate it, then perform replacement(s).
826 	  */
827 	 count = 0;
828 	 p = s1;
829 	 assert(s2_len); /* otherwise, strstr(s1,s2) will return s1. */
830 	 do {
831 		 p = strstr(p, s2);
832 		 if (p) {
833 			 p += s2_len;
834 			 count++;
835 		 }
836 	 } while (p);
837 
838 	 if (!count)
839 		 return (char *)s1;
840 
841 	 /*
842 	  * The following size arithmetic is extremely cautious, to guard against size_t overflows.
843 	  */
844 	 assert(s1_len >= count * s2_len);
845 	 assert(count);
846 	 s1_without_s2_len = s1_len - count * s2_len;
847 	 s3_len = strlen(s3);
848 	 newstr_len = s1_without_s2_len + count * s3_len;
849 	 if (s3_len && ((newstr_len <= s1_without_s2_len) || (newstr_len + 1 == 0))) /* Overflow. */
850 		 return 0;
851 
852 	 newstr = (char *)calloc(newstr_len + 1, sizeof(char)); /* w/ terminator */
853 	 if (!newstr)		/* ENOMEM, but no good way to signal it. */
854 		 return 0;
855 
856 	 dst = newstr;
857 	 start_substr = s1;
858 	 for (i = 0; i != count; ++i) {
859 		 end_substr = strstr(start_substr, s2);
860 		 assert(end_substr);
861 		 substr_len = end_substr - start_substr;
862 		 memcpy(dst, start_substr, substr_len);
863 		 dst += substr_len;
864 		 memcpy(dst, s3, s3_len);
865 		 dst += s3_len;
866 		 start_substr = end_substr + s2_len;
867 	 }
868 
869 	 /* copy remainder of s1, including trailing '\0' */
870 	 remains = s1_len - (start_substr - s1) + 1;
871 	 assert(dst + remains == newstr + newstr_len + 1);
872 	 memcpy(dst, start_substr, remains);
873 	 assert(strlen(newstr) == newstr_len);
874 	 return newstr;
875 }
876 
877 #ifndef HAVE_CHARCAT	/* Do not think this is a standard function but just in case */
chrcat(char * dest,const char add)878 char *chrcat (char *dest, const char add) {
879 	/* Simple function to add a single character to a string. No check if there is room
880 	 * only that it is not NULL, and we explicitly terminate the updated string */
881 	if (dest) {
882 		dest[strlen(dest)] = add;
883 		dest[strlen(dest)] = '\0';
884 	}
885 	return (dest);
886 }
887 #endif
888