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