1151497Sru/* Copyright (C) 2004
2151497Sru   Free Software Foundation, Inc.
3151497Sru     Written by:  Jeff Conrad    (jeff_conrad@msn.com)
4151497Sru       and        Keith Marshall (keith.d.marshall@ntlworld.com)
5151497Sru
6151497SruThis file is part of groff.
7151497Sru
8151497Srugroff is free software; you can redistribute it and/or modify it under
9151497Sruthe terms of the GNU General Public License as published by the Free
10151497SruSoftware Foundation; either version 2, or (at your option) any later
11151497Sruversion.
12151497Sru
13151497Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14151497SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15151497SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16151497Srufor more details.
17151497Sru
18151497SruYou should have received a copy of the GNU General Public License along
19151497Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21151497Sru
22151497Sru#include <stdio.h>
23151497Sru#include <stdlib.h>
24151497Sru#include <string.h>
25151497Sru#include <ctype.h>
26151497Sru#include <limits.h>
27151497Sru
28151497Sru/* Define the default mechanism, and messages, for error reporting
29151497Sru * (user may substitute a preferred alternative, by defining his own
30151497Sru *  implementation of the macros REPORT_ERROR, QUOTE_ARG_MALLOC_FAILED
31151497Sru *  and QUOTE_ARG_REALLOC_FAILED, in the header file `nonposix.h').
32151497Sru */
33151497Sru
34151497Sru#include "nonposix.h"
35151497Sru
36151497Sru#ifndef  REPORT_ERROR
37151497Sru# define REPORT_ERROR(WHY)  fprintf(stderr, "%s:%s\n", program_name, WHY)
38151497Sru#endif
39151497Sru#ifndef  QUOTE_ARG_MALLOC_ERROR
40151497Sru# define QUOTE_ARG_MALLOC_ERROR   "malloc: Buffer allocation failed"
41151497Sru#endif
42151497Sru#ifndef  QUOTE_ARG_REALLOC_ERROR
43151497Sru# define QUOTE_ARG_REALLOC_ERROR  "realloc: Buffer resize failed"
44151497Sru#endif
45151497Sru
46151497Sruextern char *program_name;	/* main program must define this */
47151497Sru
48151497Sru#undef FALSE
49151497Sru#undef TRUE
50151497Sru#define FALSE 0
51151497Sru#define TRUE  1
52151497Sru
53151497Srustatic int
54151497Sruneeds_quoting(const char *string)
55151497Sru{
56151497Sru  /* Scan `string' to see whether it needs quoting for MSVC `spawn'/`exec'
57151497Sru   * (i.e., whether it contains whitespace or embedded quotes).
58151497Sru   */
59151497Sru
60151497Sru  if (string == NULL)		/* ignore NULL strings */
61151497Sru    return FALSE;
62151497Sru
63151497Sru  if (*string == '\0')		/* explicit arguments of zero length	  */
64151497Sru    return TRUE;		/* need quoting, so they aren't discarded */
65151497Sru
66151497Sru  while (*string) {
67151497Sru    /* Scan non-NULL strings, up to '\0' terminator,
68151497Sru     * returning 'TRUE' if quote or white space found.
69151497Sru     */
70151497Sru
71151497Sru    if (*string == '"' || isspace(*string))
72151497Sru      return TRUE;
73151497Sru
74151497Sru    /* otherwise, continue scanning to end of string */
75151497Sru
76151497Sru    ++string;
77151497Sru  }
78151497Sru
79151497Sru  /* Fall through, if no quotes or white space found,
80151497Sru   * in which case, return `FALSE'.
81151497Sru   */
82151497Sru
83151497Sru  return FALSE;
84151497Sru}
85151497Sru
86151497Sruchar *
87151497Sruquote_arg(char *string)
88151497Sru{
89151497Sru  /* Enclose arguments in double quotes so that the parsing done in the
90151497Sru   * MSVC runtime startup code doesn't split them at whitespace.  Escape
91151497Sru   * embedded double quotes so that they emerge intact from the parsing.
92151497Sru   */
93151497Sru
94151497Sru  int backslashes;
95151497Sru  char *quoted, *p, *q;
96151497Sru
97151497Sru  if (needs_quoting(string)) {
98151497Sru    /* Need to create a quoted copy of `string';
99151497Sru     * maximum buffer space needed is twice the original length,
100151497Sru     * plus two enclosing quotes and one `\0' terminator.
101151497Sru     */
102151497Sru
103151497Sru    if ((quoted = (char *)malloc(2 * strlen(string) + 3)) == NULL) {
104151497Sru      /* Couldn't get a buffer for the quoted string,
105151497Sru       * so complain, and bail out gracefully.
106151497Sru       */
107151497Sru
108151497Sru      REPORT_ERROR(QUOTE_ARG_MALLOC_ERROR);
109151497Sru      exit(1);
110151497Sru    }
111151497Sru
112151497Sru    /* Ok to proceed:
113151497Sru     * insert the opening quote, then copy the source string,
114151497Sru     * adding escapes as required.
115151497Sru     */
116151497Sru
117151497Sru    *quoted = '"';
118151497Sru    for (backslashes = 0, p = string, q = quoted; *p; p++) {
119151497Sru      if (*p == '\\') {
120151497Sru	/* Just count backslashes when we find them.
121151497Sru	 * We will copy them out later, when we know if the count
122151497Sru	 * needs to be adjusted, to escape an embedded quote.
123151497Sru	 */
124151497Sru
125151497Sru	++backslashes;
126151497Sru      }
127151497Sru      else if (*p == '"') {
128151497Sru	/* This embedded quote character must be escaped,
129151497Sru	 * but first double up any immediately preceding backslashes,
130151497Sru	 * with one extra, as the escape character.
131151497Sru	 */
132151497Sru
133151497Sru	for (backslashes += backslashes + 1; backslashes; backslashes--)
134151497Sru	  *++q = '\\';
135151497Sru
136151497Sru	/* and now, add the quote character itself */
137151497Sru
138151497Sru	*++q = '"';
139151497Sru      }
140151497Sru      else {
141151497Sru	/* Any other character is simply copied,
142151497Sru	 * but first, if we have any pending backslashes,
143151497Sru	 * we must now insert them, without any count adjustment.
144151497Sru	 */
145151497Sru
146151497Sru	while (backslashes) {
147151497Sru	  *++q = '\\';
148151497Sru	  --backslashes;
149151497Sru	}
150151497Sru
151151497Sru	/* and then, copy the current character */
152151497Sru
153151497Sru	*++q = *p;
154151497Sru      }
155151497Sru    }
156151497Sru
157151497Sru    /* At end of argument:
158151497Sru     * If any backslashes remain to be copied out, append them now,
159151497Sru     * doubling the actual count to protect against reduction by MSVC,
160151497Sru     * as a consequence of the immediately following closing quote.
161151497Sru     */
162151497Sru
163151497Sru    for (backslashes += backslashes; backslashes; backslashes--)
164151497Sru      *++q = '\\';
165151497Sru
166151497Sru    /* Finally,
167151497Sru     * add the closing quote, terminate the quoted string,
168151497Sru     * and adjust its size to what was actually required,
169151497Sru     * ready for return.
170151497Sru     */
171151497Sru
172151497Sru    *++q = '"';
173151497Sru    *++q = '\0';
174151497Sru    if ((string = (char *)realloc(quoted, strlen(quoted) + 1)) == NULL) {
175151497Sru      /* but bail out gracefully, on error */
176151497Sru
177151497Sru      REPORT_ERROR(QUOTE_ARG_REALLOC_ERROR);
178151497Sru      exit(1);
179151497Sru    }
180151497Sru  }
181151497Sru
182151497Sru  /* `string' now refers to the argument,
183151497Sru   * quoted and escaped, as required.
184151497Sru   */
185151497Sru
186151497Sru  return string;
187151497Sru}
188151497Sru
189151497Sruvoid
190151497Srupurge_quoted_args(char **argv)
191151497Sru{
192151497Sru  /* To avoid memory leaks,
193151497Sru   * free all memory previously allocated by `quoted_arg()',
194151497Sru   * within the scope of the referring argument vector, `argv'.
195151497Sru   */
196151497Sru
197151497Sru  if (argv)
198151497Sru    while (*argv) {
199151497Sru      /* Any argument beginning with a double quote
200151497Sru       * SHOULD have been allocated by `quoted_arg()'.
201151497Sru       */
202151497Sru
203151497Sru      if (**argv == '"')
204151497Sru        free( *argv );		/* so free its allocation */
205151497Sru      ++argv;			/* and continue to the next argument */
206151497Sru    }
207151497Sru}
208151497Sru
209151497Sru/* quotearg.c: end of file */
210