Skip to content

Instantly share code, notes, and snippets.

@ilyakurdyukov
Last active January 19, 2022 04:32
Show Gist options
  • Save ilyakurdyukov/db5e81b87a7be5ac38813104c111799f to your computer and use it in GitHub Desktop.
Save ilyakurdyukov/db5e81b87a7be5ac38813104c111799f to your computer and use it in GitHub Desktop.
Helper function for escaping special characters from user-provided strings to be used in calls to system().
#ifndef SAFESYS_H
#define SAFESYS_H
#include <stdlib.h>
#include <stdarg.h>
#ifndef SAFESYS_NOMEM
#include <errno.h>
#define SAFESYS_NOMEM ENOMEM
#endif
static int safesys(const char *format, ...) {
va_list va; size_t size = 0;
const char *next, *p, *f, *esc;
char *d, *cmd, c; int ret, nquot;
esc = "\t\n\r !\"#$&()*;<=>?[\\]^`{|}~";
va_start(va, format);
f = format;
while ((c = *f++)) if (c == '%') {
p = next = va_arg(va, const char*);
nquot = 0;
while ((c = *p++))
nquot = c == '\'' ?
size += nquot > 1 ? 3 : nquot + 1, 0 :
nquot + !!strchr(esc, c);
size += (nquot > 1 ? 2 : nquot) + (p - next) - 2;
}
va_end(va);
size += f - format;
cmd = malloc(size);
if (!cmd) return SAFESYS_NOMEM;
#define SAFESYS_COPY \
if (nquot > 1) *d++ = '\''; \
while (next < p - 1) { \
c = *next++; \
if (nquot == 1 && strchr(esc, c)) \
*d++ = '\\', nquot = 0; \
*d++ = c; \
} \
if (nquot > 1) *d++ = '\'';
d = cmd;
va_start(va, format);
f = format;
while ((c = *f++))
if (c != '%') *d++ = c;
else {
p = next = va_arg(va, const char*);
nquot = 0;
while ((c = *p++))
if (c == '\'') {
SAFESYS_COPY
nquot = 0; next++;
*d++ = '\\'; *d++ = '\'';
} else nquot += !!strchr(esc, c);
SAFESYS_COPY
}
va_end(va);
*d = 0;
puts(cmd);
ret = system(cmd);
free(cmd);
return ret;
}
#endif // SAFESYS_H
@ilyakurdyukov
Copy link
Author

ilyakurdyukov commented Jan 19, 2022

mkiocccentry does not require double quotes escaping, because if one wants to use $FOO on the command line to invoke mkiocccentry, those variables will be expanded by the shell before mkiocccentry is run.

@ilyakurdyukov
Copy link
Author

You might wish to have the code simply return a malloced string that is "safe" and let the caller pass it to system() or popen()

This can be done very simply:

-static int safesys(const char *format, ...) {
+static char* cmdprintf(const char *format, ...) {
 
-	if (!cmd) return SAFESYS_NOMEM;
+	if (!cmd) return cmd;
 
-	puts(cmd);
-	ret = system(cmd);
-	free(cmd);
-	return ret;
+	return cmd;

I just want to keep system() in this gist as an example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment