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