1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
/* opener.c — launch external programs with %f expansion. Detached GSubprocess.
*/
#include "opener.h"
struct Opener {
GPtrArray *p_progs; /* OpenerProg* */
};
static void
_prog_free(gpointer p) {
OpenerProg *d = (OpenerProg *)p;
if (d) {
g_free(d->c_name);
g_free(d->c_command);
g_free(d);
}
}
Opener *
opener_new(void) {
Opener *o = g_new0(Opener, 1);
o->p_progs = g_ptr_array_new_with_free_func(_prog_free);
return o;
}
void
opener_delete(Opener *o) {
if (!o)
return;
g_ptr_array_unref(o->p_progs);
g_free(o);
}
void
opener_set_progs(Opener *o, const GPtrArray *p) {
g_return_if_fail(o);
g_ptr_array_set_size(o->p_progs, 0);
if (!p)
return;
for (guint i = 0; i < p->len; i++) {
const OpenerProg *src = g_ptr_array_index((GPtrArray *)p, i);
OpenerProg *np = g_new(OpenerProg, 1);
np->c_name = g_strdup(src->c_name);
np->c_command = g_strdup(src->c_command);
g_ptr_array_add(o->p_progs, np);
}
}
const GPtrArray *
opener_get_progs(Opener *o) {
return o ? o->p_progs : NULL;
}
/* Expand %f → shell-escaped path. Returns a newly-allocated argv vector
* (NULL-terminated). Caller frees with g_strfreev. */
/* Replace all occurrences of c_old with c_new in c_str. Caller frees. */
static char *
_str_replace(const char *c_str, const char *c_old, const char *c_new) {
if (c_str == NULL || c_old == NULL || c_new == NULL)
return NULL;
GString *p_out = g_string_new(NULL);
const char *p = c_str;
gsize u_old_len = strlen(c_old);
while (*p) {
if (strncmp(p, c_old, u_old_len) == 0) {
g_string_append(p_out, c_new);
p += u_old_len;
} else {
g_string_append_c(p_out, *p);
p++;
}
}
return g_string_free(p_out, FALSE);
}
static char **
_expand_command(const char *c_cmd, GFile *p_file) {
char *c_path = g_file_get_path(p_file);
/* Simple tokenisation: split on spaces, replace %f with path. */
char **parts = g_strsplit(c_cmd, " ", -1);
GPtrArray *argv = g_ptr_array_new();
for (guint i = 0; parts[i]; i++) {
if (g_str_equal(parts[i], "%f"))
g_ptr_array_add(argv, g_strdup(c_path));
else if (strstr(parts[i], "%f")) {
char *r = _str_replace(parts[i], "%f", c_path);
g_ptr_array_add(argv, r ? r : g_strdup(parts[i]));
} else
g_ptr_array_add(argv, g_strdup(parts[i]));
}
g_ptr_array_add(argv, NULL);
g_strfreev(parts);
g_free(c_path);
return (char **)g_ptr_array_free(argv, FALSE);
}
gboolean
opener_launch(Opener *o, GFile *p_file, const OpenerProg *p_prog,
GError **p_err) {
(void)o;
g_return_val_if_fail(G_IS_FILE(p_file), FALSE);
g_return_val_if_fail(p_prog, FALSE);
char **argv = _expand_command(p_prog->c_command, p_file);
GSubprocess *p_sub = g_subprocess_newv((const char *const *)argv,
G_SUBPROCESS_FLAGS_NONE, p_err);
g_strfreev(argv);
if (p_sub == NULL)
return FALSE;
/* Detached: don't wait. Unref immediately (process keeps running). */
g_object_unref(p_sub);
return TRUE;
}
|