libexplain  1.4.D001
libexplain/string_buffer/puts_shell_quoted.c
Go to the documentation of this file.
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 : */