1184610Salfred/* Copyright (C) 2004
2184610Salfred   Free Software Foundation, Inc.
3184610Salfred     Written by:  Jeff Conrad    (jeff_conrad@msn.com)
4184610Salfred       and        Keith Marshall (keith.d.marshall@ntlworld.com)
5184610Salfred
6184610SalfredThis file is part of groff.
7184610Salfred
8184610Salfredgroff is free software; you can redistribute it and/or modify it under
9184610Salfredthe terms of the GNU General Public License as published by the Free
10184610SalfredSoftware Foundation; either version 2, or (at your option) any later
11184610Salfredversion.
12184610Salfred
13184610Salfredgroff is distributed in the hope that it will be useful, but WITHOUT ANY
14184610SalfredWARRANTY; without even the implied warranty of MERCHANTABILITY or
15184610SalfredFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16184610Salfredfor more details.
17184610Salfred
18184610SalfredYou should have received a copy of the GNU General Public License along
19184610Salfredwith groff; see the file COPYING.  If not, write to the Free Software
20184610SalfredFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21184610Salfred
22184610Salfred#include <stdio.h>
23184610Salfred#include <stdlib.h>
24184610Salfred#include <string.h>
25184610Salfred#include <ctype.h>
26184610Salfred#include <limits.h>
27184610Salfred
28184610Salfred/* Define the default mechanism, and messages, for error reporting
29184610Salfred * (user may substitute a preferred alternative, by defining his own
30184610Salfred *  implementation of the macros REPORT_ERROR, QUOTE_ARG_MALLOC_FAILED
31184610Salfred *  and QUOTE_ARG_REALLOC_FAILED, in the header file `nonposix.h').
32184610Salfred */
33184610Salfred
34184610Salfred#include "nonposix.h"
35184610Salfred
36184610Salfred#ifndef  REPORT_ERROR
37184610Salfred# define REPORT_ERROR(WHY)  fprintf(stderr, "%s:%s\n", program_name, WHY)
38184610Salfred#endif
39184610Salfred#ifndef  QUOTE_ARG_MALLOC_ERROR
40184610Salfred# define QUOTE_ARG_MALLOC_ERROR   "malloc: Buffer allocation failed"
41184610Salfred#endif
42184610Salfred#ifndef  QUOTE_ARG_REALLOC_ERROR
43184610Salfred# define QUOTE_ARG_REALLOC_ERROR  "realloc: Buffer resize failed"
44184610Salfred#endif
45184610Salfred
46184610Salfredextern char *program_name;	/* main program must define this */
47188942Sthompsa
48188942Sthompsa#undef FALSE
49188942Sthompsa#undef TRUE
50188942Sthompsa#define FALSE 0
51184610Salfred#define TRUE  1
52184610Salfred
53184610Salfredstatic int
54188942Sthompsaneeds_quoting(const char *string)
55188942Sthompsa{
56188942Sthompsa  /* Scan `string' to see whether it needs quoting for MSVC `spawn'/`exec'
57188942Sthompsa   * (i.e., whether it contains whitespace or embedded quotes).
58188942Sthompsa   */
59188942Sthompsa
60188942Sthompsa  if (string == NULL)		/* ignore NULL strings */
61188942Sthompsa    return FALSE;
62188942Sthompsa
63184610Salfred  if (*string == '\0')		/* explicit arguments of zero length	  */
64188942Sthompsa    return TRUE;		/* need quoting, so they aren't discarded */
65188942Sthompsa
66188942Sthompsa  while (*string) {
67184610Salfred    /* Scan non-NULL strings, up to '\0' terminator,
68184610Salfred     * returning 'TRUE' if quote or white space found.
69184610Salfred     */
70184610Salfred
71184610Salfred    if (*string == '"' || isspace(*string))
72184610Salfred      return TRUE;
73184610Salfred
74184610Salfred    /* otherwise, continue scanning to end of string */
75184610Salfred
76184610Salfred    ++string;
77184610Salfred  }
78184610Salfred
79184610Salfred  /* Fall through, if no quotes or white space found,
80184610Salfred   * in which case, return `FALSE'.
81184610Salfred   */
82184610Salfred
83184610Salfred  return FALSE;
84184610Salfred}
85184610Salfred
86184610Salfredchar *
87184610Salfredquote_arg(char *string)
88184610Salfred{
89184610Salfred  /* Enclose arguments in double quotes so that the parsing done in the
90184610Salfred   * MSVC runtime startup code doesn't split them at whitespace.  Escape
91184610Salfred   * embedded double quotes so that they emerge intact from the parsing.
92184610Salfred   */
93184610Salfred
94184610Salfred  int backslashes;
95184610Salfred  char *quoted, *p, *q;
96184610Salfred
97184610Salfred  if (needs_quoting(string)) {
98184610Salfred    /* Need to create a quoted copy of `string';
99185948Sthompsa     * maximum buffer space needed is twice the original length,
100185948Sthompsa     * plus two enclosing quotes and one `\0' terminator.
101185948Sthompsa     */
102185948Sthompsa
103184610Salfred    if ((quoted = (char *)malloc(2 * strlen(string) + 3)) == NULL) {
104184610Salfred      /* Couldn't get a buffer for the quoted string,
105184610Salfred       * so complain, and bail out gracefully.
106184610Salfred       */
107184610Salfred
108184610Salfred      REPORT_ERROR(QUOTE_ARG_MALLOC_ERROR);
109184610Salfred      exit(1);
110184610Salfred    }
111184610Salfred
112184610Salfred    /* Ok to proceed:
113184610Salfred     * insert the opening quote, then copy the source string,
114184610Salfred     * adding escapes as required.
115184610Salfred     */
116184610Salfred
117184610Salfred    *quoted = '"';
118184610Salfred    for (backslashes = 0, p = string, q = quoted; *p; p++) {
119184610Salfred      if (*p == '\\') {
120184610Salfred	/* Just count backslashes when we find them.
121184610Salfred	 * We will copy them out later, when we know if the count
122184610Salfred	 * needs to be adjusted, to escape an embedded quote.
123184610Salfred	 */
124184610Salfred
125184610Salfred	++backslashes;
126184610Salfred      }
127184610Salfred      else if (*p == '"') {
128184610Salfred	/* This embedded quote character must be escaped,
129184610Salfred	 * but first double up any immediately preceding backslashes,
130184610Salfred	 * with one extra, as the escape character.
131184610Salfred	 */
132184610Salfred
133184610Salfred	for (backslashes += backslashes + 1; backslashes; backslashes--)
134184610Salfred	  *++q = '\\';
135184610Salfred
136184610Salfred	/* and now, add the quote character itself */
137184610Salfred
138184610Salfred	*++q = '"';
139184610Salfred      }
140184610Salfred      else {
141184610Salfred	/* Any other character is simply copied,
142184610Salfred	 * but first, if we have any pending backslashes,
143184610Salfred	 * we must now insert them, without any count adjustment.
144184610Salfred	 */
145184610Salfred
146184610Salfred	while (backslashes) {
147184610Salfred	  *++q = '\\';
148184610Salfred	  --backslashes;
149184610Salfred	}
150184610Salfred
151184610Salfred	/* and then, copy the current character */
152184610Salfred
153184610Salfred	*++q = *p;
154184610Salfred      }
155184610Salfred    }
156184610Salfred
157184610Salfred    /* At end of argument:
158184610Salfred     * If any backslashes remain to be copied out, append them now,
159184610Salfred     * doubling the actual count to protect against reduction by MSVC,
160184610Salfred     * as a consequence of the immediately following closing quote.
161184610Salfred     */
162184610Salfred
163184610Salfred    for (backslashes += backslashes; backslashes; backslashes--)
164184610Salfred      *++q = '\\';
165184610Salfred
166184610Salfred    /* Finally,
167184610Salfred     * add the closing quote, terminate the quoted string,
168184610Salfred     * and adjust its size to what was actually required,
169184610Salfred     * ready for return.
170184610Salfred     */
171184610Salfred
172184610Salfred    *++q = '"';
173184610Salfred    *++q = '\0';
174184610Salfred    if ((string = (char *)realloc(quoted, strlen(quoted) + 1)) == NULL) {
175184610Salfred      /* but bail out gracefully, on error */
176184610Salfred
177184610Salfred      REPORT_ERROR(QUOTE_ARG_REALLOC_ERROR);
178184610Salfred      exit(1);
179184610Salfred    }
180184610Salfred  }
181184610Salfred
182184610Salfred  /* `string' now refers to the argument,
183184610Salfred   * quoted and escaped, as required.
184184610Salfred   */
185184610Salfred
186184610Salfred  return string;
187184610Salfred}
188184610Salfred
189184610Salfredvoid
190184610Salfredpurge_quoted_args(char **argv)
191184610Salfred{
192184610Salfred  /* To avoid memory leaks,
193184610Salfred   * free all memory previously allocated by `quoted_arg()',
194184610Salfred   * within the scope of the referring argument vector, `argv'.
195184610Salfred   */
196184610Salfred
197184610Salfred  if (argv)
198184610Salfred    while (*argv) {
199184610Salfred      /* Any argument beginning with a double quote
200184610Salfred       * SHOULD have been allocated by `quoted_arg()'.
201184610Salfred       */
202184610Salfred
203184610Salfred      if (**argv == '"')
204184610Salfred        free( *argv );		/* so free its allocation */
205184610Salfred      ++argv;			/* and continue to the next argument */
206184610Salfred    }
207184610Salfred}
208184610Salfred
209184610Salfred/* quotearg.c: end of file */
210184610Salfred