#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "list.h" #include "log.h" #include "stringop.h" static const char whitespace[] = " \f\n\r\t\v"; void strip_whitespace(char *str) { size_t len = strlen(str); size_t start = strspn(str, whitespace); memmove(str, &str[start], len + 1 - start); if (*str) { for (len -= start + 1; isspace(str[len]); --len) {} str[len + 1] = '\0'; } } void strip_quotes(char *str) { bool in_str = false; bool in_chr = false; bool escaped = false; char *end = strchr(str,0); while (*str) { if (*str == '\'' && !in_str && !escaped) { in_chr = !in_chr; goto shift_over; } else if (*str == '\"' && !in_chr && !escaped) { in_str = !in_str; goto shift_over; } else if (*str == '\\') { escaped = !escaped; ++str; continue; } escaped = false; ++str; continue; shift_over: memmove(str, str+1, end-- - str); } *end = '\0'; } char *lenient_strcat(char *dest, const char *src) { if (dest && src) { return strcat(dest, src); } return dest; } char *lenient_strncat(char *dest, const char *src, size_t len) { if (dest && src) { return strncat(dest, src, len); } return dest; } // strcmp that also handles null pointers. int lenient_strcmp(char *a, char *b) { if (a == b) { return 0; } else if (!a) { return -1; } else if (!b) { return 1; } else { return strcmp(a, b); } } list_t *split_string(const char *str, const char *delims) { list_t *res = create_list(); char *copy = strdup(str); char *token; token = strtok(copy, delims); while(token) { token = strdup(token); list_add(res, token); token = strtok(NULL, delims); } free(copy); return res; } char **split_args(const char *start, int *argc) { *argc = 0; int alloc = 2; char **argv = malloc(sizeof(char *) * alloc); bool in_token = false; bool in_string = false; bool in_char = false; bool in_brackets = false; // brackets are used for critera bool escaped = false; const char *end = start; if (start) { while (*start) { if (!in_token) { start = (end += strspn(end, whitespace)); in_token = true; } if (*end == '"' && !in_char && !escaped) { in_string = !in_string; } else if (*end == '\'' && !in_string && !escaped) { in_char = !in_char; } else if (*end == '[' && !in_string && !in_char && !in_brackets && !escaped) { in_brackets = true; } else if (*end == ']' && !in_string && !in_char && in_brackets && !escaped) { in_brackets = false; } else if (*end == '\\') { escaped = !escaped; } else if (*end == '\0' || (!in_string && !in_char && !in_brackets && !escaped && strchr(whitespace, *end))) { goto add_token; } if (*end != '\\') { escaped = false; } ++end; continue; add_token: if (end - start > 0) { char *token = malloc(end - start + 1); strncpy(token, start, end - start + 1); token[end - start] = '\0'; argv[*argc] = token; if (++*argc + 1 == alloc) { argv = realloc(argv, (alloc *= 2) * sizeof(char *)); } } in_token = false; escaped = false; } } argv[*argc] = NULL; return argv; } void free_argv(int argc, char **argv) { while (argc-- > 0) { free(argv[argc]); } free(argv); } int unescape_string(char *string) { /* TODO: More C string escapes */ int len = strlen(string); int i; for (i = 0; string[i]; ++i) { if (string[i] == '\\') { switch (string[++i]) { case '0': string[i - 1] = '\0'; return i - 1; case 'a': string[i - 1] = '\a'; string[i] = '\0'; break; case 'b': string[i - 1] = '\b'; string[i] = '\0'; break; case 'f': string[i - 1] = '\f'; string[i] = '\0'; break; case 'n': string[i - 1] = '\n'; string[i] = '\0'; break; case 'r': string[i - 1] = '\r'; string[i] = '\0'; break; case 't': string[i - 1] = '\t'; string[i] = '\0'; break; case 'v': string[i - 1] = '\v'; string[i] = '\0'; break; case '\\': string[i] = '\0'; break; case '\'': string[i - 1] = '\''; string[i] = '\0'; break; case '\"': string[i - 1] = '\"'; string[i] = '\0'; break; case '?': string[i - 1] = '?'; string[i] = '\0'; break; case 'x': { unsigned char c = 0; if (string[i+1] >= '0' && string[i+1] <= '9') { c = string[i+1] - '0'; if (string[i+2] >= '0' && string[i+2] <= '9') { c *= 0x10; c += string[i+2] - '0'; string[i+2] = '\0'; } string[i+1] = '\0'; } string[i] = '\0'; string[i - 1] = c; } } } } // Shift characters over nullspaces int shift = 0; for (i = 0; i < len; ++i) { if (string[i] == 0) { shift++; continue; } string[i-shift] = string[i]; } string[len - shift] = 0; return len - shift; } char *join_args(char **argv, int argc) { if (!sway_assert(argc > 0, "argc should be positive")) { return NULL; } int len = 0, i; for (i = 0; i < argc; ++i) { len += strlen(argv[i]) + 1; } char *res = malloc(len); len = 0; for (i = 0; i < argc; ++i) { strcpy(res + len, argv[i]); len += strlen(argv[i]); res[len++] = ' '; } res[len - 1] = '\0'; return res; } char *argsep(char **stringp, const char *delim) { char *start = *stringp; char *end = start; bool in_string = false; bool in_char = false; bool escaped = false; while (1) { if (*end == '"' && !in_char && !escaped) { in_string = !in_string; } else if (*end == '\'' && !in_string && !escaped) { in_char = !in_char; } else if (*end == '\\') { escaped = !escaped; } else if (*end == '\0') { *stringp = NULL; goto found; } else if (!in_string && !in_char && !escaped && strchr(delim, *end)) { if (end - start) { *(end++) = 0; *stringp = end + strspn(end, delim);; if (!**stringp) *stringp = NULL; goto found; } else { ++start; end = start; } } if (*end != '\\') { escaped = false; } ++end; } found: return start; }