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