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
;
56 ret
->extra_head_content
= NULL
;
59 ret
->section
= ctx
.cfg
.section
;
60 ret
->snapshots
= ctx
.cfg
.snapshots
;
61 ret
->enable_blame
= ctx
.cfg
.enable_blame
;
62 ret
->enable_commit_graph
= ctx
.cfg
.enable_commit_graph
;
63 ret
->enable_log_filecount
= ctx
.cfg
.enable_log_filecount
;
64 ret
->enable_log_linecount
= ctx
.cfg
.enable_log_linecount
;
65 ret
->enable_remote_branches
= ctx
.cfg
.enable_remote_branches
;
66 ret
->enable_subject_links
= ctx
.cfg
.enable_subject_links
;
67 ret
->enable_html_serving
= ctx
.cfg
.enable_html_serving
;
68 ret
->max_stats
= ctx
.cfg
.max_stats
;
69 ret
->branch_sort
= ctx
.cfg
.branch_sort
;
70 ret
->commit_sort
= ctx
.cfg
.commit_sort
;
71 ret
->module_link
= ctx
.cfg
.module_link
;
72 ret
->readme
= ctx
.cfg
.readme
;
74 ret
->about_filter
= ctx
.cfg
.about_filter
;
75 ret
->commit_filter
= ctx
.cfg
.commit_filter
;
76 ret
->source_filter
= ctx
.cfg
.source_filter
;
77 ret
->email_filter
= ctx
.cfg
.email_filter
;
78 ret
->owner_filter
= ctx
.cfg
.owner_filter
;
79 ret
->clone_url
= ctx
.cfg
.clone_url
;
80 ret
->submodules
.strdup_strings
= 1;
81 ret
->hide
= ret
->ignore
= 0;
85 struct cgit_repo
*cgit_get_repoinfo(const char *url
)
88 struct cgit_repo
*repo
;
90 for (i
= 0; i
< cgit_repolist
.count
; i
++) {
91 repo
= &cgit_repolist
.repos
[i
];
94 if (!strcmp(repo
->url
, url
))
100 void cgit_free_commitinfo(struct commitinfo
*info
)
103 free(info
->author_email
);
104 free(info
->committer
);
105 free(info
->committer_email
);
108 free(info
->msg_encoding
);
112 char *trim_end(const char *str
, char c
)
119 while (len
> 0 && str
[len
- 1] == c
)
123 return xstrndup(str
, len
);
126 char *ensure_end(const char *str
, char c
)
128 size_t len
= strlen(str
);
131 if (len
&& str
[len
- 1] == c
)
132 return xstrndup(str
, len
);
134 result
= xmalloc(len
+ 2);
135 memcpy(result
, str
, len
);
137 result
[len
+ 1] = '\0';
141 void strbuf_ensure_end(struct strbuf
*sb
, char c
)
143 if (!sb
->len
|| sb
->buf
[sb
->len
- 1] != c
)
147 void cgit_add_ref(struct reflist
*list
, struct refinfo
*ref
)
151 if (list
->count
>= list
->alloc
) {
152 list
->alloc
+= (list
->alloc
? list
->alloc
: 4);
153 size
= list
->alloc
* sizeof(struct refinfo
*);
154 list
->refs
= xrealloc(list
->refs
, size
);
156 list
->refs
[list
->count
++] = ref
;
159 static struct refinfo
*cgit_mk_refinfo(const char *refname
, const struct object_id
*oid
)
163 ref
= xmalloc(sizeof (struct refinfo
));
164 ref
->refname
= xstrdup(refname
);
165 ref
->object
= parse_object(the_repository
, oid
);
166 switch (ref
->object
->type
) {
168 ref
->tag
= cgit_parse_tag((struct tag
*)ref
->object
);
171 ref
->commit
= cgit_parse_commit((struct commit
*)ref
->object
);
177 void cgit_free_taginfo(struct taginfo
*tag
)
181 if (tag
->tagger_email
)
182 free(tag
->tagger_email
);
188 static void cgit_free_refinfo(struct refinfo
*ref
)
191 free((char *)ref
->refname
);
192 switch (ref
->object
->type
) {
194 cgit_free_taginfo(ref
->tag
);
197 cgit_free_commitinfo(ref
->commit
);
203 void cgit_free_reflist_inner(struct reflist
*list
)
207 for (i
= 0; i
< list
->count
; i
++) {
208 cgit_free_refinfo(list
->refs
[i
]);
213 int cgit_refs_cb(const char *refname
, const struct object_id
*oid
, int flags
,
216 struct reflist
*list
= (struct reflist
*)cb_data
;
217 struct refinfo
*info
= cgit_mk_refinfo(refname
, oid
);
220 cgit_add_ref(list
, info
);
224 void cgit_diff_tree_cb(struct diff_queue_struct
*q
,
225 struct diff_options
*options
, void *data
)
229 for (i
= 0; i
< q
->nr
; i
++) {
230 if (q
->queue
[i
]->status
== 'U')
232 ((filepair_fn
)data
)(q
->queue
[i
]);
236 static int load_mmfile(mmfile_t
*file
, const struct object_id
*oid
)
238 enum object_type type
;
240 if (is_null_oid(oid
)) {
241 file
->ptr
= (char *)"";
244 file
->ptr
= read_object_file(oid
, &type
,
245 (unsigned long *)&file
->size
);
251 * Receive diff-buffers from xdiff and concatenate them as
252 * needed across multiple callbacks.
254 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
255 * ripped from git and modified to use globals instead of
256 * a special callback-struct.
258 static char *diffbuf
= NULL
;
259 static int buflen
= 0;
261 static int filediff_cb(void *priv
, mmbuffer_t
*mb
, int nbuf
)
265 for (i
= 0; i
< nbuf
; i
++) {
266 if (mb
[i
].ptr
[mb
[i
].size
-1] != '\n') {
267 /* Incomplete line */
268 diffbuf
= xrealloc(diffbuf
, buflen
+ mb
[i
].size
);
269 memcpy(diffbuf
+ buflen
, mb
[i
].ptr
, mb
[i
].size
);
270 buflen
+= mb
[i
].size
;
274 /* we have a complete line */
276 ((linediff_fn
)priv
)(mb
[i
].ptr
, mb
[i
].size
);
279 diffbuf
= xrealloc(diffbuf
, buflen
+ mb
[i
].size
);
280 memcpy(diffbuf
+ buflen
, mb
[i
].ptr
, mb
[i
].size
);
281 ((linediff_fn
)priv
)(diffbuf
, buflen
+ mb
[i
].size
);
287 ((linediff_fn
)priv
)(diffbuf
, buflen
);
295 int cgit_diff_files(const struct object_id
*old_oid
,
296 const struct object_id
*new_oid
, unsigned long *old_size
,
297 unsigned long *new_size
, int *binary
, int context
,
298 int ignorews
, linediff_fn fn
)
300 mmfile_t file1
, file2
;
301 xpparam_t diff_params
;
302 xdemitconf_t emit_params
;
305 if (!load_mmfile(&file1
, old_oid
) || !load_mmfile(&file2
, new_oid
))
308 *old_size
= file1
.size
;
309 *new_size
= file2
.size
;
311 if ((file1
.ptr
&& buffer_is_binary(file1
.ptr
, file1
.size
)) ||
312 (file2
.ptr
&& buffer_is_binary(file2
.ptr
, file2
.size
))) {
321 memset(&diff_params
, 0, sizeof(diff_params
));
322 memset(&emit_params
, 0, sizeof(emit_params
));
323 memset(&emit_cb
, 0, sizeof(emit_cb
));
324 diff_params
.flags
= XDF_NEED_MINIMAL
;
326 diff_params
.flags
|= XDF_IGNORE_WHITESPACE
;
327 emit_params
.ctxlen
= context
> 0 ? context
: 3;
328 emit_params
.flags
= XDL_EMIT_FUNCNAMES
;
329 emit_cb
.out_line
= filediff_cb
;
331 xdl_diff(&file1
, &file2
, &diff_params
, &emit_params
, &emit_cb
);
339 void cgit_diff_tree(const struct object_id
*old_oid
,
340 const struct object_id
*new_oid
,
341 filepair_fn fn
, const char *prefix
, int ignorews
)
343 struct diff_options opt
;
344 struct pathspec_item
*item
;
347 opt
.output_format
= DIFF_FORMAT_CALLBACK
;
348 opt
.detect_rename
= 1;
349 opt
.rename_limit
= ctx
.cfg
.renamelimit
;
350 opt
.flags
.recursive
= 1;
352 DIFF_XDL_SET(&opt
, IGNORE_WHITESPACE
);
353 opt
.format_callback
= cgit_diff_tree_cb
;
354 opt
.format_callback_data
= fn
;
356 item
= xcalloc(1, sizeof(*item
));
357 item
->match
= xstrdup(prefix
);
358 item
->len
= strlen(prefix
);
360 opt
.pathspec
.items
= item
;
362 diff_setup_done(&opt
);
364 if (old_oid
&& !is_null_oid(old_oid
))
365 diff_tree_oid(old_oid
, new_oid
, "", &opt
);
367 diff_root_tree_oid(new_oid
, "", &opt
);
372 void cgit_diff_commit(struct commit
*commit
, filepair_fn fn
, const char *prefix
)
374 const struct object_id
*old_oid
= NULL
;
377 old_oid
= &commit
->parents
->item
->object
.oid
;
378 cgit_diff_tree(old_oid
, &commit
->object
.oid
, fn
, prefix
,
382 int cgit_parse_snapshots_mask(const char *str
)
384 struct string_list tokens
= STRING_LIST_INIT_DUP
;
385 struct string_list_item
*item
;
386 const struct cgit_snapshot_format
*f
;
389 /* favor legacy setting */
393 if (strcmp(str
, "all") == 0)
396 string_list_split(&tokens
, str
, ' ', -1);
397 string_list_remove_empty_items(&tokens
, 0);
399 for_each_string_list_item(item
, &tokens
) {
400 for (f
= cgit_snapshot_formats
; f
->suffix
; f
++) {
401 if (!strcmp(item
->string
, f
->suffix
) ||
402 !strcmp(item
->string
, f
->suffix
+ 1)) {
403 rv
|= cgit_snapshot_format_bit(f
);
409 string_list_clear(&tokens
, 0);
418 void cgit_prepare_repo_env(struct cgit_repo
* repo
)
420 cgit_env_var env_vars
[] = {
421 { .name
= "CGIT_REPO_URL", .value
= repo
->url
},
422 { .name
= "CGIT_REPO_NAME", .value
= repo
->name
},
423 { .name
= "CGIT_REPO_PATH", .value
= repo
->path
},
424 { .name
= "CGIT_REPO_OWNER", .value
= repo
->owner
},
425 { .name
= "CGIT_REPO_DEFBRANCH", .value
= repo
->defbranch
},
426 { .name
= "CGIT_REPO_SECTION", .value
= repo
->section
},
427 { .name
= "CGIT_REPO_CLONE_URL", .value
= repo
->clone_url
}
429 int env_var_count
= ARRAY_SIZE(env_vars
);
431 static char *warn
= "cgit warning: failed to set env: %s=%s\n";
434 q
= p
+ env_var_count
;
436 if (p
->value
&& setenv(p
->name
, p
->value
, 1))
437 fprintf(stderr
, warn
, p
->name
, p
->value
);
440 /* Read the content of the specified file into a newly allocated buffer,
441 * zeroterminate the buffer and return 0 on success, errno otherwise.
443 int readfile(const char *path
, char **buf
, size_t *size
)
448 fd
= open(path
, O_RDONLY
);
451 if (fstat(fd
, &st
)) {
456 if (!S_ISREG(st
.st_mode
)) {
460 *buf
= xmalloc(st
.st_size
+ 1);
461 *size
= read_in_full(fd
, *buf
, st
.st_size
);
463 (*buf
)[*size
] = '\0';
465 return (*size
== st
.st_size
? 0 : e
);
468 static int is_token_char(char c
)
470 return isalnum(c
) || c
== '_';
473 /* Replace name with getenv(name), return pointer to zero-terminating char
475 static char *expand_macro(char *name
, int maxlength
)
481 value
= getenv(name
);
483 len
= strlen(value
) + 1;
486 strlcpy(name
, value
, len
);
492 #define EXPBUFSIZE (1024 * 8)
494 /* Replace all tokens prefixed by '$' in the specified text with the
495 * value of the named environment variable.
496 * NB: the return value is a static buffer, i.e. it must be strdup'd
499 char *expand_macros(const char *txt
)
501 static char result
[EXPBUFSIZE
];
507 while (p
< result
+ EXPBUFSIZE
- 1 && txt
&& *txt
) {
510 if (!is_token_char(*txt
)) {
513 len
= result
+ EXPBUFSIZE
- start
- 1;
514 p
= expand_macro(start
, len
) - 1;
532 if (start
&& p
- start
> 0) {
533 len
= result
+ EXPBUFSIZE
- start
- 1;
534 p
= expand_macro(start
, len
);
540 char *get_mimetype_for_filename(const char *filename
)
542 char *ext
, *mimetype
, *token
, line
[1024], *saveptr
;
544 struct string_list_item
*mime
;
549 ext
= strrchr(filename
, '.');
555 mime
= string_list_lookup(&ctx
.cfg
.mimetypes
, ext
);
557 return xstrdup(mime
->util
);
559 if (!ctx
.cfg
.mimetype_file
)
561 file
= fopen(ctx
.cfg
.mimetype_file
, "r");
564 while (fgets(line
, sizeof(line
), file
)) {
565 if (!line
[0] || line
[0] == '#')
567 mimetype
= strtok_r(line
, " \t\r\n", &saveptr
);
568 while ((token
= strtok_r(NULL
, " \t\r\n", &saveptr
))) {
569 if (!strcasecmp(ext
, token
)) {
571 return xstrdup(mimetype
);