Files
kovidgoyal-kitty/kitty/launcher/cmdline.c

111 lines
3.1 KiB
C

/*
* cmdline.c
* Copyright (C) 2025 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#define _POSIX_C_SOURCE 200809L
#include "shlex.h"
#include "utils.h"
#include "launcher.h"
#ifdef __APPLE__
#include <os/log.h>
#endif
bool
append_arg_to_argv_array(argv_array *a, const char *arg) {
if (a->count + 2 > a->capacity) {
size_t cap = a->capacity * 2;
if (!cap) cap = 256;
void *m = realloc(a->argv, cap * sizeof(a->argv[0]));
if (!m) return false;
a->argv = m;
a->capacity = cap;
a->needs_free = true;
}
// arg points into the process's original argv which persists for the lifetime
// of the process and is never modified through this pointer.
a->argv[a->count++] = (char*)arg;
a->argv[a->count] = 0;
return true;
}
void
free_argv_array(argv_array *a) {
if (a && a->needs_free) {
free(a->buf); free(a->argv);
*a = (argv_array){0};
}
}
static bool
add_to_argv(argv_array *a, const char* arg, size_t sz) {
if (a->count + 2 > a->capacity) {
size_t cap = a->capacity * 2;
if (!cap) cap = 256;
void *m = realloc(a->argv, cap * sizeof(a->argv[0]));
if (!m) return false;
a->argv = m;
a->argv[a->count] = 0;
a->capacity = cap;
a->needs_free = true;
}
memcpy(a->buf + a->pos, arg, sz);
a->argv[a->count++] = a->buf + a->pos;
a->argv[a->count] = 0;
a->pos += sz;
a->buf[a->pos++] = 0;
return true;
}
bool
get_argv_from(const char *filename, const char *argv0, argv_array *final_ans) {
(void)get_config_dir;
if (!filename || !filename[0]) return true;
size_t src_sz;
char* src = read_full_file(filename, &src_sz);
if (!src) {
if (errno == ENOENT || errno == ENOTDIR) return true;
#ifdef __APPLE__
int saved = errno;
os_log_error(OS_LOG_DEFAULT, "Failed to read from %{public}s with error: %{darwin.errno}d", filename, errno);
errno = saved;
#endif
fprintf(stderr, "Failed to read from %s ", filename); perror("with error");
return true;
}
ShlexState s = {0};
argv_array ans = {0};
bool ok = false;
ans.buf = malloc(src_sz + strlen(argv0) + 64);
if (!ans.buf) goto oom;
ans.needs_free = true;
if (!add_to_argv(&ans, argv0, strlen(argv0))) goto oom;
if (!alloc_shlex_state(&s, src, src_sz, false)) goto oom;
bool keep_going = true;
while (keep_going) {
ssize_t q = next_word(&s);
switch(q) {
case -1: fprintf(stderr, "Failed to parse %s with error: %s\n", filename, s.err); goto end;
case -2: keep_going = false; break;
default:
if (ans.count == 1 && strcmp(s.buf, "kitty") == 0) continue;
if (!add_to_argv(&ans, s.buf, q)) goto oom;
break;
}
}
ok = true;
oom:
if (!ok) {
errno = ENOMEM;
fprintf(stderr, "Failed to read from %s ", filename); perror("with error");
}
end:
free(src); dealloc_shlex_state(&s);
if (ok) *final_ans = ans;
else free_argv_array(final_ans);
return ok;
}