1 |
/* Copyright (C) 2004 |
2 |
Free Software Foundation, Inc. |
3 |
Written by: Jeff Conrad (jeff_conrad@msn.com) |
4 |
and Keith Marshall (keith.d.marshall@ntlworld.com) |
5 |
|
6 |
This file is part of groff. |
7 |
|
8 |
groff is free software; you can redistribute it and/or modify it under |
9 |
the terms of the GNU General Public License as published by the Free |
10 |
Software Foundation; either version 2, or (at your option) any later |
11 |
version. |
12 |
|
13 |
groff is distributed in the hope that it will be useful, but WITHOUT ANY |
14 |
WARRANTY; without even the implied warranty of MERCHANTABILITY or |
15 |
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
16 |
for more details. |
17 |
|
18 |
You should have received a copy of the GNU General Public License along |
19 |
with groff; see the file COPYING. If not, write to the Free Software |
20 |
Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ |
21 |
|
22 |
#include <stdio.h> |
23 |
#include <stdlib.h> |
24 |
#include <string.h> |
25 |
#include <ctype.h> |
26 |
#include <limits.h> |
27 |
|
28 |
/* Define the default mechanism, and messages, for error reporting |
29 |
* (user may substitute a preferred alternative, by defining his own |
30 |
* implementation of the macros REPORT_ERROR, QUOTE_ARG_MALLOC_FAILED |
31 |
* and QUOTE_ARG_REALLOC_FAILED, in the header file `nonposix.h'). |
32 |
*/ |
33 |
|
34 |
#include "nonposix.h" |
35 |
|
36 |
#ifndef REPORT_ERROR |
37 |
# define REPORT_ERROR(WHY) fprintf(stderr, "%s:%s\n", program_name, WHY) |
38 |
#endif |
39 |
#ifndef QUOTE_ARG_MALLOC_ERROR |
40 |
# define QUOTE_ARG_MALLOC_ERROR "malloc: Buffer allocation failed" |
41 |
#endif |
42 |
#ifndef QUOTE_ARG_REALLOC_ERROR |
43 |
# define QUOTE_ARG_REALLOC_ERROR "realloc: Buffer resize failed" |
44 |
#endif |
45 |
|
46 |
extern char *program_name; /* main program must define this */ |
47 |
|
48 |
#undef FALSE |
49 |
#undef TRUE |
50 |
#define FALSE 0 |
51 |
#define TRUE 1 |
52 |
|
53 |
static int |
54 |
needs_quoting(const char *string) |
55 |
{ |
56 |
/* Scan `string' to see whether it needs quoting for MSVC `spawn'/`exec' |
57 |
* (i.e., whether it contains whitespace or embedded quotes). |
58 |
*/ |
59 |
|
60 |
if (string == NULL) /* ignore NULL strings */ |
61 |
return FALSE; |
62 |
|
63 |
if (*string == '\0') /* explicit arguments of zero length */ |
64 |
return TRUE; /* need quoting, so they aren't discarded */ |
65 |
|
66 |
while (*string) { |
67 |
/* Scan non-NULL strings, up to '\0' terminator, |
68 |
* returning 'TRUE' if quote or white space found. |
69 |
*/ |
70 |
|
71 |
if (*string == '"' || isspace(*string)) |
72 |
return TRUE; |
73 |
|
74 |
/* otherwise, continue scanning to end of string */ |
75 |
|
76 |
++string; |
77 |
} |
78 |
|
79 |
/* Fall through, if no quotes or white space found, |
80 |
* in which case, return `FALSE'. |
81 |
*/ |
82 |
|
83 |
return FALSE; |
84 |
} |
85 |
|
86 |
char * |
87 |
quote_arg(char *string) |
88 |
{ |
89 |
/* Enclose arguments in double quotes so that the parsing done in the |
90 |
* MSVC runtime startup code doesn't split them at whitespace. Escape |
91 |
* embedded double quotes so that they emerge intact from the parsing. |
92 |
*/ |
93 |
|
94 |
int backslashes; |
95 |
char *quoted, *p, *q; |
96 |
|
97 |
if (needs_quoting(string)) { |
98 |
/* Need to create a quoted copy of `string'; |
99 |
* maximum buffer space needed is twice the original length, |
100 |
* plus two enclosing quotes and one `\0' terminator. |
101 |
*/ |
102 |
|
103 |
if ((quoted = (char *)malloc(2 * strlen(string) + 3)) == NULL) { |
104 |
/* Couldn't get a buffer for the quoted string, |
105 |
* so complain, and bail out gracefully. |
106 |
*/ |
107 |
|
108 |
REPORT_ERROR(QUOTE_ARG_MALLOC_ERROR); |
109 |
exit(1); |
110 |
} |
111 |
|
112 |
/* Ok to proceed: |
113 |
* insert the opening quote, then copy the source string, |
114 |
* adding escapes as required. |
115 |
*/ |
116 |
|
117 |
*quoted = '"'; |
118 |
for (backslashes = 0, p = string, q = quoted; *p; p++) { |
119 |
if (*p == '\\') { |
120 |
/* Just count backslashes when we find them. |
121 |
* We will copy them out later, when we know if the count |
122 |
* needs to be adjusted, to escape an embedded quote. |
123 |
*/ |
124 |
|
125 |
++backslashes; |
126 |
} |
127 |
else if (*p == '"') { |
128 |
/* This embedded quote character must be escaped, |
129 |
* but first double up any immediately preceding backslashes, |
130 |
* with one extra, as the escape character. |
131 |
*/ |
132 |
|
133 |
for (backslashes += backslashes + 1; backslashes; backslashes--) |
134 |
*++q = '\\'; |
135 |
|
136 |
/* and now, add the quote character itself */ |
137 |
|
138 |
*++q = '"'; |
139 |
} |
140 |
else { |
141 |
/* Any other character is simply copied, |
142 |
* but first, if we have any pending backslashes, |
143 |
* we must now insert them, without any count adjustment. |
144 |
*/ |
145 |
|
146 |
while (backslashes) { |
147 |
*++q = '\\'; |
148 |
--backslashes; |
149 |
} |
150 |
|
151 |
/* and then, copy the current character */ |
152 |
|
153 |
*++q = *p; |
154 |
} |
155 |
} |
156 |
|
157 |
/* At end of argument: |
158 |
* If any backslashes remain to be copied out, append them now, |
159 |
* doubling the actual count to protect against reduction by MSVC, |
160 |
* as a consequence of the immediately following closing quote. |
161 |
*/ |
162 |
|
163 |
for (backslashes += backslashes; backslashes; backslashes--) |
164 |
*++q = '\\'; |
165 |
|
166 |
/* Finally, |
167 |
* add the closing quote, terminate the quoted string, |
168 |
* and adjust its size to what was actually required, |
169 |
* ready for return. |
170 |
*/ |
171 |
|
172 |
*++q = '"'; |
173 |
*++q = '\0'; |
174 |
if ((string = (char *)realloc(quoted, strlen(quoted) + 1)) == NULL) { |
175 |
/* but bail out gracefully, on error */ |
176 |
|
177 |
REPORT_ERROR(QUOTE_ARG_REALLOC_ERROR); |
178 |
exit(1); |
179 |
} |
180 |
} |
181 |
|
182 |
/* `string' now refers to the argument, |
183 |
* quoted and escaped, as required. |
184 |
*/ |
185 |
|
186 |
return string; |
187 |
} |
188 |
|
189 |
void |
190 |
purge_quoted_args(char **argv) |
191 |
{ |
192 |
/* To avoid memory leaks, |
193 |
* free all memory previously allocated by `quoted_arg()', |
194 |
* within the scope of the referring argument vector, `argv'. |
195 |
*/ |
196 |
|
197 |
if (argv) |
198 |
while (*argv) { |
199 |
/* Any argument beginning with a double quote |
200 |
* SHOULD have been allocated by `quoted_arg()'. |
201 |
*/ |
202 |
|
203 |
if (**argv == '"') |
204 |
free( *argv ); /* so free its allocation */ |
205 |
++argv; /* and continue to the next argument */ |
206 |
} |
207 |
} |
208 |
|
209 |
/* quotearg.c: end of file */ |