1 #include "emacs-module.h"
2 #include "git2.h"
3 #include "git2.h"
4 
5 #ifndef EGIT_H
6 #define EGIT_H
7 
8 /**
9  * Macro that defines a docstring for a function.
10  * @param name The function name (without egit_ prefix).
11  * @param args The argument list as visible from Emacs (without parens).
12  * @param docstring The rest of the documentation.
13  */
14 #define EGIT_DOC(name, args, docstring)                                 \
15     const char *egit_##name##__doc = (docstring "\n\n(fn " args ")")
16 
17 /**
18  * Macro that declares a function and its docstring variable.
19  * @param name The function name (without egit_ prefix)
20  * @param ... The function arguments (without emacs_env)
21  */
22 #define EGIT_DEFUN(name, ...)                                   \
23     extern const char *egit_##name##__doc;                      \
24     emacs_value egit_##name(emacs_env *env, __VA_ARGS__)
25 
26 // Variant of EGIT_DEFUN for zero parameters.
27 #define EGIT_DEFUN_0(name)                      \
28     extern const char *egit_##name##__doc;      \
29     emacs_value egit_##name(emacs_env *env)
30 
31 // Assert that VAL is a git annotated commit, signal an error and return otherwise.
32 #define EGIT_ASSERT_ANNOTATED_COMMIT(val)                                          \
33     do { if (!egit_assert_type(env, (val), EGIT_ANNOTATED_COMMIT, esym_libgit_annotated_commit_p)) return esym_nil; } while (0)
34 
35 // Assert that VAL is a git blame, signal an error and return otherwise.
36 #define EGIT_ASSERT_BLAME(val)                                          \
37     do { if (!egit_assert_type(env, (val), EGIT_BLAME, esym_libgit_blame_p)) return esym_nil; } while (0)
38 
39 // Assert that VAL is a git blob, signal an error and return otherwise.
40 #define EGIT_ASSERT_BLOB(val)                                           \
41     do { if (!egit_assert_type(env, (val), EGIT_BLOB, esym_libgit_blob_p)) return esym_nil; } while (0)
42 
43 // Assert that VAL is a git blame hunk, signal an error and return otherwise.
44 #define EGIT_ASSERT_BLAME_HUNK(val)                                     \
45     do { if (!egit_assert_type(env, (val), EGIT_BLAME_HUNK, esym_libgit_blame_hunk_p)) return esym_nil; } while (0)
46 
47 // Assert that VAL is a git commit, signal an error and return otherwise.
48 #define EGIT_ASSERT_COMMIT(val)                                         \
49     do { if (!egit_assert_type(env, (val), EGIT_COMMIT, esym_libgit_commit_p)) return esym_nil; } while (0)
50 
51 // Assert that VAL is a git config, signal an error and return otherwise.
52 #define EGIT_ASSERT_CONFIG(val)                                         \
53     do { if (!egit_assert_type(env, (val), EGIT_CONFIG, esym_libgit_config_p)) return esym_nil; } while (0)
54 
55 // Assert that VAL is a git credential, signal an error and return otherwise.
56 #define EGIT_ASSERT_CRED(val)                                           \
57     do { if (!egit_assert_type(env, (val), EGIT_CRED, esym_libgit_cred_p)) return esym_nil; } while (0)
58 
59 // Assert that VAL is a git diff, signal an error and return otherwise.
60 #define EGIT_ASSERT_DIFF(val)                                           \
61     do { if (!egit_assert_type(env, (val), EGIT_DIFF, esym_libgit_diff_p)) return esym_nil; } while (0)
62 
63 // Assert that VAL is a git diff delta, signal an error and return otherwise.
64 #define EGIT_ASSERT_DIFF_DELTA(val)                                     \
65     do { if (!egit_assert_type(env, (val), EGIT_DIFF_DELTA, esym_libgit_diff_delta_p)) return esym_nil; } while (0)
66 
67 // Assert that VAL is a git diff binary, signal an error and return otherwise.
68 #define EGIT_ASSERT_DIFF_BINARY(val)                                    \
69     do { if (!egit_assert_type(env, (val), EGIT_DIFF_BINARY, esym_libgit_diff_binary_p)) return esym_nil; } while (0)
70 
71 // Assert that VAL is a git diff hunk, signal an error and return otherwise.
72 #define EGIT_ASSERT_DIFF_HUNK(val)                                     \
73     do { if (!egit_assert_type(env, (val), EGIT_DIFF_HUNK, esym_libgit_diff_hunk_p)) return esym_nil; } while (0)
74 
75 // Assert that VAL is a git diff line, signal an error and return otherwise.
76 #define EGIT_ASSERT_DIFF_LINE(val)                                     \
77     do { if (!egit_assert_type(env, (val), EGIT_DIFF_LINE, esym_libgit_diff_line_p)) return esym_nil; } while (0)
78 
79 // Assert that VAL is a git index, signal an error and return otherwise.
80 #define EGIT_ASSERT_INDEX(val)                                          \
81     do { if (!egit_assert_type(env, (val), EGIT_INDEX, esym_libgit_index_p)) return esym_nil; } while (0)
82 
83 // Assert that VAL is a git index entry, signal an error and return otherwise.
84 #define EGIT_ASSERT_INDEX_ENTRY(val)                                    \
85     do { if (!egit_assert_type(env, (val), EGIT_INDEX_ENTRY, esym_libgit_index_entry_p)) return esym_nil; } while (0)
86 
87 // Assert that VAL is a git object, signal an error and return otherwise.
88 #define EGIT_ASSERT_OBJECT(val)                                         \
89     do { if (!egit_assert_object(env, (val))) return esym_nil; } while (0)
90 
91 // Assert that VAL is a git pathspec, signal an error and return otherwise.
92 #define EGIT_ASSERT_PATHSPEC(val)                                      \
93     do { if (!egit_assert_type(env, (val), EGIT_PATHSPEC, esym_libgit_pathspec_p)) return esym_nil; } while (0)
94 
95 // Assert that VAL is a git pathspec match list, signal an error and return otherwise.
96 #define EGIT_ASSERT_PATHSPEC_MATCH_LIST(val)                                      \
97     do { if (!egit_assert_type(env, (val), EGIT_PATHSPEC_MATCH_LIST, esym_libgit_pathspec_match_list_p)) return esym_nil; } while (0)
98 
99 // Assert that VAL is a git reference, signal an error and return otherwise.
100 #define EGIT_ASSERT_REFERENCE(val)                                      \
101     do { if (!egit_assert_type(env, (val), EGIT_REFERENCE, esym_libgit_reference_p)) return esym_nil; } while (0)
102 
103 // Assert that VAL is a reflog, signal an error and return otherwise.
104 #define EGIT_ASSERT_REFLOG(val)                                         \
105     do { if (!egit_assert_type(env, (val), EGIT_REFLOG, esym_libgit_reflog_p)) return esym_nil; } while (0)
106 
107 // Assert that VAL is a reflog entry, signal an error and return otherwise.
108 #define EGIT_ASSERT_REFLOG_ENTRY(val)                                   \
109     do { if (!egit_assert_type(env, (val), EGIT_REFLOG_ENTRY, esym_libgit_reflog_entry_p)) return esym_nil; } while (0)
110 
111 // Assert that VAL is a git refspec, signal an error and return otherwise.
112 #define EGIT_ASSERT_REFSPEC(val)                                        \
113     do { if (!egit_assert_type(env, (val), EGIT_REFSPEC, esym_libgit_refspec_p)) return esym_nil; } while (0)
114 
115 // Assert that VAL is a git remote, signal an error and return otherwise.
116 #define EGIT_ASSERT_REMOTE(val)                                         \
117     do { if (!egit_assert_type(env, (val), EGIT_REMOTE, esym_libgit_remote_p)) return esym_nil; } while (0)
118 
119 // Assert that VAL is a git repository, signal an error and return otherwise.
120 #define EGIT_ASSERT_REPOSITORY(val)                                     \
121     do { if (!egit_assert_type(env, (val), EGIT_REPOSITORY, esym_libgit_repository_p)) return esym_nil; } while (0)
122 
123 // Assert that VAL is a git revwalk, signal an error and return otherwise.
124 #define EGIT_ASSERT_REVWALK(val)                                        \
125     do { if (!egit_assert_type(env, (val), EGIT_REVWALK, esym_libgit_revwalk_p)) return esym_nil; } while (0)
126 
127 // Assert that VAL is a signature, signal an error and return otherwise.
128 #define EGIT_ASSERT_SIGNATURE(val)                                      \
129     do { if (!egit_assert_type(env, (val), EGIT_SIGNATURE, esym_libgit_signature_p)) return esym_nil; } while (0)
130 
131 // Assert that VAL is a transaction, signal an error and return otherwise.
132 #define EGIT_ASSERT_SIGNATURE_OR_NIL(val)                               \
133     do { if (EGIT_EXTRACT_BOOLEAN(val)) EGIT_ASSERT_SIGNATURE(val); } while (0)
134 
135 // Assert that VAL is a signature, signal an error and return otherwise.
136 #define EGIT_ASSERT_SUBMODULE(val)                                      \
137     do { if (!egit_assert_type(env, (val), EGIT_SUBMODULE, esym_libgit_submodule_p)) return esym_nil; } while (0)
138 
139 // Assert that VAL is a transaction, signal an error and return otherwise.
140 #define EGIT_ASSERT_TAG(val)                                            \
141     do { if (!egit_assert_type(env, (val), EGIT_TAG, esym_libgit_tag_p)) return esym_nil; } while (0)
142 
143 // Assert that VAL is a transaction, signal an error and return otherwise.
144 #define EGIT_ASSERT_TRANSACTION(val)                                    \
145     do { if (!egit_assert_type(env, (val), EGIT_TRANSACTION, esym_libgit_transaction_p)) return esym_nil; } while (0)
146 
147 // Assert that VAL is a tree, signal an error and return otherwise.
148 #define EGIT_ASSERT_TREE(val)                                           \
149     do { if (!egit_assert_type(env, (val), EGIT_TREE, esym_libgit_tree_p)) return esym_nil; } while (0)
150 
151 // Assert that VAL is a treebuilder, signal an error and return otherwise.
152 #define EGIT_ASSERT_TREEBUILDER(val)                                    \
153     do { if (!egit_assert_type(env, (val), EGIT_TREEBUILDER, esym_libgit_treebuilder_p)) return esym_nil; } while (0)
154 
155 /**
156  * Extract a libgit git_??? struct from an emacs_value.
157  * Caller is responsible for ensuring that this is a valid operation.
158  */
159 #define EGIT_EXTRACT(val) (((egit_object*) EM_EXTRACT_USER_PTR(val))->ptr)
160 
161 /**
162  * Extract a libgit git_??? struct from an emacs_value, or NULL.
163  * Caller is responsible for ensuring that this is a valid operation.
164  */
165 #define EGIT_EXTRACT_OR_NULL(val)                                \
166   (EM_EXTRACT_BOOLEAN(val) ? EGIT_EXTRACT(val) : NULL);
167 
168 /**
169  * Extract egit_object pointer representing a parent object, or NULL.
170  * Caller is responsible for ensuring that this is a valid operation.
171  */
172 #define EGIT_EXTRACT_PARENT(val)                        \
173     (((egit_object*) EM_EXTRACT_USER_PTR(val))->parent) \
174 
175 /**
176  * Extract a git_oid from an emacs_value.
177  * Caller is responsible for ensuring that the emacs_value is a string.
178  */
179 #define EGIT_EXTRACT_OID(val, tgt)                      \
180     do {                                                \
181         char *__str = em_get_string(env, (val));        \
182         int __retval = git_oid_fromstrp(&(tgt), __str); \
183         free(__str);                                    \
184         EGIT_CHECK_ERROR(__retval);                     \
185     } while (0)
186 
187 /**
188  * Extract a partial git_oid from an emacs_value and store its length.
189  * Caller is responsible for ensuring that the emacs_value is a string.
190  */
191 #define EGIT_EXTRACT_OID_PREFIX(val, tgt, tgt_len)      \
192     do {                                                \
193         char *__str = em_get_string(env, (val));        \
194         tgt_len = strlen(__str);                        \
195         int __retval = git_oid_fromstrp(&(tgt), __str); \
196         free(__str);                                    \
197         EGIT_CHECK_ERROR(__retval);                     \
198     } while (0)
199 
200 /**
201  * If libgit2 signalled an error, pass the error on to Emacs and return.
202  * @param val A libgit2 return value (negative value indicates error).
203  */
204 #define EGIT_CHECK_ERROR(val)                                           \
205     do { if (egit_dispatch_error(env, (val))) return esym_nil; } while (0)
206 
207 /**
208  * Convert a git_buf to an Emacs string and return it, freeing the git_buf in the process.
209  */
210 #define EGIT_RET_BUF_AS_STRING(buf)                                     \
211     do {                                                                \
212         emacs_value ret = env->make_string(env, (buf).ptr, (buf).size); \
213         git_buf_dispose(&(buf));                                        \
214         return ret;                                                     \
215     } while (0)
216 
217 /**
218  * Convert a git_strarray to an Emacs list and return it, freeing the git_strarray in the process.
219  */
220 #define EGIT_RET_STRARRAY(arr)                                  \
221     do {                                                        \
222         emacs_value list = esym_nil;                              \
223         for (ptrdiff_t c = (arr).count-1; c >= 0; c--) {        \
224             emacs_value str = EM_STRING((arr).strings[c]);      \
225             list = em_cons(env, str, list);                     \
226         }                                                       \
227         git_strarray_free(&(arr));                              \
228         return list;                                            \
229     } while (0)
230 
231 /**
232  * Set or unset a bit according to the truth value of an emacs value.
233  * @param tgt The value to change
234  * @param bit The bit to flip
235  * @param opt The determining emacs value
236  */
237 #define EGIT_SET_BIT(tgt, bit, opt)                      \
238     do {                                                 \
239         if (EM_EXTRACT_BOOLEAN(opt))                     \
240             (tgt) |= (bit);                              \
241         else                                             \
242             (tgt) &= ~(bit);                             \
243     } while (0)
244 
245 /**
246  * Enum used to distinguish between various types of git_??? structs.
247  */
248 typedef enum {
249     EGIT_UNKNOWN,
250     EGIT_REPOSITORY,
251     EGIT_REFERENCE,
252     EGIT_COMMIT,
253     EGIT_TREE,
254     EGIT_BLOB,
255     EGIT_TAG,
256     EGIT_OBJECT,
257     EGIT_SIGNATURE,
258     EGIT_BLAME,
259     EGIT_BLAME_HUNK,
260     EGIT_CONFIG,
261     EGIT_TRANSACTION,
262     EGIT_INDEX,
263     EGIT_INDEX_ENTRY,
264     EGIT_DIFF,
265     EGIT_DIFF_DELTA,
266     EGIT_DIFF_BINARY,
267     EGIT_DIFF_HUNK,
268     EGIT_DIFF_LINE,
269     EGIT_PATHSPEC,
270     EGIT_PATHSPEC_MATCH_LIST,
271     EGIT_REMOTE,
272     EGIT_REFSPEC,
273     EGIT_SUBMODULE,
274     EGIT_CRED,
275     EGIT_ANNOTATED_COMMIT,
276     EGIT_REFLOG,
277     EGIT_REFLOG_ENTRY,
278     EGIT_REVWALK,
279     EGIT_TREEBUILDER
280 } egit_type;
281 
282 /**
283  * Hashable wrapper structure for a git_??? struct.
284  * These are used in an internal object hash-table to ensure that pointers aren't freed too early.
285  * This is necessary since there may be many emacs_values that point to the same git_repository
286  * (for example), so we can't just free the git_repository pointer in a finalizer called from Emacs.
287  * Moreover, emacs_values that store e.g. git_object pointers must keep the git_repository alive.
288  * To fix this we keep a hash table of pointers mapping to egit_objects, counting references and
289  * freeing objects when they reach zero.
290  *
291  * User-pointers returned to Emacs should always wrap a struct of type egit_object.
292  */
293 typedef struct egit_object_s egit_object;
294 
295 struct egit_object_s {
296     egit_type type;             /**< Type of object stored. */
297     ptrdiff_t refcount;         /**< Reference count. */
298     void *ptr;                  /**< Pointer to git_??? structure. */
299     egit_object *parent;        /**< Optional pointer to parent wrapper. */
300 };
301 
302 /**
303  * Return the git object type stored by en Emacs value.
304  * @param env The active Emacs environment.
305  * @param _obj The value to check.
306  * @return The object type, or EGIT_UNKNOWN if not known.
307  */
308 egit_type egit_get_type(emacs_env *env, emacs_value _obj);
309 
310 /**
311  * Assert that an Emacs value represents a libgit2 struct of a given type, or signal an Emacs error.
312  * To check for EGIT_OBJECT, use egit_assert_object instead.
313  * @param env The active Emacs environment.
314  * @param obj The value to check.
315  * @param type The type to check for.
316  * @param predicate Symbol to use in a wrong-type-argument error signal.
317  * @return True iff the value has the right type.
318  */
319 bool egit_assert_type(emacs_env *env, emacs_value obj, egit_type type, emacs_value predicate);
320 
321 /**
322  * Assert that an Emacs value represents a libgit2 git_object struct, or signal an Emacs error.
323  * @param env The active Emacs environment.
324  * @param obj The value to check.
325  * @return True iff the value has the right type.
326  */
327 bool egit_assert_object(emacs_env *env, emacs_value obj);
328 
329 /**
330  * Signal a wrong-type-argument error if ARG is not a proper list, every
331  * element of which satisfies has the correct libgit2 type.
332  * @param env The active Emacs environment.
333  * @param type The type to check for.
334  * @param predicate Symbol to use in a wrong-type-argument error signal.
335  * @param arg The list.
336  * @return The number of elements in the list, or negative if error.
337  */
338 ptrdiff_t egit_assert_list(emacs_env *env, egit_type type, emacs_value predicate, emacs_value arg);
339 
340 /**
341  * Finalizer for user pointers.
342  */
343 void egit_finalize(void* _obj);
344 
345 /**
346  * Wrap a git_??? structure in an emacs_value.
347  * @param env The active Emacs environment.
348  * @param obj The type of the object.
349  * @param ptr The pointer to store.
350  * @return The Emacs value.
351  */
352 emacs_value egit_wrap(emacs_env *env, egit_type type, const void* ptr, egit_object *parent);
353 
354 /**
355  * If libgit2 signaled an error, dispatch that error to Emacs.
356  * @param env The active Emacs environment.
357  * @param retval A libgit2 return value.
358  * @return True iff an error was signalled.
359  */
360 bool egit_dispatch_error(emacs_env *env, int retval);
361 
362 /**
363  * Define functions visible to Emacs.
364  * This function only needs to be called once.
365  * @param env The active Emacs environment.
366  */
367 void egit_init(emacs_env *env);
368 
369 #endif /* EGIT_H */
370