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