libexplain
1.4.D001
|
00001 /* 00002 * libexplain - a library of system-call-specific strerror replacements 00003 * Copyright (C) 2013 Peter Miller 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU Lesser General Public License as 00007 * published by the Free Software Foundation; either version 3 of the 00008 * License, or (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 #include <libexplain/ac/ctype.h> 00020 00021 #include <libexplain/string_buffer.h> 00022 #include <libexplain/buffer/pointer.h> 00023 #include <libexplain/is_efault.h> 00024 00025 00026 void 00027 explain_string_buffer_puts_shell_quoted(explain_string_buffer_t *sb, 00028 const char *text) 00029 { 00030 const char *cp; 00031 int needs_quoting; 00032 unsigned char mode; 00033 unsigned char c; 00034 00035 if (!text) 00036 text = ""; 00037 00038 /* 00039 * Work out if the string needs quoting. 00040 * 00041 * The empty string is not quoted, even though it could be argued 00042 * that it needs to be. It has proven more useful in the present 00043 * form, because it allows empty filename lists to pass through 00044 * and remain empty. 00045 */ 00046 needs_quoting = 0; 00047 mode = 0; 00048 cp = text; 00049 for (;;) 00050 { 00051 c = *cp++; 00052 switch (c) 00053 { 00054 case 0: 00055 break; 00056 00057 default: 00058 if (isspace(c) || !isprint(c)) 00059 needs_quoting = 1; 00060 continue; 00061 00062 case '!': 00063 /* 00064 * special for bash and csh 00065 * 00066 * You can only quote exclamation marks within single 00067 * quotes. You can't escape them within double quotes. 00068 */ 00069 mode = '\''; 00070 needs_quoting = 1; 00071 break; 00072 00073 case '\'': 00074 /* 00075 * You can only quote single quote within double 00076 * quotes. You can't escape them within single quotes. 00077 */ 00078 mode = '"'; 00079 needs_quoting = 1; 00080 break; 00081 00082 case '"': 00083 case '#': 00084 case '$': 00085 case '&': 00086 case '(': 00087 case ')': 00088 case '*': 00089 case ':': 00090 case ';': 00091 case '<': 00092 case '=': 00093 case '>': 00094 case '?': 00095 case '[': 00096 case '\\': 00097 case ']': 00098 case '^': 00099 case '`': 00100 case '{': 00101 case '|': 00102 case '}': 00103 case '~': 00104 needs_quoting = 1; 00105 continue; 00106 } 00107 break; 00108 } 00109 00110 /* 00111 * If it doesn't need quoting, return immediately. 00112 */ 00113 if (!needs_quoting) 00114 { 00115 explain_string_buffer_puts(sb, text); 00116 return; 00117 } 00118 00119 /* 00120 * If we have a choice, use single quote mode, 00121 * it's shorter and easier to read. 00122 */ 00123 if (!mode) 00124 mode = '\''; 00125 explain_string_buffer_putc(sb, mode); 00126 00127 /* 00128 * Form the quoted string, using the minimum number of escapes. 00129 * 00130 * The gotcha here is the backquote: the `blah` substitution is 00131 * still active within double quotes. And so are a few others. 00132 * 00133 * Also, there are some difficulties: the single quote can't be 00134 * quoted within single quotes, and the exclamation mark can't 00135 * be quoted by anything *except* single quotes. Sheesh. 00136 * 00137 * Also, the rules change depending on which style of quoting 00138 * is in force at the time. 00139 */ 00140 cp = text; 00141 for (;;) 00142 { 00143 c = *cp++; 00144 if (!c) 00145 break; 00146 if (mode == '\'') 00147 { 00148 /* within single quotes */ 00149 if (c == '\'') 00150 { 00151 /* 00152 * You can't quote a single quote within 00153 * single quotes. Need to change to 00154 * double quote mode. 00155 */ 00156 explain_string_buffer_puts(sb, "'\"'"); 00157 mode = '"'; 00158 } 00159 else 00160 explain_string_buffer_putc(sb, c); 00161 } 00162 else 00163 { 00164 /* within double quotes */ 00165 switch (c) 00166 { 00167 case '!': 00168 /* 00169 * You can't quote an exclamation mark 00170 * within double quotes. Need to change 00171 * to single quote mode. 00172 */ 00173 explain_string_buffer_puts(sb, "\"'!"); 00174 mode = '\''; 00175 break; 00176 00177 case '\n': 00178 case '"': 00179 case '\\': 00180 case '`': /* stop command substitutions */ 00181 case '$': /* stop variable substitutions */ 00182 explain_string_buffer_putc(sb, '\\'); 00183 /* fall through... */ 00184 00185 default: 00186 explain_string_buffer_putc(sb, c); 00187 break; 00188 } 00189 } 00190 } 00191 explain_string_buffer_putc(sb, mode); 00192 } 00193 00194 00195 /* vim: set ts=8 sw=4 et : */