Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / groff / src / libs / libgroff / quotearg.c
1 /* Copyright (C) 2004, 2009
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 3 of the License, or
11 (at your option) any later 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
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
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
45 extern char *program_name;      /* main program must define this */
46
47 #undef FALSE
48 #undef TRUE
49 #define FALSE 0
50 #define TRUE  1
51
52 static int
53 needs_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       
85 char *
86 quote_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
188 void
189 purge_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 */