1/*	$NetBSD: quotearg.c,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $	*/
2
3/* Copyright (C) 2004
4   Free Software Foundation, Inc.
5     Written by:  Jeff Conrad    (jeff_conrad@msn.com)
6       and        Keith Marshall (keith.d.marshall@ntlworld.com)
7
8This file is part of groff.
9
10groff is free software; you can redistribute it and/or modify it under
11the terms of the GNU General Public License as published by the Free
12Software Foundation; either version 2, or (at your option) any later
13version.
14
15groff is distributed in the hope that it will be useful, but WITHOUT ANY
16WARRANTY; without even the implied warranty of MERCHANTABILITY or
17FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18for more details.
19
20You should have received a copy of the GNU General Public License along
21with groff; see the file COPYING.  If not, write to the Free Software
22Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <ctype.h>
28#include <limits.h>
29
30/* Define the default mechanism, and messages, for error reporting
31 * (user may substitute a preferred alternative, by defining his own
32 *  implementation of the macros REPORT_ERROR, QUOTE_ARG_MALLOC_FAILED
33 *  and QUOTE_ARG_REALLOC_FAILED, in the header file `nonposix.h').
34 */
35
36#include "nonposix.h"
37
38#ifndef  REPORT_ERROR
39# define REPORT_ERROR(WHY)  fprintf(stderr, "%s:%s\n", program_name, WHY)
40#endif
41#ifndef  QUOTE_ARG_MALLOC_ERROR
42# define QUOTE_ARG_MALLOC_ERROR   "malloc: Buffer allocation failed"
43#endif
44#ifndef  QUOTE_ARG_REALLOC_ERROR
45# define QUOTE_ARG_REALLOC_ERROR  "realloc: Buffer resize failed"
46#endif
47
48extern char *program_name;	/* main program must define this */
49
50#undef FALSE
51#undef TRUE
52#define FALSE 0
53#define TRUE  1
54
55static int
56needs_quoting(const char *string)
57{
58  /* Scan `string' to see whether it needs quoting for MSVC `spawn'/`exec'
59   * (i.e., whether it contains whitespace or embedded quotes).
60   */
61
62  if (string == NULL)		/* ignore NULL strings */
63    return FALSE;
64
65  if (*string == '\0')		/* explicit arguments of zero length	  */
66    return TRUE;		/* need quoting, so they aren't discarded */
67
68  while (*string) {
69    /* Scan non-NULL strings, up to '\0' terminator,
70     * returning 'TRUE' if quote or white space found.
71     */
72
73    if (*string == '"' || isspace(*string))
74      return TRUE;
75
76    /* otherwise, continue scanning to end of string */
77
78    ++string;
79  }
80
81  /* Fall through, if no quotes or white space found,
82   * in which case, return `FALSE'.
83   */
84
85  return FALSE;
86}
87
88char *
89quote_arg(char *string)
90{
91  /* Enclose arguments in double quotes so that the parsing done in the
92   * MSVC runtime startup code doesn't split them at whitespace.  Escape
93   * embedded double quotes so that they emerge intact from the parsing.
94   */
95
96  int backslashes;
97  char *quoted, *p, *q;
98
99  if (needs_quoting(string)) {
100    /* Need to create a quoted copy of `string';
101     * maximum buffer space needed is twice the original length,
102     * plus two enclosing quotes and one `\0' terminator.
103     */
104
105    if ((quoted = (char *)malloc(2 * strlen(string) + 3)) == NULL) {
106      /* Couldn't get a buffer for the quoted string,
107       * so complain, and bail out gracefully.
108       */
109
110      REPORT_ERROR(QUOTE_ARG_MALLOC_ERROR);
111      exit(1);
112    }
113
114    /* Ok to proceed:
115     * insert the opening quote, then copy the source string,
116     * adding escapes as required.
117     */
118
119    *quoted = '"';
120    for (backslashes = 0, p = string, q = quoted; *p; p++) {
121      if (*p == '\\') {
122	/* Just count backslashes when we find them.
123	 * We will copy them out later, when we know if the count
124	 * needs to be adjusted, to escape an embedded quote.
125	 */
126
127	++backslashes;
128      }
129      else if (*p == '"') {
130	/* This embedded quote character must be escaped,
131	 * but first double up any immediately preceding backslashes,
132	 * with one extra, as the escape character.
133	 */
134
135	for (backslashes += backslashes + 1; backslashes; backslashes--)
136	  *++q = '\\';
137
138	/* and now, add the quote character itself */
139
140	*++q = '"';
141      }
142      else {
143	/* Any other character is simply copied,
144	 * but first, if we have any pending backslashes,
145	 * we must now insert them, without any count adjustment.
146	 */
147
148	while (backslashes) {
149	  *++q = '\\';
150	  --backslashes;
151	}
152
153	/* and then, copy the current character */
154
155	*++q = *p;
156      }
157    }
158
159    /* At end of argument:
160     * If any backslashes remain to be copied out, append them now,
161     * doubling the actual count to protect against reduction by MSVC,
162     * as a consequence of the immediately following closing quote.
163     */
164
165    for (backslashes += backslashes; backslashes; backslashes--)
166      *++q = '\\';
167
168    /* Finally,
169     * add the closing quote, terminate the quoted string,
170     * and adjust its size to what was actually required,
171     * ready for return.
172     */
173
174    *++q = '"';
175    *++q = '\0';
176    if ((string = (char *)realloc(quoted, strlen(quoted) + 1)) == NULL) {
177      /* but bail out gracefully, on error */
178
179      REPORT_ERROR(QUOTE_ARG_REALLOC_ERROR);
180      exit(1);
181    }
182  }
183
184  /* `string' now refers to the argument,
185   * quoted and escaped, as required.
186   */
187
188  return string;
189}
190
191void
192purge_quoted_args(char **argv)
193{
194  /* To avoid memory leaks,
195   * free all memory previously allocated by `quoted_arg()',
196   * within the scope of the referring argument vector, `argv'.
197   */
198
199  if (argv)
200    while (*argv) {
201      /* Any argument beginning with a double quote
202       * SHOULD have been allocated by `quoted_arg()'.
203       */
204
205      if (**argv == '"')
206        free( *argv );		/* so free its allocation */
207      ++argv;			/* and continue to the next argument */
208    }
209}
210
211/* quotearg.c: end of file */
212