1 /* shared.c: global vars + some callback functions
3 * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
11 struct cgit_repolist cgit_repolist
;
12 struct cgit_context ctx
;
14 int chk_zero(int result
, char *msg
)
21 int chk_positive(int result
, char *msg
)
28 int chk_non_negative(int result
, char *msg
)
35 char *cgit_default_repo_desc
= "[no description]";
36 struct cgit_repo
*cgit_add_repo(const char *url
)
38 struct cgit_repo
*ret
;
40 if (++cgit_repolist
.count
> cgit_repolist
.length
) {
41 if (cgit_repolist
.length
== 0)
42 cgit_repolist
.length
= 8;
44 cgit_repolist
.length
*= 2;
45 cgit_repolist
.repos
= xrealloc(cgit_repolist
.repos
,
46 cgit_repolist
.length
*
47 sizeof(struct cgit_repo
));
50 ret
= &cgit_repolist
.repos
[cgit_repolist
.count
-1];
51 memset(ret
, 0, sizeof(struct cgit_repo
));
52 ret
->url
= trim_end(url
, '/');
55 ret
->desc
= cgit_default_repo_desc
;
57 ret
->section
= ctx
.cfg
.section
;
58 ret
->snapshots
= ctx
.cfg
.snapshots
;
59 ret
->enable_commit_graph
= ctx
.cfg
.enable_commit_graph
;
60 ret
->enable_log_filecount
= ctx
.cfg
.enable_log_filecount
;
61 ret
->enable_log_linecount
= ctx
.cfg
.enable_log_linecount
;
62 ret
->enable_remote_branches
= ctx
.cfg
.enable_remote_branches
;
63 ret
->enable_subject_links
= ctx
.cfg
.enable_subject_links
;
64 ret
->max_stats
= ctx
.cfg
.max_stats
;
65 ret
->branch_sort
= ctx
.cfg
.branch_sort
;
66 ret
->commit_sort
= ctx
.cfg
.commit_sort
;
67 ret
->module_link
= ctx
.cfg
.module_link
;
68 ret
->readme
= ctx
.cfg
.readme
;
70 ret
->about_filter
= ctx
.cfg
.about_filter
;
71 ret
->commit_filter
= ctx
.cfg
.commit_filter
;
72 ret
->source_filter
= ctx
.cfg
.source_filter
;
73 ret
->email_filter
= ctx
.cfg
.email_filter
;
74 ret
->owner_filter
= ctx
.cfg
.owner_filter
;
75 ret
->clone_url
= ctx
.cfg
.clone_url
;
76 ret
->submodules
.strdup_strings
= 1;
77 ret
->hide
= ret
->ignore
= 0;
81 struct cgit_repo
*cgit_get_repoinfo(const char *url
)
84 struct cgit_repo
*repo
;
86 for (i
= 0; i
< cgit_repolist
.count
; i
++) {
87 repo
= &cgit_repolist
.repos
[i
];
90 if (!strcmp(repo
->url
, url
))
96 void *cgit_free_commitinfo(struct commitinfo
*info
)
99 free(info
->author_email
);
100 free(info
->committer
);
101 free(info
->committer_email
);
104 free(info
->msg_encoding
);
109 char *trim_end(const char *str
, char c
)
116 while (len
> 0 && str
[len
- 1] == c
)
120 return xstrndup(str
, len
);
123 char *ensure_end(const char *str
, char c
)
125 size_t len
= strlen(str
);
128 if (len
&& str
[len
- 1] == c
)
129 return xstrndup(str
, len
);
131 result
= xmalloc(len
+ 2);
132 memcpy(result
, str
, len
);
134 result
[len
+ 1] = '\0';
138 void strbuf_ensure_end(struct strbuf
*sb
, char c
)
140 if (!sb
->len
|| sb
->buf
[sb
->len
- 1] != c
)
144 char *strlpart(char *txt
, int maxlen
)
151 if (strlen(txt
) <= maxlen
)
153 result
= xmalloc(maxlen
+ 1);
154 memcpy(result
, txt
, maxlen
- 3);
155 result
[maxlen
-1] = result
[maxlen
-2] = result
[maxlen
-3] = '.';
156 result
[maxlen
] = '\0';
160 char *strrpart(char *txt
, int maxlen
)
167 if (strlen(txt
) <= maxlen
)
169 result
= xmalloc(maxlen
+ 1);
170 memcpy(result
+ 3, txt
+ strlen(txt
) - maxlen
+ 4, maxlen
- 3);
171 result
[0] = result
[1] = result
[2] = '.';
175 void cgit_add_ref(struct reflist
*list
, struct refinfo
*ref
)
179 if (list
->count
>= list
->alloc
) {
180 list
->alloc
+= (list
->alloc
? list
->alloc
: 4);
181 size
= list
->alloc
* sizeof(struct refinfo
*);
182 list
->refs
= xrealloc(list
->refs
, size
);
184 list
->refs
[list
->count
++] = ref
;
187 static struct refinfo
*cgit_mk_refinfo(const char *refname
, const struct object_id
*oid
)
191 ref
= xmalloc(sizeof (struct refinfo
));
192 ref
->refname
= xstrdup(refname
);
193 ref
->object
= parse_object(oid
->hash
);
194 switch (ref
->object
->type
) {
196 ref
->tag
= cgit_parse_tag((struct tag
*)ref
->object
);
199 ref
->commit
= cgit_parse_commit((struct commit
*)ref
->object
);
205 static void cgit_free_taginfo(struct taginfo
*tag
)
209 if (tag
->tagger_email
)
210 free(tag
->tagger_email
);
216 static void cgit_free_refinfo(struct refinfo
*ref
)
219 free((char *)ref
->refname
);
220 switch (ref
->object
->type
) {
222 cgit_free_taginfo(ref
->tag
);
225 cgit_free_commitinfo(ref
->commit
);
231 void cgit_free_reflist_inner(struct reflist
*list
)
235 for (i
= 0; i
< list
->count
; i
++) {
236 cgit_free_refinfo(list
->refs
[i
]);
241 int cgit_refs_cb(const char *refname
, const struct object_id
*oid
, int flags
,
244 struct reflist
*list
= (struct reflist
*)cb_data
;
245 struct refinfo
*info
= cgit_mk_refinfo(refname
, oid
);
248 cgit_add_ref(list
, info
);
252 void cgit_diff_tree_cb(struct diff_queue_struct
*q
,
253 struct diff_options
*options
, void *data
)
257 for (i
= 0; i
< q
->nr
; i
++) {
258 if (q
->queue
[i
]->status
== 'U')
260 ((filepair_fn
)data
)(q
->queue
[i
]);
264 static int load_mmfile(mmfile_t
*file
, const unsigned char *sha1
)
266 enum object_type type
;
268 if (is_null_sha1(sha1
)) {
269 file
->ptr
= (char *)"";
272 file
->ptr
= read_sha1_file(sha1
, &type
,
273 (unsigned long *)&file
->size
);
279 * Receive diff-buffers from xdiff and concatenate them as
280 * needed across multiple callbacks.
282 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
283 * ripped from git and modified to use globals instead of
284 * a special callback-struct.
286 static char *diffbuf
= NULL
;
287 static int buflen
= 0;
289 static int filediff_cb(void *priv
, mmbuffer_t
*mb
, int nbuf
)
293 for (i
= 0; i
< nbuf
; i
++) {
294 if (mb
[i
].ptr
[mb
[i
].size
-1] != '\n') {
295 /* Incomplete line */
296 diffbuf
= xrealloc(diffbuf
, buflen
+ mb
[i
].size
);
297 memcpy(diffbuf
+ buflen
, mb
[i
].ptr
, mb
[i
].size
);
298 buflen
+= mb
[i
].size
;
302 /* we have a complete line */
304 ((linediff_fn
)priv
)(mb
[i
].ptr
, mb
[i
].size
);
307 diffbuf
= xrealloc(diffbuf
, buflen
+ mb
[i
].size
);
308 memcpy(diffbuf
+ buflen
, mb
[i
].ptr
, mb
[i
].size
);
309 ((linediff_fn
)priv
)(diffbuf
, buflen
+ mb
[i
].size
);
315 ((linediff_fn
)priv
)(diffbuf
, buflen
);
323 int cgit_diff_files(const unsigned char *old_sha1
,
324 const unsigned char *new_sha1
, unsigned long *old_size
,
325 unsigned long *new_size
, int *binary
, int context
,
326 int ignorews
, linediff_fn fn
)
328 mmfile_t file1
, file2
;
329 xpparam_t diff_params
;
330 xdemitconf_t emit_params
;
333 if (!load_mmfile(&file1
, old_sha1
) || !load_mmfile(&file2
, new_sha1
))
336 *old_size
= file1
.size
;
337 *new_size
= file2
.size
;
339 if ((file1
.ptr
&& buffer_is_binary(file1
.ptr
, file1
.size
)) ||
340 (file2
.ptr
&& buffer_is_binary(file2
.ptr
, file2
.size
))) {
349 memset(&diff_params
, 0, sizeof(diff_params
));
350 memset(&emit_params
, 0, sizeof(emit_params
));
351 memset(&emit_cb
, 0, sizeof(emit_cb
));
352 diff_params
.flags
= XDF_NEED_MINIMAL
;
354 diff_params
.flags
|= XDF_IGNORE_WHITESPACE
;
355 emit_params
.ctxlen
= context
> 0 ? context
: 3;
356 emit_params
.flags
= XDL_EMIT_FUNCNAMES
;
357 emit_cb
.outf
= filediff_cb
;
359 xdl_diff(&file1
, &file2
, &diff_params
, &emit_params
, &emit_cb
);
367 void cgit_diff_tree(const unsigned char *old_sha1
,
368 const unsigned char *new_sha1
,
369 filepair_fn fn
, const char *prefix
, int ignorews
)
371 struct diff_options opt
;
372 struct pathspec_item item
;
374 memset(&item
, 0, sizeof(item
));
376 opt
.output_format
= DIFF_FORMAT_CALLBACK
;
377 opt
.detect_rename
= 1;
378 opt
.rename_limit
= ctx
.cfg
.renamelimit
;
379 DIFF_OPT_SET(&opt
, RECURSIVE
);
381 DIFF_XDL_SET(&opt
, IGNORE_WHITESPACE
);
382 opt
.format_callback
= cgit_diff_tree_cb
;
383 opt
.format_callback_data
= fn
;
386 item
.len
= strlen(prefix
);
388 opt
.pathspec
.items
= &item
;
390 diff_setup_done(&opt
);
392 if (old_sha1
&& !is_null_sha1(old_sha1
))
393 diff_tree_sha1(old_sha1
, new_sha1
, "", &opt
);
395 diff_root_tree_sha1(new_sha1
, "", &opt
);
400 void cgit_diff_commit(struct commit
*commit
, filepair_fn fn
, const char *prefix
)
402 unsigned char *old_sha1
= NULL
;
405 old_sha1
= commit
->parents
->item
->object
.sha1
;
406 cgit_diff_tree(old_sha1
, commit
->object
.sha1
, fn
, prefix
,
410 int cgit_parse_snapshots_mask(const char *str
)
412 struct string_list tokens
= STRING_LIST_INIT_DUP
;
413 struct string_list_item
*item
;
414 const struct cgit_snapshot_format
*f
;
417 /* favor legacy setting */
421 string_list_split(&tokens
, str
, ' ', -1);
422 string_list_remove_empty_items(&tokens
, 0);
424 for_each_string_list_item(item
, &tokens
) {
425 for (f
= cgit_snapshot_formats
; f
->suffix
; f
++) {
426 if (!strcmp(item
->string
, f
->suffix
) ||
427 !strcmp(item
->string
, f
->suffix
+ 1)) {
434 string_list_clear(&tokens
, 0);
443 void cgit_prepare_repo_env(struct cgit_repo
* repo
)
445 cgit_env_var env_vars
[] = {
446 { .name
= "CGIT_REPO_URL", .value
= repo
->url
},
447 { .name
= "CGIT_REPO_NAME", .value
= repo
->name
},
448 { .name
= "CGIT_REPO_PATH", .value
= repo
->path
},
449 { .name
= "CGIT_REPO_OWNER", .value
= repo
->owner
},
450 { .name
= "CGIT_REPO_DEFBRANCH", .value
= repo
->defbranch
},
451 { .name
= "CGIT_REPO_SECTION", .value
= repo
->section
},
452 { .name
= "CGIT_REPO_CLONE_URL", .value
= repo
->clone_url
}
454 int env_var_count
= ARRAY_SIZE(env_vars
);
456 static char *warn
= "cgit warning: failed to set env: %s=%s\n";
459 q
= p
+ env_var_count
;
461 if (p
->value
&& setenv(p
->name
, p
->value
, 1))
462 fprintf(stderr
, warn
, p
->name
, p
->value
);
465 /* Read the content of the specified file into a newly allocated buffer,
466 * zeroterminate the buffer and return 0 on success, errno otherwise.
468 int readfile(const char *path
, char **buf
, size_t *size
)
473 fd
= open(path
, O_RDONLY
);
476 if (fstat(fd
, &st
)) {
481 if (!S_ISREG(st
.st_mode
)) {
485 *buf
= xmalloc(st
.st_size
+ 1);
486 *size
= read_in_full(fd
, *buf
, st
.st_size
);
488 (*buf
)[*size
] = '\0';
490 return (*size
== st
.st_size
? 0 : e
);
493 static int is_token_char(char c
)
495 return isalnum(c
) || c
== '_';
498 /* Replace name with getenv(name), return pointer to zero-terminating char
500 static char *expand_macro(char *name
, int maxlength
)
506 value
= getenv(name
);
511 strncpy(name
, value
, len
);
516 #define EXPBUFSIZE (1024 * 8)
518 /* Replace all tokens prefixed by '$' in the specified text with the
519 * value of the named environment variable.
520 * NB: the return value is a static buffer, i.e. it must be strdup'd
523 char *expand_macros(const char *txt
)
525 static char result
[EXPBUFSIZE
];
531 while (p
< result
+ EXPBUFSIZE
- 1 && txt
&& *txt
) {
534 if (!is_token_char(*txt
)) {
537 len
= result
+ EXPBUFSIZE
- start
- 1;
538 p
= expand_macro(start
, len
) - 1;
556 if (start
&& p
- start
> 0) {
557 len
= result
+ EXPBUFSIZE
- start
- 1;
558 p
= expand_macro(start
, len
);