Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / libs / libgroff / quotearg.c
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 */