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