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
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
10Software Foundation; either version 2, or (at your option) any later
11version.
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
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 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
46extern char *program_name;	/* main program must define this */
47
48#undef FALSE
49#undef TRUE
50#define FALSE 0
51#define TRUE  1
52
53static int
54needs_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
86char *
87quote_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
189void
190purge_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 */
210