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