1 #include <stdarg.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "emacs-module.h"
6 #include "interface.h"
7
8
em_init(emacs_env * env)9 void em_init(emacs_env *env)
10 {
11 esyms_init(env);
12
13 em_define_error(env, esym_wrong_value_argument, "Wrong argument value passed", esym_nil);
14 em_define_error(env, esym_giterr, "Git error", esym_nil);
15 em_define_error(env, esym_giterr_nomemory, "Git error: out of memory", esym_giterr);
16 em_define_error(env, esym_giterr_os, "Git error: OS", esym_giterr);
17 em_define_error(env, esym_giterr_invalid, "Git error: invalid", esym_giterr);
18 em_define_error(env, esym_giterr_reference, "Git error: reference", esym_giterr);
19 em_define_error(env, esym_giterr_zlib, "Git error: zlib", esym_giterr);
20 em_define_error(env, esym_giterr_repository, "Git error: repository", esym_giterr);
21 em_define_error(env, esym_giterr_config, "Git error: config", esym_giterr);
22 em_define_error(env, esym_giterr_regex, "Git error: regex", esym_giterr);
23 em_define_error(env, esym_giterr_odb, "Git error: ODB", esym_giterr);
24 em_define_error(env, esym_giterr_index, "Git error: index", esym_giterr);
25 em_define_error(env, esym_giterr_object, "Git error: object", esym_giterr);
26 em_define_error(env, esym_giterr_net, "Git error: net", esym_giterr);
27 em_define_error(env, esym_giterr_tag, "Git error: tag", esym_giterr);
28 em_define_error(env, esym_giterr_tree, "Git error: tree", esym_giterr);
29 em_define_error(env, esym_giterr_indexer, "Git error: indexer", esym_giterr);
30 em_define_error(env, esym_giterr_ssl, "Git error: SSL", esym_giterr);
31 em_define_error(env, esym_giterr_submodule, "Git error: submodule", esym_giterr);
32 em_define_error(env, esym_giterr_thread, "Git error: thread", esym_giterr);
33 em_define_error(env, esym_giterr_stash, "Git error: stash", esym_giterr);
34 em_define_error(env, esym_giterr_checkout, "Git error: checkout", esym_giterr);
35 em_define_error(env, esym_giterr_fetchhead, "Git error: fetch-head", esym_giterr);
36 em_define_error(env, esym_giterr_merge, "Git error: merge", esym_giterr);
37 em_define_error(env, esym_giterr_ssh, "Git error: SSH", esym_giterr);
38 em_define_error(env, esym_giterr_filter, "Git error: filter", esym_giterr);
39 em_define_error(env, esym_giterr_revert, "Git error: revert", esym_giterr);
40 em_define_error(env, esym_giterr_callback, "Git error: callback", esym_giterr);
41 em_define_error(env, esym_giterr_cherrypick, "Git error: cherry-pick", esym_giterr);
42 em_define_error(env, esym_giterr_describe, "Git error: describe", esym_giterr);
43 em_define_error(env, esym_giterr_rebase, "Git error: rebase", esym_giterr);
44 em_define_error(env, esym_giterr_filesystem, "Git error: filesystem", esym_giterr);
45 em_define_error(env, esym_giterr_patch, "Git error: patch", esym_giterr);
46 em_define_error(env, esym_giterr_worktree, "Git error: worktree", esym_giterr);
47 em_define_error(env, esym_giterr_sha1, "Git error: SHA-1", esym_giterr);
48 }
49
50 /**
51 * Call an Emacs function without error checking.
52 * @param env The active Emacs environment.
53 * @param func The function to call.
54 * @param nargs The number of arguments that follow.
55 * @return The function return value.
56 */
em_funcall(emacs_env * env,emacs_value func,ptrdiff_t nargs,...)57 static emacs_value em_funcall(emacs_env *env, emacs_value func, ptrdiff_t nargs, ...)
58 {
59 emacs_value args[nargs];
60
61 va_list vargs;
62 va_start(vargs, nargs);
63 for (ptrdiff_t i = 0; i < nargs; i++)
64 args[i] = va_arg(vargs, emacs_value);
65 va_end(vargs);
66
67 return env->funcall(env, func, nargs, args);
68 }
69
em_assert(emacs_env * env,emacs_value predicate,emacs_value arg)70 bool em_assert(emacs_env *env, emacs_value predicate, emacs_value arg)
71 {
72 bool cond = EM_EXTRACT_BOOLEAN(em_funcall(env, predicate, 1, arg));
73 if (!cond)
74 em_signal_wrong_type(env, predicate, arg);
75 return cond;
76 }
77
em_assert_list(emacs_env * env,emacs_value predicate,emacs_value arg)78 ptrdiff_t em_assert_list(emacs_env *env, emacs_value predicate, emacs_value arg)
79 {
80 ptrdiff_t nelems = 0;
81 bool predp = EM_EXTRACT_BOOLEAN(predicate);
82
83 while (em_consp(env, arg)) {
84 emacs_value car = em_car(env, arg);
85 if (predp && !em_assert(env, predicate, car))
86 return -1;
87 nelems++;
88 arg = em_cdr(env, arg);
89 }
90
91 if (EM_EXTRACT_BOOLEAN(arg)) {
92 em_signal_wrong_type(env, esym_listp, arg);
93 return -1;
94 }
95
96 return nelems;
97 }
98
em_signal(emacs_env * env,emacs_value error,const char * _msg)99 void em_signal(emacs_env *env, emacs_value error, const char *_msg)
100 {
101 emacs_value msg = EM_STRING(_msg);
102 env->non_local_exit_signal(env, error, em_cons(env, msg, esym_nil));
103 }
104
em_signal_wrong_type(emacs_env * env,emacs_value expected,emacs_value actual)105 void em_signal_wrong_type(emacs_env *env, emacs_value expected, emacs_value actual)
106 {
107 env->non_local_exit_signal(
108 env, esym_wrong_type_argument,
109 em_cons(env, expected, em_cons(env, actual, esym_nil))
110 );
111 }
112
em_signal_wrong_value(emacs_env * env,emacs_value actual)113 void em_signal_wrong_value(emacs_env *env, emacs_value actual)
114 {
115 env->non_local_exit_signal(env, esym_wrong_value_argument, actual);
116 }
117
em_signal_args_out_of_range(emacs_env * env,intmax_t index)118 void em_signal_args_out_of_range(emacs_env *env, intmax_t index)
119 {
120 env->non_local_exit_signal(env, esym_args_out_of_range, EM_INTEGER(index));
121 }
122
em_get_string_with_size(emacs_env * env,emacs_value arg,ptrdiff_t * size)123 char *em_get_string_with_size(emacs_env *env, emacs_value arg, ptrdiff_t *size)
124 {
125 env->copy_string_contents(env, arg, NULL, size);
126
127 char *buf = (char*) malloc((*size) * sizeof(char));
128 env->copy_string_contents(env, arg, buf, size);
129
130 (*size)--;
131 return buf;
132 }
133
em_get_string(emacs_env * env,emacs_value arg)134 char *em_get_string(emacs_env *env, emacs_value arg)
135 {
136 ptrdiff_t size;
137 return em_get_string_with_size(env, arg, &size);
138 }
139
em_cons(emacs_env * env,emacs_value car,emacs_value cdr)140 emacs_value em_cons(emacs_env *env, emacs_value car, emacs_value cdr)
141 {
142 return em_funcall(env, esym_cons, 2, car, cdr);
143 }
144
em_consp(emacs_env * env,emacs_value cell)145 bool em_consp(emacs_env *env, emacs_value cell)
146 {
147 return EM_EXTRACT_BOOLEAN(em_funcall(env, esym_consp, 1, cell));
148 }
149
em_car(emacs_env * env,emacs_value cell)150 emacs_value em_car(emacs_env *env, emacs_value cell)
151 {
152 return em_funcall(env, esym_car, 1, cell);
153 }
154
em_cdr(emacs_env * env,emacs_value cell)155 emacs_value em_cdr(emacs_env *env, emacs_value cell)
156 {
157 return em_funcall(env, esym_cdr, 1, cell);
158 }
159
em_list(emacs_env * env,emacs_value * objects,ptrdiff_t nobjects)160 emacs_value em_list(emacs_env *env, emacs_value *objects, ptrdiff_t nobjects)
161 {
162 return env->funcall(env, esym_list, nobjects, objects);
163 }
164
em_listp(emacs_env * env,emacs_value object)165 bool em_listp(emacs_env *env, emacs_value object)
166 {
167 return EM_EXTRACT_BOOLEAN(em_funcall(env, esym_listp, 1, object));
168 }
169
em_length(emacs_env * env,emacs_value sequence)170 ptrdiff_t em_length(emacs_env *env, emacs_value sequence)
171 {
172 emacs_value result = em_funcall(env, esym_length, 1, sequence);
173 ptrdiff_t length = env->extract_integer(env, result);
174 EM_RETURN_IF_NLE(-1);
175 return length;
176 }
177
em_assq(emacs_env * env,emacs_value key,emacs_value list)178 emacs_value em_assq(emacs_env *env, emacs_value key, emacs_value list)
179 {
180 return em_funcall(env, esym_assq, 2, key, list);
181 }
182
em_define_error(emacs_env * env,emacs_value symbol,const char * msg,emacs_value parent)183 void em_define_error(emacs_env *env, emacs_value symbol, const char *msg, emacs_value parent)
184 {
185 em_funcall(env, esym_define_error, 3, symbol, EM_STRING(msg), parent);
186 }
187
em_defun(emacs_env * env,const char * name,emacs_value func)188 void em_defun(emacs_env *env, const char *name, emacs_value func)
189 {
190 em_funcall(env, esym_defalias, 2, env->intern(env, name), func);
191 }
192
em_expand_file_name(emacs_env * env,emacs_value path)193 emacs_value em_expand_file_name(emacs_env *env, emacs_value path)
194 {
195 return em_funcall(env, esym_expand_file_name, 1, path);
196 }
197
em_provide(emacs_env * env,const char * feature)198 void em_provide(emacs_env *env, const char *feature)
199 {
200 em_funcall(env, esym_provide, 1, env->intern(env, feature));
201 }
202
em_user_ptrp(emacs_env * env,emacs_value val)203 bool em_user_ptrp(emacs_env *env, emacs_value val)
204 {
205 return EM_EXTRACT_BOOLEAN(em_funcall(env, esym_user_ptrp, 1, val));
206 }
207
em_default_directory(emacs_env * env)208 char *em_default_directory(emacs_env *env)
209 {
210 emacs_value dir = em_funcall(env, esym_symbol_value, 1, esym_default_directory);
211 dir = em_expand_file_name(env, dir);
212 return em_get_string(env, dir);
213 }
214
em_decode_time(emacs_env * env,intmax_t timestamp,intmax_t offset)215 emacs_value em_decode_time(emacs_env *env, intmax_t timestamp, intmax_t offset)
216 {
217 return em_funcall(env, esym_decode_time, 2,
218 EM_INTEGER(timestamp),
219 EM_INTEGER(offset));
220 }
221
em_encode_time(emacs_env * env,emacs_value time,intmax_t * timestamp,intmax_t * offset)222 bool em_encode_time(emacs_env *env, emacs_value time, intmax_t *timestamp, intmax_t *offset)
223 {
224 emacs_value encoded = em_funcall(env, esym_apply, 2, esym_encode_time, time);
225 EM_RETURN_IF_NLE(false);
226
227 emacs_value high = em_car(env, encoded);
228 *timestamp = EM_EXTRACT_INTEGER(high) << 16;
229
230 encoded = em_cdr(env, encoded);
231 emacs_value low = em_car(env, encoded);
232 *timestamp += EM_EXTRACT_INTEGER(low);
233
234 emacs_value last = em_funcall(env, esym_last, 1, time);
235 if (!em_consp(env, last)) {
236 em_signal_wrong_type(env, last, esym_consp);
237 return false;
238 }
239 emacs_value zone = em_car(env, last);
240 if (!EM_EXTRACT_BOOLEAN(em_funcall(env, esym_integerp, 1, zone))) {
241 em_signal_wrong_type(env, zone, esym_integerp);
242 return false;
243 }
244 *offset = EM_EXTRACT_INTEGER(zone);
245
246 return true;
247 }
248
em_insert(emacs_env * env,const char * ptr,size_t length)249 void em_insert(emacs_env *env, const char *ptr, size_t length)
250 {
251 em_funcall(env, esym_insert, 1, env->make_string(env, ptr, length));
252 }
253
em_string_as_unibyte(emacs_env * env,emacs_value str)254 emacs_value em_string_as_unibyte(emacs_env *env, emacs_value str)
255 {
256 return em_funcall(env, esym_string_as_unibyte, 1, str);
257 }
258
259
260 // =============================================================================
261 // Symbol <-> enum map functions
262
em_findsym(esym_enumval * out,emacs_env * env,emacs_value value,esym_map * map,bool required)263 static bool em_findsym(esym_enumval *out, emacs_env *env, emacs_value value, esym_map *map, bool required)
264 {
265 while (map->symbol != NULL) {
266 if (EM_EQ(*map->symbol, value)) {
267 *out = map->value;
268 return true;
269 }
270 map++;
271 }
272
273 if (required)
274 em_signal_wrong_value(env, value);
275 return false;
276 }
277
278 #define MKFINDSYM(type, map) \
279 bool em_findsym_##map( \
280 type *out, emacs_env *env, emacs_value value, bool required) \
281 { \
282 esym_enumval val = {0}; \
283 bool retval = em_findsym( \
284 &val, env, value, esym_##map##_map, required); \
285 *out = val.map; \
286 return retval; \
287 }
288
289 MKFINDSYM(git_branch_t, branch);
290 MKFINDSYM(git_checkout_strategy_t, checkout_strategy);
291 MKFINDSYM(git_config_level_t, config_level);
292 MKFINDSYM(git_describe_strategy_t, describe_strategy);
293 MKFINDSYM(git_delta_t, delta);
294 MKFINDSYM(git_diff_format_t, diff_format);
295 MKFINDSYM(git_fetch_prune_t, fetch_prune);
296 MKFINDSYM(git_filemode_t, filemode);
297 MKFINDSYM(git_merge_file_favor_t, merge_file_favor);
298 MKFINDSYM(git_otype, otype);
299 MKFINDSYM(git_pathspec_flag_t, pathspec_flag);
300 MKFINDSYM(git_proxy_t, proxy);
301 MKFINDSYM(git_remote_autotag_option_t, remote_autotag_option);
302 MKFINDSYM(git_reset_t, reset);
303 MKFINDSYM(git_submodule_ignore_t, submodule_ignore);
304 MKFINDSYM(git_submodule_recurse_t, submodule_recurse);
305 MKFINDSYM(git_submodule_update_t, submodule_update);
306 MKFINDSYM(int, stage);
307 MKFINDSYM(git_status_show_t, status_show);
308 MKFINDSYM(git_diff_find_t, diff_find);
309
310 #undef MKFINDSYM
311
312 #define MKFINDENUM(type, mapname) \
313 emacs_value em_findenum_##mapname(type value) \
314 { \
315 esym_map *map = esym_##mapname##_map; \
316 while (map->symbol != NULL) { \
317 if (map->value.mapname == value) \
318 return *map->symbol; \
319 map++; \
320 } \
321 return esym_nil; \
322 }
323
324 MKFINDENUM(git_checkout_notify_t, checkout_notify);
325 MKFINDENUM(git_delta_t, delta);
326 MKFINDENUM(git_direction, direction);
327 MKFINDENUM(git_error_t, error);
328 MKFINDENUM(git_filemode_t, filemode);
329 MKFINDENUM(git_merge_preference_t, merge_preference);
330 MKFINDENUM(git_otype, otype);
331 MKFINDENUM(git_submodule_ignore_t, submodule_ignore);
332 MKFINDENUM(git_submodule_update_t, submodule_update);
333 MKFINDENUM(git_submodule_recurse_t, submodule_recurse);
334 MKFINDENUM(git_remote_autotag_option_t, remote_autotag_option);
335 MKFINDENUM(git_repository_state_t, repository_state);
336 MKFINDENUM(int, stage);
337
338 #undef MKFINDENUM
339
340 #define MKSETFLAG(type, map) \
341 bool em_setflag_##map( \
342 void *out, emacs_env *env, emacs_value value, \
343 bool on, bool required) \
344 { \
345 esym_enumval val; \
346 if (!em_findsym(&val, env, value, esym_##map##_map, required)) \
347 return false; \
348 if (on) \
349 *((type*) out) |= val.map; \
350 else \
351 *((type*) out) &= ~(val.map); \
352 return true; \
353 }
354
355 MKSETFLAG(git_checkout_notify_t, checkout_notify);
356 MKSETFLAG(git_diff_option_t, diff_option);
357 MKSETFLAG(git_index_add_option_t, index_add_option);
358 MKSETFLAG(git_merge_file_flag_t, merge_file_flag);
359 MKSETFLAG(git_merge_flag_t, merge_flag);
360 MKSETFLAG(git_sort_t, sort);
361 MKSETFLAG(git_status_opt_t, status_opt);
362
363 #undef MKSETFLAG
364
em_setflags_list(void * out,emacs_env * env,emacs_value list,bool required,setter * setter)365 bool em_setflags_list(void *out, emacs_env *env, emacs_value list,
366 bool required, setter *setter)
367 {
368 while (em_consp(env, list)) {
369 emacs_value car = em_car(env, list);
370 if (!setter(out, env, car, true, required)) {
371 if (required) return false;
372 }
373 list = em_cdr(env, list);
374 }
375 if (EM_EXTRACT_BOOLEAN(list)) {
376 em_signal_wrong_type(env, esym_consp, list);
377 return false;
378 }
379 return true;
380 }
381
em_setflags_alist(void * out,emacs_env * env,emacs_value alist,bool required,setter * setter)382 bool em_setflags_alist(void *out, emacs_env *env, emacs_value alist,
383 bool required, setter *setter)
384 {
385 while (em_consp(env, alist)) {
386 emacs_value cons = em_car(env, alist);
387 if (!em_assert(env, esym_consp, cons))
388 return false;
389 emacs_value car = em_car(env, cons);
390 emacs_value cdr = em_cdr(env, cons);
391 if (!setter(out, env, car, EM_EXTRACT_BOOLEAN(cdr), required)) {
392 if (required) return false;
393 }
394 alist = em_cdr(env, alist);
395 }
396 if (EM_EXTRACT_BOOLEAN(alist)) {
397 em_signal_wrong_type(env, esym_consp, alist);
398 return false;
399 }
400 return true;
401 }
402
403 #define MKGETLIST(type, mapname) \
404 emacs_value em_getlist_##mapname(emacs_env *env, type value) \
405 { \
406 esym_map *map = esym_##mapname##_map; \
407 emacs_value ret = esym_nil; \
408 ptrdiff_t nen = 0; \
409 for (; map[nen].symbol != NULL; nen++); \
410 while ((nen--) > 0) { \
411 if (map[nen].value.mapname & value) \
412 ret = em_cons(env, *map[nen].symbol, ret); \
413 } \
414 return ret; \
415 }
416
417 MKGETLIST(git_credtype_t, credtype);
418 MKGETLIST(int, indexcap);
419 MKGETLIST(git_merge_analysis_t, merge_analysis);
420 MKGETLIST(git_status_t, status);
421 MKGETLIST(git_submodule_status_t, submodule_status);
422
423 #undef MKGETLIST
424
425 #define MKCHECKFLAG(type, map) \
426 bool em_checkflag_##map( \
427 emacs_value *out, emacs_env *env, emacs_value symbol, \
428 type value, bool required) \
429 { \
430 esym_enumval val; \
431 if (!em_findsym(&val, env, symbol, esym_##map##_map, required)) \
432 return false; \
433 *out = (val.map & value) ? esym_t : esym_nil; \
434 return true; \
435 }
436
437 MKCHECKFLAG(git_feature_t, feature);
438 MKCHECKFLAG(git_submodule_status_t, submodule_status);
439
440 #undef MKCHECKFLAG
441