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  * script2verbatim.c removes comments and replaces -ps from example scripts
19  *
20  * Author:  Florian Wobbe
21  * Date:    6-JAN-2015
22  * Version: 5
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29 
30 #define MAX_LINE_LEN 256
31 #define FAILURE_PREFIX "script2verbatim: "
32 
33 /* Copy from common_string.c to avoid Windows link issues */
34 /*
35  * strrep.c - C substring replacement.
36  *
37  * Written in 2011 by Drew Hess <dhess-src@bothan.net>.
38  * https://gist.github.com/dhess/975639
39  *
40  * To the extent possible under law, the author(s) have dedicated all
41  * copyright and related and neighboring rights to this software to
42  * the public domain worldwide. This software is distributed without
43  * any warranty.
44  *
45  * For the full statement of the dedication, see the Creative Commons
46  * CC0 Public Domain Dedication at
47  * <http://creativecommons.org/publicdomain/zero/1.0/>.
48  */
49 
50  /*
51   * This file includes a main() function so that the file can be
52   * compiled into an executable, in order to run some simple test cases
53   * on the included strrep() function. If you want to use strrep in
54   * your own project, make sure you cut or comment out the main()
55   * function below.
56   *
57   * This function returns string s1 if string s2 is an empty string, or
58   * if s2 is not found in s1. If s2 is found in s1, the function
59   * returns a new null-terminated string whose contents are identical
60   * to s1, except that all occurrences of s2 in the original string s1
61   * are, in the new string, replaced by the string s3. The caller owns
62   * the new string.
63   *
64   * Strings s1, s2, and s3 must all be null-terminated strings. If any
65   * of s1, s2, or s3 are NULL, the function returns NULL, indicating an
66   * error condition. If any other error occurs, the function returns NULL.
67   *
68   * This code is written pedantically, primarily so that asserts can be
69   * used liberally. The code could certainly be optimized and/or made
70   * less verbose, and I encourage you to do that if you use strstr in
71   * your production code, once you're comfortable that it functions as
72   * intended. Each assert makes plain an invariant condition that is
73   * assumed to be true by the statement(s) that immediately follow the
74   * assert.  Some of the asserts are trivially true, as written, but
75   * they're included, nonetheless, in case you, in the process of
76   * optimizing or adapting the code for your own purposes, make a
77   * change that breaks an assumption made downstream by the original code.
78   */
79 
gmt_strrep(const char * s1,const char * s2,const char * s3)80  char *gmt_strrep(const char *s1, const char *s2, const char *s3) {
81 	 size_t s1_len, s2_len, s3_len, count, s1_without_s2_len, newstr_len, i, substr_len, remains;
82 	 const char *p, *start_substr, *end_substr;
83 	 char *newstr, *dst;
84 	 if (!s1 || !s2 || !s3)
85 		 return 0;
86 	 s1_len = strlen(s1);
87 	 if (!s1_len)
88 		 return (char *)s1;
89 	 s2_len = strlen(s2);
90 	 if (!s2_len)
91 		 return (char *)s1;
92 
93 	 /*
94 	  * Two-pass approach: figure out how much space to allocate for
95 	  * the new string, pre-allocate it, then perform replacement(s).
96 	  */
97 	 count = 0;
98 	 p = s1;
99 	 assert(s2_len); /* otherwise, strstr(s1,s2) will return s1. */
100 	 do {
101 		 p = strstr(p, s2);
102 		 if (p) {
103 			 p += s2_len;
104 			 count++;
105 		 }
106 	 } while (p);
107 
108 	 if (!count)
109 		 return (char *)s1;
110 
111 	 /*
112 	  * The following size arithmetic is extremely cautious, to guard against size_t overflows.
113 	  */
114 	 assert(s1_len >= count * s2_len);
115 	 assert(count);
116 	 s1_without_s2_len = s1_len - count * s2_len;
117 	 s3_len = strlen(s3);
118 	 newstr_len = s1_without_s2_len + count * s3_len;
119 	 if (s3_len && ((newstr_len <= s1_without_s2_len) || (newstr_len + 1 == 0))) /* Overflow. */
120 		 return 0;
121 
122 	 newstr = (char *)calloc(newstr_len + 1, sizeof(char)); /* w/ terminator */
123 	 if (!newstr)		/* ENOMEM, but no good way to signal it. */
124 		 return 0;
125 
126 	 dst = newstr;
127 	 start_substr = s1;
128 	 for (i = 0; i != count; ++i) {
129 		 end_substr = strstr(start_substr, s2);
130 		 assert(end_substr);
131 		 substr_len = end_substr - start_substr;
132 		 memcpy(dst, start_substr, substr_len);
133 		 dst += substr_len;
134 		 memcpy(dst, s3, s3_len);
135 		 dst += s3_len;
136 		 start_substr = end_substr + s2_len;
137 	 }
138 
139 	 /* copy remainder of s1, including trailing '\0' */
140 	 remains = s1_len - (start_substr - s1) + 1;
141 	 assert(dst + remains == newstr + newstr_len + 1);
142 	 memcpy(dst, start_substr, remains);
143 	 assert(strlen(newstr) == newstr_len);
144 	 return newstr;
145 }
146 
script2verbatim_is_comment(char * line)147 static int script2verbatim_is_comment (char *line) {
148 	/* return 1 if line is a comment line, 0 otherwise */
149 	size_t n = strspn (line, " #");  /* span ' ' and '#' */
150 	while (n > 0) {
151 		if (line[--n] == '#') /* rewind until '#' found */
152 			return 1;
153 	}
154 	/* not a comment line */
155 	return 0;
156 }
157 
158 
main(int argc,char * argv[])159 int main (int argc, char *argv[]) {
160     	int i, nargs = 0, line_num = 0, strip_comments = 0, ps2pdf = 0;
161 	FILE *fp_in, *fp_out;
162 	char line[MAX_LINE_LEN];
163 
164 	for (i = 1; i < argc; i++) {
165 		if (strcmp (argv[i], "--strip-comments") == 0) strip_comments = 1;
166 		else if (strcmp (argv[i], "--ps2pdf") == 0) ps2pdf = 1;
167 		else	nargs++;
168 	}
169 
170 	if (nargs != 2) {
171 		fprintf (stderr, FAILURE_PREFIX "usage: script2verbatim [--strip-comments] [--ps2pdf] input output\n");
172 		return EXIT_FAILURE;
173 	}
174 
175 	if ((fp_in = fopen (argv[argc-2], "r")) == NULL) {
176 		fprintf (stderr, FAILURE_PREFIX "error opening input file %s.\n", argv[argc-2]);
177 		return EXIT_FAILURE;
178 	}
179 
180 	if ((fp_out = fopen (argv[argc-1], "w")) == NULL) {
181 		fprintf (stderr, FAILURE_PREFIX "error opening output file %s.\n", argv[argc-1]);
182 		fclose (fp_in);
183 		return EXIT_FAILURE;
184 	}
185 
186 	/* Opening files succeeded */
187 	while (fgets (line, MAX_LINE_LEN, fp_in) != NULL) {
188 		size_t len = strlen (line);
189 		++line_num;
190 		if (len > MAX_LINE_LEN) {
191 			fprintf (stderr, FAILURE_PREFIX "line %d too long: %s\n", line_num, line);
192 			fclose (fp_in);
193 			fclose (fp_out);
194 			return EXIT_FAILURE;
195 		}
196 		if (strip_comments && script2verbatim_is_comment(line)) continue;
197 		if (ps2pdf)
198 			fputs (gmt_strrep(line, " -ps ", " -pdf "), fp_out);
199 		else
200 			fputs (line, fp_out);
201 	}
202 
203 	/* Check EOF indicator */
204 	if (!feof (fp_in)) {
205 		fprintf (stderr, FAILURE_PREFIX "error: did not reach eof.\n");
206 		fclose (fp_in);
207 		fclose (fp_out);
208 		return EXIT_FAILURE;
209 	}
210 
211 	fclose (fp_in);
212 	fclose (fp_out);
213 	return EXIT_SUCCESS;
214 }
215