summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bfstd.c46
-rw-r--r--src/bfstd.h13
2 files changed, 59 insertions, 0 deletions
diff --git a/src/bfstd.c b/src/bfstd.c
index 4e1175f..cfff426 100644
--- a/src/bfstd.c
+++ b/src/bfstd.c
@@ -544,3 +544,49 @@ size_t xstrwidth(const char *str) {
return ret;
}
+
+char *wordesc(const char *str) {
+ size_t len = strlen(str);
+
+ if (strcspn(str, "|&;<>()$`\\\"' \t\n*?[#˜=%") == len) {
+ // Whole string is safe
+ // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02
+ if (len > 0) {
+ return strdup(str);
+ } else {
+ return strdup("\"\"");
+ }
+ } else if (strcspn(str, "`$\\\"") == len) {
+ // Safe to double-quote the whole string
+ // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_03
+ char *ret = malloc(len + 3);
+ if (!ret) {
+ return NULL;
+ }
+
+ char *cur = stpcpy(ret, "\"");
+ cur = stpcpy(cur, str);
+ cur = stpcpy(cur, "\"");
+ return ret;
+ }
+
+ // Every ' is replaced with '\'', so at most a 3x growth
+ char *ret = malloc(3 * len + 3);
+ if (!ret) {
+ return NULL;
+ }
+
+ char *cur = stpcpy(ret, "'");
+ while (*str) {
+ size_t chunk = strcspn(str, "'");
+ cur = stpncpy(cur, str, chunk);
+ str += chunk;
+ if (*str) {
+ cur = stpcpy(cur, "'\\''");
+ ++str;
+ }
+ }
+ cur = stpcpy(cur, "'");
+
+ return ret;
+}
diff --git a/src/bfstd.h b/src/bfstd.h
index ee4cf16..750847e 100644
--- a/src/bfstd.h
+++ b/src/bfstd.h
@@ -277,4 +277,17 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *
*/
size_t xstrwidth(const char *str);
+// #include <wordexp.h>
+
+/**
+ * Escape a string as a single shell word.
+ *
+ * @param str
+ * The string to escape.
+ * @return
+ * A string that a shell would evaluate to str, dynamically allocated,
+ * or NULL on failure.
+ */
+char *wordesc(const char *str);
+
#endif // BFS_BFSTD_H