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