2 * Copyright (c) 2019, 2020 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2018, 2019 Stefan Sperling <stsp@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <sys/queue.h>
20 #include <sys/types.h>
35 #include <got_error.h>
36 #include <got_object.h>
37 #include <got_reference.h>
38 #include <got_repository.h>
40 #include <got_cancel.h>
41 #include <got_worktree.h>
43 #include <got_commit_graph.h>
44 #include <got_blame.h>
45 #include <got_privsep.h>
46 #include <got_opentemp.h>
54 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
58 TAILQ_HEAD(headers
, gw_header
) gw_headers
;
59 TAILQ_HEAD(dirs
, gw_dir
) gw_dirs
;
60 struct got_repository
*repo
;
61 struct gw_dir
*gw_dir
;
62 struct gotweb_config
*gw_conf
;
63 struct ktemplate
*gw_tmpl
;
64 struct khtmlreq
*gw_html_req
;
66 const struct got_error
*error
;
67 const char *repo_name
;
72 const char *repo_file
;
77 unsigned int repos_total
;
83 TAILQ_ENTRY(gw_header
) entry
;
84 struct got_reflist_head refs
;
88 char *commit_id
; /* id_str1 */
89 char *parent_id
; /* id_str2 */
94 time_t committer_time
;
98 TAILQ_ENTRY(gw_dir
) entry
;
140 static const char *const gw_templs
[TEMPL__MAX
] = {
150 static const struct kvalid gw_keys
[KEY__ZMAX
] = {
151 { kvalid_stringne
, "action" },
152 { kvalid_stringne
, "commit" },
153 { kvalid_stringne
, "file" },
154 { kvalid_stringne
, "folder" },
155 { kvalid_stringne
, "headref" },
156 { kvalid_int
, "page" },
157 { kvalid_stringne
, "path" },
158 { kvalid_stringne
, "prev" },
161 static struct gw_header
*gw_init_header(void);
163 static void gw_free_header(struct gw_header
*);
165 static int gw_template(size_t, void *);
167 static const struct got_error
*gw_error(struct gw_trans
*);
168 static const struct got_error
*gw_init_gw_dir(struct gw_dir
**, const char *);
169 static const struct got_error
*gw_get_repo_description(char **,
170 struct gw_trans
*, char *);
171 static const struct got_error
*gw_get_repo_owner(char **, struct gw_trans
*,
173 static const struct got_error
*gw_get_time_str(char **, time_t, int);
174 static const struct got_error
*gw_get_repo_age(char **, struct gw_trans
*,
175 char *, const char *, int);
176 static const struct got_error
*gw_output_file_blame(struct gw_trans
*,
178 static const struct got_error
*gw_output_blob_buf(struct gw_trans
*,
180 static const struct got_error
*gw_output_repo_tree(struct gw_trans
*,
182 static const struct got_error
*gw_output_diff(struct gw_trans
*,
184 static const struct got_error
*gw_output_repo_tags(struct gw_trans
*,
185 struct gw_header
*, int, int);
186 static const struct got_error
*gw_output_repo_heads(struct gw_trans
*);
187 static const struct got_error
*gw_output_site_link(struct gw_trans
*);
188 static const struct got_error
*gw_get_clone_url(char **, struct gw_trans
*,
190 static const struct got_error
*gw_colordiff_line(struct gw_trans
*, char *);
192 static const struct got_error
*gw_gen_commit_header(struct gw_trans
*, char *,
194 static const struct got_error
*gw_gen_diff_header(struct gw_trans
*, char *,
196 static const struct got_error
*gw_gen_author_header(struct gw_trans
*,
198 static const struct got_error
*gw_gen_age_header(struct gw_trans
*,
200 static const struct got_error
*gw_gen_committer_header(struct gw_trans
*,
202 static const struct got_error
*gw_gen_commit_msg_header(struct gw_trans
*,
204 static const struct got_error
*gw_gen_tree_header(struct gw_trans
*, char *);
205 static const struct got_error
*gw_display_open(struct gw_trans
*, enum khttp
,
207 static const struct got_error
*gw_display_index(struct gw_trans
*);
208 static const struct got_error
*gw_get_header(struct gw_trans
*,
209 struct gw_header
*, int);
210 static const struct got_error
*gw_get_commits(struct gw_trans
*,
211 struct gw_header
*, int,
212 struct got_object_id
*);
213 static const struct got_error
*gw_get_commit(struct gw_trans
*,
215 struct got_commit_object
*,
216 struct got_object_id
*);
217 static const struct got_error
*gw_apply_unveil(const char *);
218 static const struct got_error
*gw_blame_cb(void *, int, int,
219 struct got_commit_object
*,
220 struct got_object_id
*);
221 static const struct got_error
*gw_load_got_paths(struct gw_trans
*);
222 static const struct got_error
*gw_load_got_path(struct gw_trans
*,
224 static const struct got_error
*gw_parse_querystring(struct gw_trans
*);
225 static const struct got_error
*gw_blame(struct gw_trans
*);
226 static const struct got_error
*gw_blob(struct gw_trans
*);
227 static const struct got_error
*gw_diff(struct gw_trans
*);
228 static const struct got_error
*gw_index(struct gw_trans
*);
229 static const struct got_error
*gw_commits(struct gw_trans
*);
230 static const struct got_error
*gw_briefs(struct gw_trans
*);
231 static const struct got_error
*gw_summary(struct gw_trans
*);
232 static const struct got_error
*gw_tree(struct gw_trans
*);
233 static const struct got_error
*gw_tag(struct gw_trans
*);
234 static const struct got_error
*gw_tags(struct gw_trans
*);
236 struct gw_query_action
{
237 unsigned int func_id
;
238 const char *func_name
;
239 const struct got_error
*(*func_main
)(struct gw_trans
*);
240 const char *template;
243 enum gw_query_actions
{
257 static const struct gw_query_action gw_query_funcs
[] = {
258 { GW_BLAME
, "blame", gw_blame
, "gw_tmpl/blame.tmpl" },
259 { GW_BLOB
, "blob", NULL
, NULL
},
260 { GW_BRIEFS
, "briefs", gw_briefs
, "gw_tmpl/briefs.tmpl" },
261 { GW_COMMITS
, "commits", gw_commits
, "gw_tmpl/commit.tmpl" },
262 { GW_DIFF
, "diff", gw_diff
, "gw_tmpl/diff.tmpl" },
263 { GW_ERR
, "error", gw_error
, "gw_tmpl/err.tmpl" },
264 { GW_INDEX
, "index", gw_index
, "gw_tmpl/index.tmpl" },
265 { GW_SUMMARY
, "summary", gw_summary
, "gw_tmpl/summry.tmpl" },
266 { GW_TAG
, "tag", gw_tag
, "gw_tmpl/tag.tmpl" },
267 { GW_TAGS
, "tags", gw_tags
, "gw_tmpl/tags.tmpl" },
268 { GW_TREE
, "tree", gw_tree
, "gw_tmpl/tree.tmpl" },
272 gw_get_action_name(struct gw_trans
*gw_trans
)
274 return gw_query_funcs
[gw_trans
->action
].func_name
;
277 static const struct got_error
*
278 gw_kcgi_error(enum kcgi_err kerr
)
283 if (kerr
== KCGI_EXIT
|| kerr
== KCGI_HUP
)
284 return got_error(GOT_ERR_CANCELLED
);
286 if (kerr
== KCGI_ENOMEM
)
287 return got_error_set_errno(ENOMEM
,
288 kcgi_strerror(kerr
));
290 if (kerr
== KCGI_ENFILE
)
291 return got_error_set_errno(ENFILE
,
292 kcgi_strerror(kerr
));
294 if (kerr
== KCGI_EAGAIN
)
295 return got_error_set_errno(EAGAIN
,
296 kcgi_strerror(kerr
));
298 if (kerr
== KCGI_FORM
)
299 return got_error_msg(GOT_ERR_IO
,
300 kcgi_strerror(kerr
));
302 return got_error_from_errno(kcgi_strerror(kerr
));
305 static const struct got_error
*
306 gw_apply_unveil(const char *repo_path
)
308 const struct got_error
*err
;
311 if (unveil("gmon.out", "rwc") != 0)
312 return got_error_from_errno2("unveil", "gmon.out");
314 if (repo_path
&& unveil(repo_path
, "r") != 0)
315 return got_error_from_errno2("unveil", repo_path
);
317 if (unveil(GOT_TMPDIR_STR
, "rwc") != 0)
318 return got_error_from_errno2("unveil", GOT_TMPDIR_STR
);
320 err
= got_privsep_unveil_exec_helpers();
324 if (unveil(NULL
, NULL
) != 0)
325 return got_error_from_errno("unveil");
331 isbinary(const uint8_t *buf
, size_t n
)
335 for (i
= 0; i
< n
; i
++)
341 static const struct got_error
*
342 gw_blame(struct gw_trans
*gw_trans
)
344 const struct got_error
*error
= NULL
;
345 struct gw_header
*header
= NULL
;
347 enum kcgi_err kerr
= KCGI_OK
;
350 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
352 return got_error_from_errno("pledge");
354 if ((header
= gw_init_header()) == NULL
)
355 return got_error_from_errno("malloc");
357 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
361 /* check querystring */
362 if (gw_trans
->repo_file
== NULL
) {
363 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
364 "file required in querystring");
367 if (gw_trans
->commit_id
== NULL
) {
368 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
369 "commit required in querystring");
373 error
= gw_get_header(gw_trans
, header
, 1);
376 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
377 "blame_header_wrapper", KATTR__MAX
);
380 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
381 "blame_header", KATTR__MAX
);
384 error
= gw_get_time_str(&age
, header
->committer_time
,
388 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
391 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
394 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
397 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
398 "dotted_line", KATTR__MAX
);
401 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
405 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
406 "blame", KATTR__MAX
);
409 error
= gw_output_file_blame(gw_trans
, header
);
412 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
414 gw_free_header(header
);
415 if (error
== NULL
&& kerr
!= KCGI_OK
)
416 error
= gw_kcgi_error(kerr
);
420 static const struct got_error
*
421 gw_blob(struct gw_trans
*gw_trans
)
423 const struct got_error
*error
= NULL
, *err
= NULL
;
424 struct gw_header
*header
= NULL
;
425 enum kcgi_err kerr
= KCGI_OK
;
428 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
430 return got_error_from_errno("pledge");
432 if ((header
= gw_init_header()) == NULL
)
433 return got_error_from_errno("malloc");
435 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
439 /* check querystring */
440 if (gw_trans
->repo_file
== NULL
) {
441 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
442 "file required in querystring");
445 if (gw_trans
->commit_id
== NULL
) {
446 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
447 "commit required in querystring");
450 error
= gw_get_header(gw_trans
, header
, 1);
454 error
= gw_output_blob_buf(gw_trans
, header
);
457 gw_trans
->mime
= KMIME_TEXT_PLAIN
;
458 err
= gw_display_index(gw_trans
);
463 kerr
= khttp_puts(gw_trans
->gw_req
, error
->msg
);
466 gw_free_header(header
);
467 if (error
== NULL
&& kerr
!= KCGI_OK
)
468 error
= gw_kcgi_error(kerr
);
472 static const struct got_error
*
473 gw_diff(struct gw_trans
*gw_trans
)
475 const struct got_error
*error
= NULL
;
476 struct gw_header
*header
= NULL
;
478 enum kcgi_err kerr
= KCGI_OK
;
481 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
483 return got_error_from_errno("pledge");
485 if ((header
= gw_init_header()) == NULL
)
486 return got_error_from_errno("malloc");
488 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
492 error
= gw_get_header(gw_trans
, header
, 1);
496 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
497 "diff_header_wrapper", KATTR__MAX
);
500 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
501 "diff_header", KATTR__MAX
);
504 error
= gw_gen_diff_header(gw_trans
, header
->parent_id
,
508 error
= gw_gen_commit_header(gw_trans
, header
->commit_id
,
512 error
= gw_gen_tree_header(gw_trans
, header
->tree_id
);
515 error
= gw_gen_author_header(gw_trans
, header
->author
);
518 error
= gw_gen_committer_header(gw_trans
, header
->author
);
521 error
= gw_get_time_str(&age
, header
->committer_time
,
525 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
528 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
531 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
534 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
535 "dotted_line", KATTR__MAX
);
538 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
542 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
546 error
= gw_output_diff(gw_trans
, header
);
550 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
552 gw_free_header(header
);
554 if (error
== NULL
&& kerr
!= KCGI_OK
)
555 error
= gw_kcgi_error(kerr
);
559 static const struct got_error
*
560 gw_index(struct gw_trans
*gw_trans
)
562 const struct got_error
*error
= NULL
;
563 struct gw_dir
*gw_dir
= NULL
;
564 char *href_next
= NULL
, *href_prev
= NULL
, *href_summary
= NULL
;
565 char *href_briefs
= NULL
, *href_commits
= NULL
, *href_tree
= NULL
;
566 char *href_tags
= NULL
;
567 unsigned int prev_disp
= 0, next_disp
= 1, dir_c
= 0;
568 enum kcgi_err kerr
= KCGI_OK
;
571 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
573 error
= got_error_from_errno("pledge");
577 error
= gw_apply_unveil(gw_trans
->gw_conf
->got_repos_path
);
581 error
= gw_load_got_paths(gw_trans
);
585 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
586 "index_header", KATTR__MAX
);
588 return gw_kcgi_error(kerr
);
589 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
590 "index_header_project", KATTR__MAX
);
592 return gw_kcgi_error(kerr
);
593 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Project");
595 return gw_kcgi_error(kerr
);
596 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
598 return gw_kcgi_error(kerr
);
600 if (gw_trans
->gw_conf
->got_show_repo_description
) {
601 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
602 "index_header_description", KATTR__MAX
);
604 return gw_kcgi_error(kerr
);
605 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Description");
607 return gw_kcgi_error(kerr
);
608 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
610 return gw_kcgi_error(kerr
);
613 if (gw_trans
->gw_conf
->got_show_repo_owner
) {
614 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
615 "index_header_owner", KATTR__MAX
);
617 return gw_kcgi_error(kerr
);
618 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Owner");
620 return gw_kcgi_error(kerr
);
621 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
623 return gw_kcgi_error(kerr
);
626 if (gw_trans
->gw_conf
->got_show_repo_age
) {
627 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
628 "index_header_age", KATTR__MAX
);
630 return gw_kcgi_error(kerr
);
631 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Last Change");
633 return gw_kcgi_error(kerr
);
634 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
636 return gw_kcgi_error(kerr
);
639 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
641 return gw_kcgi_error(kerr
);
643 if (TAILQ_EMPTY(&gw_trans
->gw_dirs
)) {
644 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
645 "index_wrapper", KATTR__MAX
);
647 return gw_kcgi_error(kerr
);
648 kerr
= khtml_printf(gw_trans
->gw_html_req
,
649 "No repositories found in %s",
650 gw_trans
->gw_conf
->got_repos_path
);
652 return gw_kcgi_error(kerr
);
653 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
655 return gw_kcgi_error(kerr
);
656 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
657 "dotted_line", KATTR__MAX
);
659 return gw_kcgi_error(kerr
);
660 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
662 return gw_kcgi_error(kerr
);
666 TAILQ_FOREACH(gw_dir
, &gw_trans
->gw_dirs
, entry
)
669 TAILQ_FOREACH(gw_dir
, &gw_trans
->gw_dirs
, entry
) {
670 if (gw_trans
->page
> 0 && (gw_trans
->page
*
671 gw_trans
->gw_conf
->got_max_repos_display
) > prev_disp
) {
678 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
679 "index_wrapper", KATTR__MAX
);
683 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
684 gw_dir
->name
, "action", "summary", NULL
);
685 if (href_summary
== NULL
) {
686 error
= got_error_from_errno("khttp_urlpart");
689 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
690 "index_project", KATTR__MAX
);
693 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
694 href_summary
, KATTR__MAX
);
697 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_dir
->name
);
700 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
703 if (gw_trans
->gw_conf
->got_show_repo_description
) {
704 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
705 KATTR_ID
, "index_project_description", KATTR__MAX
);
708 kerr
= khtml_puts(gw_trans
->gw_html_req
,
709 gw_dir
->description
? gw_dir
->description
: "");
712 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
716 if (gw_trans
->gw_conf
->got_show_repo_owner
) {
717 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
718 KATTR_ID
, "index_project_owner", KATTR__MAX
);
721 kerr
= khtml_puts(gw_trans
->gw_html_req
,
722 gw_dir
->owner
? gw_dir
->owner
: "");
725 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
729 if (gw_trans
->gw_conf
->got_show_repo_age
) {
730 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
731 KATTR_ID
, "index_project_age", KATTR__MAX
);
734 kerr
= khtml_puts(gw_trans
->gw_html_req
,
735 gw_dir
->age
? gw_dir
->age
: "");
738 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
743 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
744 "navs_wrapper", KATTR__MAX
);
747 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
752 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
753 href_summary
, KATTR__MAX
);
756 kerr
= khtml_puts(gw_trans
->gw_html_req
, "summary");
759 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
763 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
767 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
768 gw_dir
->name
, "action", "briefs", NULL
);
769 if (href_briefs
== NULL
) {
770 error
= got_error_from_errno("khttp_urlpart");
773 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
774 href_briefs
, KATTR__MAX
);
777 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commit briefs");
780 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
782 error
= gw_kcgi_error(kerr
);
784 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
788 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
789 gw_dir
->name
, "action", "commits", NULL
);
790 if (href_commits
== NULL
) {
791 error
= got_error_from_errno("khttp_urlpart");
794 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
795 href_commits
, KATTR__MAX
);
798 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
801 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
805 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
809 href_tags
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
810 gw_dir
->name
, "action", "tags", NULL
);
811 if (href_tags
== NULL
) {
812 error
= got_error_from_errno("khttp_urlpart");
815 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
816 href_tags
, KATTR__MAX
);
819 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tags");
822 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
826 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
830 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
831 gw_dir
->name
, "action", "tree", NULL
);
832 if (href_tree
== NULL
) {
833 error
= got_error_from_errno("khttp_urlpart");
836 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
837 href_tree
, KATTR__MAX
);
840 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tree");
844 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 4);
847 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
848 "dotted_line", KATTR__MAX
);
851 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
866 if (gw_trans
->gw_conf
->got_max_repos_display
== 0)
869 if ((next_disp
== gw_trans
->gw_conf
->got_max_repos_display
) ||
870 ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
871 (gw_trans
->page
> 0) &&
872 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
873 prev_disp
== gw_trans
->repos_total
))) {
874 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
875 KATTR_ID
, "np_wrapper", KATTR__MAX
);
878 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
879 KATTR_ID
, "nav_prev", KATTR__MAX
);
884 if ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
885 (gw_trans
->page
> 0) &&
886 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
887 prev_disp
== gw_trans
->repos_total
)) {
888 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "page",
889 KATTRX_INT
, (int64_t)(gw_trans
->page
- 1), NULL
);
890 if (href_prev
== NULL
) {
891 error
= got_error_from_errno("khttp_urlpartx");
894 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
895 KATTR_HREF
, href_prev
, KATTR__MAX
);
898 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
901 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
906 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
908 return gw_kcgi_error(kerr
);
910 if (gw_trans
->gw_conf
->got_max_repos_display
> 0 &&
911 next_disp
== gw_trans
->gw_conf
->got_max_repos_display
&&
912 dir_c
!= (gw_trans
->page
+ 1) *
913 gw_trans
->gw_conf
->got_max_repos_display
) {
914 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
915 KATTR_ID
, "nav_next", KATTR__MAX
);
918 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "page",
919 KATTRX_INT
, (int64_t)(gw_trans
->page
+ 1), NULL
);
920 if (href_next
== NULL
) {
921 error
= got_error_from_errno("khttp_urlpartx");
924 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
925 KATTR_HREF
, href_next
, KATTR__MAX
);
928 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
931 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
938 if ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
939 (gw_trans
->page
> 0) &&
940 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
941 prev_disp
== gw_trans
->repos_total
)) {
942 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
956 if (error
== NULL
&& kerr
!= KCGI_OK
)
957 error
= gw_kcgi_error(kerr
);
961 static const struct got_error
*
962 gw_commits(struct gw_trans
*gw_trans
)
964 const struct got_error
*error
= NULL
;
965 struct gw_header
*header
= NULL
, *n_header
= NULL
;
966 char *age
= NULL
, *href_diff
= NULL
, *href_tree
= NULL
;
967 char *href_prev
= NULL
, *href_next
= NULL
;
968 enum kcgi_err kerr
= KCGI_OK
;
969 int commit_found
= 0;
971 if ((header
= gw_init_header()) == NULL
)
972 return got_error_from_errno("malloc");
975 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
977 error
= got_error_from_errno("pledge");
981 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
985 error
= gw_get_header(gw_trans
, header
,
986 gw_trans
->gw_conf
->got_max_commits_display
);
990 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
) {
991 if (commit_found
== 0 && gw_trans
->commit_id
!= NULL
) {
992 if (strcmp(gw_trans
->commit_id
,
993 n_header
->commit_id
) != 0)
998 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
999 "commits_line_wrapper", KATTR__MAX
);
1000 if (kerr
!= KCGI_OK
)
1002 error
= gw_gen_commit_header(gw_trans
, n_header
->commit_id
,
1003 n_header
->refs_str
);
1006 error
= gw_gen_author_header(gw_trans
, n_header
->author
);
1009 error
= gw_gen_committer_header(gw_trans
, n_header
->author
);
1012 error
= gw_get_time_str(&age
, n_header
->committer_time
,
1016 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
1019 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1020 if (kerr
!= KCGI_OK
)
1023 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1024 "dotted_line", KATTR__MAX
);
1025 if (kerr
!= KCGI_OK
)
1027 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1028 if (kerr
!= KCGI_OK
)
1031 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1032 "commit", KATTR__MAX
);
1033 if (kerr
!= KCGI_OK
)
1035 kerr
= khttp_puts(gw_trans
->gw_req
, n_header
->commit_msg
);
1036 if (kerr
!= KCGI_OK
)
1038 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1039 if (kerr
!= KCGI_OK
)
1042 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1043 gw_trans
->repo_name
, "action", "diff", "commit",
1044 n_header
->commit_id
, NULL
);
1045 if (href_diff
== NULL
) {
1046 error
= got_error_from_errno("khttp_urlpart");
1049 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1050 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
1051 if (kerr
!= KCGI_OK
)
1053 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1054 KATTR_ID
, "navs", KATTR__MAX
);
1055 if (kerr
!= KCGI_OK
)
1057 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1058 KATTR_HREF
, href_diff
, KATTR__MAX
);
1059 if (kerr
!= KCGI_OK
)
1061 kerr
= khtml_puts(gw_trans
->gw_html_req
, "diff");
1062 if (kerr
!= KCGI_OK
)
1064 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1065 if (kerr
!= KCGI_OK
)
1068 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
1069 if (kerr
!= KCGI_OK
)
1072 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1073 gw_trans
->repo_name
, "action", "tree", "commit",
1074 n_header
->commit_id
, NULL
);
1075 if (href_tree
== NULL
) {
1076 error
= got_error_from_errno("khttp_urlpart");
1079 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1080 KATTR_HREF
, href_tree
, KATTR__MAX
);
1081 if (kerr
!= KCGI_OK
)
1083 khtml_puts(gw_trans
->gw_html_req
, "tree");
1084 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1085 if (kerr
!= KCGI_OK
)
1087 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1088 if (kerr
!= KCGI_OK
)
1091 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1092 "solid_line", KATTR__MAX
);
1093 if (kerr
!= KCGI_OK
)
1095 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1096 if (kerr
!= KCGI_OK
)
1103 if (gw_trans
->next_id
|| gw_trans
->prev_id
) {
1104 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1105 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1106 if (kerr
!= KCGI_OK
)
1108 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1109 KATTR_ID
, "nav_prev", KATTR__MAX
);
1110 if (kerr
!= KCGI_OK
)
1114 if (gw_trans
->prev_id
) {
1115 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1116 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1117 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1118 KATTRX_STRING
, "commits", "commit", KATTRX_STRING
,
1119 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1120 if (href_prev
== NULL
) {
1121 error
= got_error_from_errno("khttp_urlpartx");
1124 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1125 KATTR_HREF
, href_prev
, KATTR__MAX
);
1126 if (kerr
!= KCGI_OK
)
1128 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1129 if (kerr
!= KCGI_OK
)
1131 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1132 if (kerr
!= KCGI_OK
)
1136 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1137 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1138 if (kerr
!= KCGI_OK
)
1139 return gw_kcgi_error(kerr
);
1142 if (gw_trans
->next_id
) {
1143 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1144 KATTR_ID
, "nav_next", KATTR__MAX
);
1145 if (kerr
!= KCGI_OK
)
1147 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1148 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1149 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1150 KATTRX_STRING
, "commits", "commit", KATTRX_STRING
,
1151 gw_trans
->next_id
, NULL
);
1152 if (href_next
== NULL
) {
1153 error
= got_error_from_errno("khttp_urlpartx");
1156 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1157 KATTR_HREF
, href_next
, KATTR__MAX
);
1158 if (kerr
!= KCGI_OK
)
1160 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1161 if (kerr
!= KCGI_OK
)
1163 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1164 if (kerr
!= KCGI_OK
)
1168 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1169 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1170 if (kerr
!= KCGI_OK
)
1174 gw_free_header(header
);
1175 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
)
1176 gw_free_header(n_header
);
1182 if (error
== NULL
&& kerr
!= KCGI_OK
)
1183 error
= gw_kcgi_error(kerr
);
1187 static const struct got_error
*
1188 gw_briefs(struct gw_trans
*gw_trans
)
1190 const struct got_error
*error
= NULL
;
1191 struct gw_header
*header
= NULL
, *n_header
= NULL
;
1192 char *age
= NULL
, *href_diff
= NULL
, *href_tree
= NULL
;
1193 char *href_prev
= NULL
, *href_next
= NULL
;
1194 char *newline
, *smallerthan
;
1195 enum kcgi_err kerr
= KCGI_OK
;
1196 int commit_found
= 0;
1198 if ((header
= gw_init_header()) == NULL
)
1199 return got_error_from_errno("malloc");
1202 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
1204 error
= got_error_from_errno("pledge");
1208 if (gw_trans
->action
!= GW_SUMMARY
) {
1209 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1214 if (gw_trans
->action
== GW_SUMMARY
)
1215 error
= gw_get_header(gw_trans
, header
, D_MAXSLCOMMDISP
);
1217 error
= gw_get_header(gw_trans
, header
,
1218 gw_trans
->gw_conf
->got_max_commits_display
);
1222 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
) {
1223 if (commit_found
== 0 && gw_trans
->commit_id
!= NULL
) {
1224 if (strcmp(gw_trans
->commit_id
,
1225 n_header
->commit_id
) != 0)
1230 error
= gw_get_time_str(&age
, n_header
->committer_time
,
1235 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1236 KATTR_ID
, "briefs_wrapper", KATTR__MAX
);
1237 if (kerr
!= KCGI_OK
)
1240 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1241 KATTR_ID
, "briefs_age", KATTR__MAX
);
1242 if (kerr
!= KCGI_OK
)
1244 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
? age
: "");
1245 if (kerr
!= KCGI_OK
)
1247 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1248 if (kerr
!= KCGI_OK
)
1251 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1252 KATTR_ID
, "briefs_author", KATTR__MAX
);
1253 if (kerr
!= KCGI_OK
)
1255 smallerthan
= strchr(n_header
->author
, '<');
1257 *smallerthan
= '\0';
1258 kerr
= khtml_puts(gw_trans
->gw_html_req
, n_header
->author
);
1259 if (kerr
!= KCGI_OK
)
1261 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1262 if (kerr
!= KCGI_OK
)
1265 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1266 gw_trans
->repo_name
, "action", "diff", "commit",
1267 n_header
->commit_id
, NULL
);
1268 if (href_diff
== NULL
) {
1269 error
= got_error_from_errno("khttp_urlpart");
1272 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1273 KATTR_ID
, "briefs_log", KATTR__MAX
);
1274 if (kerr
!= KCGI_OK
)
1276 newline
= strchr(n_header
->commit_msg
, '\n');
1279 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1280 KATTR_HREF
, href_diff
, KATTR__MAX
);
1281 if (kerr
!= KCGI_OK
)
1283 kerr
= khtml_puts(gw_trans
->gw_html_req
, n_header
->commit_msg
);
1284 if (kerr
!= KCGI_OK
)
1286 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1287 if (kerr
!= KCGI_OK
)
1290 if (n_header
->refs_str
) {
1291 kerr
= khtml_puts(gw_trans
->gw_html_req
, " ");
1292 if (kerr
!= KCGI_OK
)
1294 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_SPAN
,
1295 KATTR_ID
, "refs_str", KATTR__MAX
);
1296 if (kerr
!= KCGI_OK
)
1298 kerr
= khtml_printf(gw_trans
->gw_html_req
, "(%s)",
1299 n_header
->refs_str
);
1300 if (kerr
!= KCGI_OK
)
1302 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1303 if (kerr
!= KCGI_OK
)
1307 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1308 if (kerr
!= KCGI_OK
)
1311 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1312 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
1313 if (kerr
!= KCGI_OK
)
1315 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1316 KATTR_ID
, "navs", KATTR__MAX
);
1317 if (kerr
!= KCGI_OK
)
1319 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1320 KATTR_HREF
, href_diff
, KATTR__MAX
);
1321 if (kerr
!= KCGI_OK
)
1323 kerr
= khtml_puts(gw_trans
->gw_html_req
, "diff");
1324 if (kerr
!= KCGI_OK
)
1326 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1327 if (kerr
!= KCGI_OK
)
1330 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
1331 if (kerr
!= KCGI_OK
)
1334 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1335 gw_trans
->repo_name
, "action", "tree", "commit",
1336 n_header
->commit_id
, NULL
);
1337 if (href_tree
== NULL
) {
1338 error
= got_error_from_errno("khttp_urlpart");
1341 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1342 KATTR_HREF
, href_tree
, KATTR__MAX
);
1343 if (kerr
!= KCGI_OK
)
1345 khtml_puts(gw_trans
->gw_html_req
, "tree");
1346 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1347 if (kerr
!= KCGI_OK
)
1349 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1350 if (kerr
!= KCGI_OK
)
1353 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1354 KATTR_ID
, "dotted_line", KATTR__MAX
);
1355 if (kerr
!= KCGI_OK
)
1357 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1358 if (kerr
!= KCGI_OK
)
1369 if (gw_trans
->next_id
|| gw_trans
->prev_id
) {
1370 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1371 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1372 if (kerr
!= KCGI_OK
)
1374 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1375 KATTR_ID
, "nav_prev", KATTR__MAX
);
1376 if (kerr
!= KCGI_OK
)
1380 if (gw_trans
->prev_id
) {
1381 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1382 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1383 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1384 KATTRX_STRING
, "briefs", "commit", KATTRX_STRING
,
1385 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1386 if (href_prev
== NULL
) {
1387 error
= got_error_from_errno("khttp_urlpartx");
1390 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1391 KATTR_HREF
, href_prev
, KATTR__MAX
);
1392 if (kerr
!= KCGI_OK
)
1394 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1395 if (kerr
!= KCGI_OK
)
1397 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1398 if (kerr
!= KCGI_OK
)
1402 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1403 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1404 if (kerr
!= KCGI_OK
)
1405 return gw_kcgi_error(kerr
);
1408 if (gw_trans
->next_id
) {
1409 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1410 KATTR_ID
, "nav_next", KATTR__MAX
);
1411 if (kerr
!= KCGI_OK
)
1414 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1415 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1416 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1417 KATTRX_STRING
, "briefs", "commit", KATTRX_STRING
,
1418 gw_trans
->next_id
, NULL
);
1419 if (href_next
== NULL
) {
1420 error
= got_error_from_errno("khttp_urlpartx");
1423 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1424 KATTR_HREF
, href_next
, KATTR__MAX
);
1425 if (kerr
!= KCGI_OK
)
1427 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1428 if (kerr
!= KCGI_OK
)
1430 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1431 if (kerr
!= KCGI_OK
)
1435 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1436 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1437 if (kerr
!= KCGI_OK
)
1441 gw_free_header(header
);
1442 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
)
1443 gw_free_header(n_header
);
1449 if (error
== NULL
&& kerr
!= KCGI_OK
)
1450 error
= gw_kcgi_error(kerr
);
1454 static const struct got_error
*
1455 gw_summary(struct gw_trans
*gw_trans
)
1457 const struct got_error
*error
= NULL
;
1459 enum kcgi_err kerr
= KCGI_OK
;
1462 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
1464 return got_error_from_errno("pledge");
1466 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1470 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1471 "summary_wrapper", KATTR__MAX
);
1472 if (kerr
!= KCGI_OK
)
1473 return gw_kcgi_error(kerr
);
1475 if (gw_trans
->gw_conf
->got_show_repo_description
&&
1476 gw_trans
->gw_dir
->description
!= NULL
&&
1477 (strcmp(gw_trans
->gw_dir
->description
, "") != 0)) {
1478 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1479 KATTR_ID
, "description_title", KATTR__MAX
);
1480 if (kerr
!= KCGI_OK
)
1482 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Description: ");
1483 if (kerr
!= KCGI_OK
)
1485 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1486 if (kerr
!= KCGI_OK
)
1488 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1489 KATTR_ID
, "description", KATTR__MAX
);
1490 if (kerr
!= KCGI_OK
)
1492 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1493 gw_trans
->gw_dir
->description
);
1494 if (kerr
!= KCGI_OK
)
1496 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1497 if (kerr
!= KCGI_OK
)
1501 if (gw_trans
->gw_conf
->got_show_repo_owner
&&
1502 gw_trans
->gw_dir
->owner
!= NULL
&&
1503 (strcmp(gw_trans
->gw_dir
->owner
, "") != 0)) {
1504 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1505 KATTR_ID
, "repo_owner_title", KATTR__MAX
);
1506 if (kerr
!= KCGI_OK
)
1508 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Owner: ");
1509 if (kerr
!= KCGI_OK
)
1511 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1512 if (kerr
!= KCGI_OK
)
1514 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1515 KATTR_ID
, "repo_owner", KATTR__MAX
);
1516 if (kerr
!= KCGI_OK
)
1518 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1519 gw_trans
->gw_dir
->owner
);
1520 if (kerr
!= KCGI_OK
)
1522 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1523 if (kerr
!= KCGI_OK
)
1527 if (gw_trans
->gw_conf
->got_show_repo_age
) {
1528 error
= gw_get_repo_age(&age
, gw_trans
, gw_trans
->gw_dir
->path
,
1533 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1534 KATTR_ID
, "last_change_title", KATTR__MAX
);
1535 if (kerr
!= KCGI_OK
)
1537 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1539 if (kerr
!= KCGI_OK
)
1541 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1542 if (kerr
!= KCGI_OK
)
1544 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1545 KATTR_ID
, "last_change", KATTR__MAX
);
1546 if (kerr
!= KCGI_OK
)
1548 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
);
1549 if (kerr
!= KCGI_OK
)
1551 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1552 if (kerr
!= KCGI_OK
)
1557 if (gw_trans
->gw_conf
->got_show_repo_cloneurl
&&
1558 gw_trans
->gw_dir
->url
!= NULL
&&
1559 (strcmp(gw_trans
->gw_dir
->url
, "") != 0)) {
1560 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1561 KATTR_ID
, "cloneurl_title", KATTR__MAX
);
1562 if (kerr
!= KCGI_OK
)
1564 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Clone URL: ");
1565 if (kerr
!= KCGI_OK
)
1567 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1568 if (kerr
!= KCGI_OK
)
1570 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1571 KATTR_ID
, "cloneurl", KATTR__MAX
);
1572 if (kerr
!= KCGI_OK
)
1574 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->gw_dir
->url
);
1575 if (kerr
!= KCGI_OK
)
1577 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1578 if (kerr
!= KCGI_OK
)
1582 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1583 if (kerr
!= KCGI_OK
)
1586 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1587 "briefs_title_wrapper", KATTR__MAX
);
1588 if (kerr
!= KCGI_OK
)
1590 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1591 "briefs_title", KATTR__MAX
);
1592 if (kerr
!= KCGI_OK
)
1594 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Commit Briefs");
1595 if (kerr
!= KCGI_OK
)
1597 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1598 if (kerr
!= KCGI_OK
)
1600 error
= gw_briefs(gw_trans
);
1604 error
= gw_tags(gw_trans
);
1608 error
= gw_output_repo_heads(gw_trans
);
1611 if (error
== NULL
&& kerr
!= KCGI_OK
)
1612 error
= gw_kcgi_error(kerr
);
1616 static const struct got_error
*
1617 gw_tree(struct gw_trans
*gw_trans
)
1619 const struct got_error
*error
= NULL
;
1620 struct gw_header
*header
= NULL
;
1621 char *tree
= NULL
, *tree_html
= NULL
, *tree_html_disp
= NULL
;
1623 enum kcgi_err kerr
= KCGI_OK
;
1626 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
1628 return got_error_from_errno("pledge");
1630 if ((header
= gw_init_header()) == NULL
)
1631 return got_error_from_errno("malloc");
1633 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1637 error
= gw_get_header(gw_trans
, header
, 1);
1641 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1642 "tree_header_wrapper", KATTR__MAX
);
1643 if (kerr
!= KCGI_OK
)
1645 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1646 "tree_header", KATTR__MAX
);
1647 if (kerr
!= KCGI_OK
)
1649 error
= gw_gen_tree_header(gw_trans
, header
->tree_id
);
1652 error
= gw_get_time_str(&age
, header
->committer_time
,
1656 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
1659 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
1662 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1663 if (kerr
!= KCGI_OK
)
1665 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1666 "dotted_line", KATTR__MAX
);
1667 if (kerr
!= KCGI_OK
)
1669 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1670 if (kerr
!= KCGI_OK
)
1673 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1674 "tree", KATTR__MAX
);
1675 if (kerr
!= KCGI_OK
)
1677 error
= gw_output_repo_tree(gw_trans
, header
);
1681 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1683 gw_free_header(header
);
1684 free(tree_html_disp
);
1688 if (error
== NULL
&& kerr
!= KCGI_OK
)
1689 error
= gw_kcgi_error(kerr
);
1693 static const struct got_error
*
1694 gw_tags(struct gw_trans
*gw_trans
)
1696 const struct got_error
*error
= NULL
;
1697 struct gw_header
*header
= NULL
;
1698 char *href_next
= NULL
, *href_prev
= NULL
;
1699 enum kcgi_err kerr
= KCGI_OK
;
1702 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
1704 return got_error_from_errno("pledge");
1706 if ((header
= gw_init_header()) == NULL
)
1707 return got_error_from_errno("malloc");
1709 if (gw_trans
->action
!= GW_SUMMARY
) {
1710 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1715 error
= gw_get_header(gw_trans
, header
, 1);
1719 if (gw_trans
->action
== GW_SUMMARY
) {
1720 gw_trans
->next_id
= NULL
;
1721 error
= gw_output_repo_tags(gw_trans
, header
,
1722 D_MAXSLCOMMDISP
, TAGBRIEF
);
1726 error
= gw_output_repo_tags(gw_trans
, header
,
1727 gw_trans
->gw_conf
->got_max_commits_display
, TAGBRIEF
);
1732 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1733 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1734 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1735 if (kerr
!= KCGI_OK
)
1737 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1738 KATTR_ID
, "nav_prev", KATTR__MAX
);
1739 if (kerr
!= KCGI_OK
)
1743 if (gw_trans
->prev_id
) {
1744 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1745 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1746 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1747 KATTRX_STRING
, "tags", "commit", KATTRX_STRING
,
1748 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1749 if (href_prev
== NULL
) {
1750 error
= got_error_from_errno("khttp_urlpartx");
1753 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1754 KATTR_HREF
, href_prev
, KATTR__MAX
);
1755 if (kerr
!= KCGI_OK
)
1757 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1758 if (kerr
!= KCGI_OK
)
1760 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1761 if (kerr
!= KCGI_OK
)
1765 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1766 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1767 if (kerr
!= KCGI_OK
)
1768 return gw_kcgi_error(kerr
);
1771 if (gw_trans
->next_id
) {
1772 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1773 KATTR_ID
, "nav_next", KATTR__MAX
);
1774 if (kerr
!= KCGI_OK
)
1776 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1777 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1778 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1779 KATTRX_STRING
, "tags", "commit", KATTRX_STRING
,
1780 gw_trans
->next_id
, NULL
);
1781 if (href_next
== NULL
) {
1782 error
= got_error_from_errno("khttp_urlpartx");
1785 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1786 KATTR_HREF
, href_next
, KATTR__MAX
);
1787 if (kerr
!= KCGI_OK
)
1789 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1790 if (kerr
!= KCGI_OK
)
1792 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1793 if (kerr
!= KCGI_OK
)
1797 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1798 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1799 if (kerr
!= KCGI_OK
)
1803 gw_free_header(header
);
1806 if (error
== NULL
&& kerr
!= KCGI_OK
)
1807 error
= gw_kcgi_error(kerr
);
1811 static const struct got_error
*
1812 gw_tag(struct gw_trans
*gw_trans
)
1814 const struct got_error
*error
= NULL
;
1815 struct gw_header
*header
= NULL
;
1816 enum kcgi_err kerr
= KCGI_OK
;
1819 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil", NULL
) == -1)
1820 return got_error_from_errno("pledge");
1822 if ((header
= gw_init_header()) == NULL
)
1823 return got_error_from_errno("malloc");
1825 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1829 if (gw_trans
->commit_id
== NULL
) {
1830 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
1831 "commit required in querystring");
1835 error
= gw_get_header(gw_trans
, header
, 1);
1839 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1840 "tag_header_wrapper", KATTR__MAX
);
1841 if (kerr
!= KCGI_OK
)
1843 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1844 "tag_header", KATTR__MAX
);
1845 if (kerr
!= KCGI_OK
)
1847 error
= gw_gen_commit_header(gw_trans
, header
->commit_id
,
1851 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
1854 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1855 if (kerr
!= KCGI_OK
)
1857 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1858 "dotted_line", KATTR__MAX
);
1859 if (kerr
!= KCGI_OK
)
1861 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1862 if (kerr
!= KCGI_OK
)
1865 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1866 "tree", KATTR__MAX
);
1867 if (kerr
!= KCGI_OK
)
1870 error
= gw_output_repo_tags(gw_trans
, header
, 1, TAGFULL
);
1874 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1876 gw_free_header(header
);
1877 if (error
== NULL
&& kerr
!= KCGI_OK
)
1878 error
= gw_kcgi_error(kerr
);
1882 static const struct got_error
*
1883 gw_load_got_path(struct gw_trans
*gw_trans
, struct gw_dir
*gw_dir
)
1885 const struct got_error
*error
= NULL
;
1890 if (asprintf(&dir_test
, "%s/%s/%s",
1891 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
,
1892 GOTWEB_GIT_DIR
) == -1)
1893 return got_error_from_errno("asprintf");
1895 dt
= opendir(dir_test
);
1899 gw_dir
->path
= strdup(dir_test
);
1900 if (gw_dir
->path
== NULL
) {
1902 error
= got_error_from_errno("strdup");
1909 if (asprintf(&dir_test
, "%s/%s/%s",
1910 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
,
1911 GOTWEB_GOT_DIR
) == -1) {
1913 error
= got_error_from_errno("asprintf");
1917 dt
= opendir(dir_test
);
1922 error
= got_error(GOT_ERR_NOT_GIT_REPO
);
1926 if (asprintf(&dir_test
, "%s/%s",
1927 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
) == -1) {
1928 error
= got_error_from_errno("asprintf");
1933 gw_dir
->path
= strdup(dir_test
);
1934 if (gw_dir
->path
== NULL
) {
1936 error
= got_error_from_errno("strdup");
1940 dt
= opendir(dir_test
);
1942 error
= got_error_path(gw_dir
->name
, GOT_ERR_NOT_GIT_REPO
);
1947 error
= gw_get_repo_description(&gw_dir
->description
, gw_trans
,
1951 error
= gw_get_repo_owner(&gw_dir
->owner
, gw_trans
, gw_dir
->path
);
1954 error
= gw_get_repo_age(&gw_dir
->age
, gw_trans
, gw_dir
->path
,
1958 error
= gw_get_clone_url(&gw_dir
->url
, gw_trans
, gw_dir
->path
);
1962 if (dt
&& closedir(dt
) == -1 && error
== NULL
)
1963 error
= got_error_from_errno("closedir");
1967 static const struct got_error
*
1968 gw_load_got_paths(struct gw_trans
*gw_trans
)
1970 const struct got_error
*error
= NULL
;
1972 struct dirent
**sd_dent
;
1973 struct gw_dir
*gw_dir
;
1975 unsigned int d_cnt
, d_i
;
1977 d
= opendir(gw_trans
->gw_conf
->got_repos_path
);
1979 error
= got_error_from_errno2("opendir",
1980 gw_trans
->gw_conf
->got_repos_path
);
1984 d_cnt
= scandir(gw_trans
->gw_conf
->got_repos_path
, &sd_dent
, NULL
,
1987 error
= got_error_from_errno2("scandir",
1988 gw_trans
->gw_conf
->got_repos_path
);
1992 for (d_i
= 0; d_i
< d_cnt
; d_i
++) {
1993 if (gw_trans
->gw_conf
->got_max_repos
> 0 &&
1994 (d_i
- 2) == gw_trans
->gw_conf
->got_max_repos
)
1995 break; /* account for parent and self */
1997 if (strcmp(sd_dent
[d_i
]->d_name
, ".") == 0 ||
1998 strcmp(sd_dent
[d_i
]->d_name
, "..") == 0)
2001 error
= gw_init_gw_dir(&gw_dir
, sd_dent
[d_i
]->d_name
);
2005 error
= gw_load_got_path(gw_trans
, gw_dir
);
2006 if (error
&& error
->code
== GOT_ERR_NOT_GIT_REPO
) {
2009 } else if (error
&& error
->code
!= GOT_ERR_LONELY_PACKIDX
)
2012 if (lstat(gw_dir
->path
, &st
) == 0 && S_ISDIR(st
.st_mode
) &&
2013 !got_path_dir_is_empty(gw_dir
->path
)) {
2014 TAILQ_INSERT_TAIL(&gw_trans
->gw_dirs
, gw_dir
,
2016 gw_trans
->repos_total
++;
2020 if (d
&& closedir(d
) == -1 && error
== NULL
)
2021 error
= got_error_from_errno("closedir");
2025 static const struct got_error
*
2026 gw_parse_querystring(struct gw_trans
*gw_trans
)
2028 const struct got_error
*error
= NULL
;
2030 const struct gw_query_action
*action
= NULL
;
2033 if (gw_trans
->gw_req
->fieldnmap
[0]) {
2034 return got_error(GOT_ERR_QUERYSTRING
);
2035 } else if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PATH
])) {
2036 /* define gw_trans->repo_path */
2037 gw_trans
->repo_name
= p
->parsed
.s
;
2039 if (asprintf(&gw_trans
->repo_path
, "%s/%s",
2040 gw_trans
->gw_conf
->got_repos_path
, p
->parsed
.s
) == -1)
2041 return got_error_from_errno("asprintf");
2043 /* get action and set function */
2044 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_ACTION
])) {
2045 for (i
= 0; i
< nitems(gw_query_funcs
); i
++) {
2046 action
= &gw_query_funcs
[i
];
2047 if (action
->func_name
== NULL
)
2049 if (strcmp(action
->func_name
,
2050 p
->parsed
.s
) == 0) {
2051 gw_trans
->action
= i
;
2056 if (gw_trans
->action
== -1) {
2057 gw_trans
->action
= GW_ERR
;
2058 gw_trans
->error
= got_error_msg(GOT_ERR_QUERYSTRING
,
2059 p
!= NULL
? "bad action in querystring" :
2060 "no action in querystring");
2064 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_COMMIT_ID
])) {
2065 if (asprintf(&gw_trans
->commit_id
, "%s",
2067 return got_error_from_errno("asprintf");
2070 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_FILE
]))
2071 gw_trans
->repo_file
= p
->parsed
.s
;
2073 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_FOLDER
])) {
2074 if (asprintf(&gw_trans
->repo_folder
, "%s",
2076 return got_error_from_errno("asprintf");
2079 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PREV_ID
])) {
2080 if (asprintf(&gw_trans
->prev_id
, "%s",
2082 return got_error_from_errno("asprintf");
2085 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_HEADREF
]))
2086 gw_trans
->headref
= p
->parsed
.s
;
2088 error
= gw_init_gw_dir(&gw_trans
->gw_dir
, gw_trans
->repo_name
);
2092 gw_trans
->error
= gw_load_got_path(gw_trans
, gw_trans
->gw_dir
);
2094 gw_trans
->action
= GW_INDEX
;
2096 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PAGE
]))
2097 gw_trans
->page
= p
->parsed
.i
;
2102 static const struct got_error
*
2103 gw_init_gw_dir(struct gw_dir
**gw_dir
, const char *dir
)
2105 const struct got_error
*error
;
2107 *gw_dir
= malloc(sizeof(**gw_dir
));
2108 if (*gw_dir
== NULL
)
2109 return got_error_from_errno("malloc");
2111 if (asprintf(&(*gw_dir
)->name
, "%s", dir
) == -1) {
2112 error
= got_error_from_errno("asprintf");
2121 static const struct got_error
*
2122 gw_display_open(struct gw_trans
*gw_trans
, enum khttp code
, enum kmime mime
)
2124 enum kcgi_err kerr
= KCGI_OK
;
2126 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_ALLOW
], "GET");
2127 if (kerr
!= KCGI_OK
)
2128 return gw_kcgi_error(kerr
);
2129 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_STATUS
], "%s",
2131 if (kerr
!= KCGI_OK
)
2132 return gw_kcgi_error(kerr
);
2133 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_CONTENT_TYPE
], "%s",
2135 if (kerr
!= KCGI_OK
)
2136 return gw_kcgi_error(kerr
);
2137 kerr
= khttp_head(gw_trans
->gw_req
, "X-Content-Type-Options",
2139 if (kerr
!= KCGI_OK
)
2140 return gw_kcgi_error(kerr
);
2141 kerr
= khttp_head(gw_trans
->gw_req
, "X-Frame-Options", "DENY");
2142 if (kerr
!= KCGI_OK
)
2143 return gw_kcgi_error(kerr
);
2144 kerr
= khttp_head(gw_trans
->gw_req
, "X-XSS-Protection",
2146 if (kerr
!= KCGI_OK
)
2147 return gw_kcgi_error(kerr
);
2149 if (gw_trans
->mime
== KMIME_APP_OCTET_STREAM
) {
2150 kerr
= khttp_head(gw_trans
->gw_req
,
2151 kresps
[KRESP_CONTENT_DISPOSITION
],
2152 "attachment; filename=%s", gw_trans
->repo_file
);
2153 if (kerr
!= KCGI_OK
)
2154 return gw_kcgi_error(kerr
);
2157 kerr
= khttp_body(gw_trans
->gw_req
);
2158 return gw_kcgi_error(kerr
);
2161 static const struct got_error
*
2162 gw_display_index(struct gw_trans
*gw_trans
)
2164 const struct got_error
*error
;
2165 enum kcgi_err kerr
= KCGI_OK
;
2167 /* catch early querystring errors */
2168 if (gw_trans
->error
)
2169 gw_trans
->action
= GW_ERR
;
2171 error
= gw_display_open(gw_trans
, KHTTP_200
, gw_trans
->mime
);
2175 kerr
= khtml_open(gw_trans
->gw_html_req
, gw_trans
->gw_req
, 0);
2176 if (kerr
!= KCGI_OK
)
2177 return gw_kcgi_error(kerr
);
2179 if (gw_trans
->action
!= GW_BLOB
) {
2180 kerr
= khttp_template(gw_trans
->gw_req
, gw_trans
->gw_tmpl
,
2181 gw_query_funcs
[gw_trans
->action
].template);
2182 if (kerr
!= KCGI_OK
) {
2183 khtml_close(gw_trans
->gw_html_req
);
2184 return gw_kcgi_error(kerr
);
2188 return gw_kcgi_error(khtml_close(gw_trans
->gw_html_req
));
2191 static const struct got_error
*
2192 gw_error(struct gw_trans
*gw_trans
)
2194 enum kcgi_err kerr
= KCGI_OK
;
2196 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->error
->msg
);
2198 return gw_kcgi_error(kerr
);
2202 gw_template(size_t key
, void *arg
)
2204 const struct got_error
*error
= NULL
;
2205 enum kcgi_err kerr
= KCGI_OK
;
2206 struct gw_trans
*gw_trans
= arg
;
2207 char *ati
= NULL
, *fic32
= NULL
, *fic16
= NULL
;
2208 char *swm
= NULL
, *spt
= NULL
, *css
= NULL
, *logo
= NULL
;
2210 if (asprintf(&ati
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2211 "/apple-touch-icon.png") == -1)
2213 if (asprintf(&fic32
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2214 "/favicon-32x32.png") == -1)
2216 if (asprintf(&fic16
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2217 "/favicon-16x16.png") == -1)
2219 if (asprintf(&swm
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2220 "/site.webmanifest") == -1)
2222 if (asprintf(&spt
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2223 "/safari-pinned-tab.svg") == -1)
2225 if (asprintf(&css
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2226 "/gotweb.css") == -1)
2228 if (asprintf(&logo
, "%s%s%s", gw_trans
->gw_conf
->got_www_path
,
2229 gw_trans
->gw_conf
->got_www_path
? "/" : "",
2230 gw_trans
->gw_conf
->got_logo
) == -1)
2235 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2236 KATTR_NAME
, "viewport",
2237 KATTR_CONTENT
, "initial-scale=.75, user-scalable=yes",
2239 if (kerr
!= KCGI_OK
)
2241 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2242 if (kerr
!= KCGI_OK
)
2244 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2245 KATTR_CHARSET
, "utf-8",
2247 if (kerr
!= KCGI_OK
)
2249 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2250 if (kerr
!= KCGI_OK
)
2252 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2253 KATTR_NAME
, "msapplication-TileColor",
2254 KATTR_CONTENT
, "#da532c", KATTR__MAX
);
2255 if (kerr
!= KCGI_OK
)
2257 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2258 if (kerr
!= KCGI_OK
)
2260 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2261 KATTR_NAME
, "theme-color",
2262 KATTR_CONTENT
, "#ffffff", KATTR__MAX
);
2263 if (kerr
!= KCGI_OK
)
2265 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2266 if (kerr
!= KCGI_OK
)
2268 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2269 KATTR_REL
, "apple-touch-icon", KATTR_SIZES
, "180x180",
2270 KATTR_HREF
, ati
, KATTR__MAX
);
2271 if (kerr
!= KCGI_OK
)
2273 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2274 if (kerr
!= KCGI_OK
)
2276 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2277 KATTR_REL
, "icon", KATTR_TYPE
, "image/png", KATTR_SIZES
,
2278 "32x32", KATTR_HREF
, fic32
, KATTR__MAX
);
2279 if (kerr
!= KCGI_OK
)
2281 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2282 if (kerr
!= KCGI_OK
)
2284 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2285 KATTR_REL
, "icon", KATTR_TYPE
, "image/png", KATTR_SIZES
,
2286 "16x16", KATTR_HREF
, fic16
, KATTR__MAX
);
2287 if (kerr
!= KCGI_OK
)
2289 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2290 if (kerr
!= KCGI_OK
)
2292 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2293 KATTR_REL
, "manifest", KATTR_HREF
, swm
,
2295 if (kerr
!= KCGI_OK
)
2297 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2298 if (kerr
!= KCGI_OK
)
2300 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2301 KATTR_REL
, "mask-icon", KATTR_HREF
,
2303 if (kerr
!= KCGI_OK
)
2305 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2306 if (kerr
!= KCGI_OK
)
2308 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2309 KATTR_REL
, "stylesheet", KATTR_TYPE
, "text/css",
2310 KATTR_HREF
, css
, KATTR__MAX
);
2311 if (kerr
!= KCGI_OK
)
2313 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2314 if (kerr
!= KCGI_OK
)
2318 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2319 KATTR_ID
, "got_link", KATTR__MAX
);
2320 if (kerr
!= KCGI_OK
)
2322 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
2323 KATTR_HREF
, gw_trans
->gw_conf
->got_logo_url
,
2324 KATTR_TARGET
, "_sotd", KATTR__MAX
);
2325 if (kerr
!= KCGI_OK
)
2327 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_IMG
,
2328 KATTR_SRC
, logo
, KATTR__MAX
);
2329 if (kerr
!= KCGI_OK
)
2331 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
2332 if (kerr
!= KCGI_OK
)
2335 case (TEMPL_SITEPATH
):
2336 error
= gw_output_site_link(gw_trans
);
2341 if (gw_trans
->gw_conf
->got_site_name
!= NULL
) {
2342 kerr
= khtml_puts(gw_trans
->gw_html_req
,
2343 gw_trans
->gw_conf
->got_site_name
);
2344 if (kerr
!= KCGI_OK
)
2348 case (TEMPL_SEARCH
):
2350 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
2351 "search", KATTR__MAX
);
2352 if (kerr
!= KCGI_OK
)
2354 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_FORM
,
2355 KATTR_METHOD
, "POST", KATTR__MAX
);
2356 if (kerr
!= KCGI_OK
)
2358 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_INPUT
, KATTR_ID
,
2359 "got-search", KATTR_NAME
, "got-search", KATTR_SIZE
, "15",
2360 KATTR_MAXLENGTH
, "50", KATTR__MAX
);
2361 if (kerr
!= KCGI_OK
)
2363 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_BUTTON
,
2365 if (kerr
!= KCGI_OK
)
2367 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Search");
2368 if (kerr
!= KCGI_OK
)
2370 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 4);
2371 if (kerr
!= KCGI_OK
)
2374 case(TEMPL_SITEOWNER
):
2375 if (gw_trans
->gw_conf
->got_site_owner
!= NULL
&&
2376 gw_trans
->gw_conf
->got_show_site_owner
) {
2377 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2378 KATTR_ID
, "site_owner_wrapper", KATTR__MAX
);
2379 if (kerr
!= KCGI_OK
)
2381 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2382 KATTR_ID
, "site_owner", KATTR__MAX
);
2383 if (kerr
!= KCGI_OK
)
2385 kerr
= khtml_puts(gw_trans
->gw_html_req
,
2386 gw_trans
->gw_conf
->got_site_owner
);
2387 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
2388 if (kerr
!= KCGI_OK
)
2392 case(TEMPL_CONTENT
):
2393 error
= gw_query_funcs
[gw_trans
->action
].func_main(gw_trans
);
2395 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2396 KATTR_ID
, "tmpl_err", KATTR__MAX
);
2397 if (kerr
!= KCGI_OK
)
2399 kerr
= khttp_printf(gw_trans
->gw_req
, "Error: %s",
2401 if (kerr
!= KCGI_OK
)
2403 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2404 if (kerr
!= KCGI_OK
)
2430 static const struct got_error
*
2431 gw_gen_commit_header(struct gw_trans
*gw_trans
, char *str1
, char *str2
)
2433 const struct got_error
*error
= NULL
;
2434 enum kcgi_err kerr
= KCGI_OK
;
2436 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2437 KATTR_ID
, "header_commit_title", KATTR__MAX
);
2438 if (kerr
!= KCGI_OK
)
2440 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Commit: ");
2441 if (kerr
!= KCGI_OK
)
2443 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2444 if (kerr
!= KCGI_OK
)
2446 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2447 KATTR_ID
, "header_commit", KATTR__MAX
);
2448 if (kerr
!= KCGI_OK
)
2450 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s ", str1
);
2451 if (kerr
!= KCGI_OK
)
2454 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_SPAN
,
2455 KATTR_ID
, "refs_str", KATTR__MAX
);
2456 if (kerr
!= KCGI_OK
)
2458 kerr
= khtml_printf(gw_trans
->gw_html_req
, "(%s)", str2
);
2459 if (kerr
!= KCGI_OK
)
2461 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2462 if (kerr
!= KCGI_OK
)
2465 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2467 if (error
== NULL
&& kerr
!= KCGI_OK
)
2468 error
= gw_kcgi_error(kerr
);
2472 static const struct got_error
*
2473 gw_gen_diff_header(struct gw_trans
*gw_trans
, char *str1
, char *str2
)
2475 const struct got_error
*error
= NULL
;
2476 enum kcgi_err kerr
= KCGI_OK
;
2478 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2479 KATTR_ID
, "header_diff_title", KATTR__MAX
);
2480 if (kerr
!= KCGI_OK
)
2482 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Diff: ");
2483 if (kerr
!= KCGI_OK
)
2485 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2486 if (kerr
!= KCGI_OK
)
2488 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2489 KATTR_ID
, "header_diff", KATTR__MAX
);
2490 if (kerr
!= KCGI_OK
)
2493 kerr
= khtml_puts(gw_trans
->gw_html_req
, str1
);
2494 if (kerr
!= KCGI_OK
)
2497 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_BR
, KATTR__MAX
);
2498 if (kerr
!= KCGI_OK
)
2500 kerr
= khtml_puts(gw_trans
->gw_html_req
, str2
);
2501 if (kerr
!= KCGI_OK
)
2503 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2505 if (error
== NULL
&& kerr
!= KCGI_OK
)
2506 error
= gw_kcgi_error(kerr
);
2510 static const struct got_error
*
2511 gw_gen_age_header(struct gw_trans
*gw_trans
, const char *str
)
2513 const struct got_error
*error
= NULL
;
2514 enum kcgi_err kerr
= KCGI_OK
;
2516 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2517 KATTR_ID
, "header_age_title", KATTR__MAX
);
2518 if (kerr
!= KCGI_OK
)
2520 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Date: ");
2521 if (kerr
!= KCGI_OK
)
2523 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2524 if (kerr
!= KCGI_OK
)
2526 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2527 KATTR_ID
, "header_age", KATTR__MAX
);
2528 if (kerr
!= KCGI_OK
)
2530 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2531 if (kerr
!= KCGI_OK
)
2533 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2535 if (error
== NULL
&& kerr
!= KCGI_OK
)
2536 error
= gw_kcgi_error(kerr
);
2540 static const struct got_error
*
2541 gw_gen_author_header(struct gw_trans
*gw_trans
, const char *str
)
2543 const struct got_error
*error
= NULL
;
2544 enum kcgi_err kerr
= KCGI_OK
;
2546 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2547 KATTR_ID
, "header_author_title", KATTR__MAX
);
2548 if (kerr
!= KCGI_OK
)
2550 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Author: ");
2551 if (kerr
!= KCGI_OK
)
2553 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2554 if (kerr
!= KCGI_OK
)
2556 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2557 KATTR_ID
, "header_author", KATTR__MAX
);
2558 if (kerr
!= KCGI_OK
)
2560 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2561 if (kerr
!= KCGI_OK
)
2563 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2565 if (error
== NULL
&& kerr
!= KCGI_OK
)
2566 error
= gw_kcgi_error(kerr
);
2570 static const struct got_error
*
2571 gw_gen_committer_header(struct gw_trans
*gw_trans
, const char *str
)
2573 const struct got_error
*error
= NULL
;
2574 enum kcgi_err kerr
= KCGI_OK
;
2576 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2577 KATTR_ID
, "header_committer_title", KATTR__MAX
);
2578 if (kerr
!= KCGI_OK
)
2580 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Committer: ");
2581 if (kerr
!= KCGI_OK
)
2583 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2584 if (kerr
!= KCGI_OK
)
2586 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2587 KATTR_ID
, "header_committer", KATTR__MAX
);
2588 if (kerr
!= KCGI_OK
)
2590 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2591 if (kerr
!= KCGI_OK
)
2593 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2595 if (error
== NULL
&& kerr
!= KCGI_OK
)
2596 error
= gw_kcgi_error(kerr
);
2600 static const struct got_error
*
2601 gw_gen_commit_msg_header(struct gw_trans
*gw_trans
, char *str
)
2603 const struct got_error
*error
= NULL
;
2604 enum kcgi_err kerr
= KCGI_OK
;
2606 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2607 KATTR_ID
, "header_commit_msg_title", KATTR__MAX
);
2608 if (kerr
!= KCGI_OK
)
2610 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Message: ");
2611 if (kerr
!= KCGI_OK
)
2613 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2614 if (kerr
!= KCGI_OK
)
2616 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2617 KATTR_ID
, "header_commit_msg", KATTR__MAX
);
2618 if (kerr
!= KCGI_OK
)
2620 kerr
= khttp_puts(gw_trans
->gw_req
, str
);
2621 if (kerr
!= KCGI_OK
)
2623 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2625 if (error
== NULL
&& kerr
!= KCGI_OK
)
2626 error
= gw_kcgi_error(kerr
);
2630 static const struct got_error
*
2631 gw_gen_tree_header(struct gw_trans
*gw_trans
, char *str
)
2633 const struct got_error
*error
= NULL
;
2634 enum kcgi_err kerr
= KCGI_OK
;
2636 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2637 KATTR_ID
, "header_tree_title", KATTR__MAX
);
2638 if (kerr
!= KCGI_OK
)
2640 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tree: ");
2641 if (kerr
!= KCGI_OK
)
2643 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2644 if (kerr
!= KCGI_OK
)
2646 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2647 KATTR_ID
, "header_tree", KATTR__MAX
);
2648 if (kerr
!= KCGI_OK
)
2650 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2651 if (kerr
!= KCGI_OK
)
2653 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2655 if (error
== NULL
&& kerr
!= KCGI_OK
)
2656 error
= gw_kcgi_error(kerr
);
2660 static const struct got_error
*
2661 gw_get_repo_description(char **description
, struct gw_trans
*gw_trans
,
2664 const struct got_error
*error
= NULL
;
2666 char *d_file
= NULL
;
2670 *description
= NULL
;
2671 if (gw_trans
->gw_conf
->got_show_repo_description
== 0)
2674 if (asprintf(&d_file
, "%s/description", dir
) == -1)
2675 return got_error_from_errno("asprintf");
2677 f
= fopen(d_file
, "re");
2679 if (errno
== ENOENT
|| errno
== EACCES
)
2681 error
= got_error_from_errno2("fopen", d_file
);
2685 if (fseek(f
, 0, SEEK_END
) == -1) {
2686 error
= got_ferror(f
, GOT_ERR_IO
);
2691 error
= got_ferror(f
, GOT_ERR_IO
);
2694 if (fseek(f
, 0, SEEK_SET
) == -1) {
2695 error
= got_ferror(f
, GOT_ERR_IO
);
2698 *description
= calloc(len
+ 1, sizeof(**description
));
2699 if (*description
== NULL
) {
2700 error
= got_error_from_errno("calloc");
2704 n
= fread(*description
, 1, len
, f
);
2705 if (n
== 0 && ferror(f
))
2706 error
= got_ferror(f
, GOT_ERR_IO
);
2708 if (f
!= NULL
&& fclose(f
) == EOF
&& error
== NULL
)
2709 error
= got_error_from_errno("fclose");
2714 static const struct got_error
*
2715 gw_get_time_str(char **repo_age
, time_t committer_time
, int ref_tm
)
2719 const char *years
= "years ago", *months
= "months ago";
2720 const char *weeks
= "weeks ago", *days
= "days ago", *hours
= "hours ago";
2721 const char *minutes
= "minutes ago", *seconds
= "seconds ago";
2722 const char *now
= "right now";
2730 diff_time
= time(NULL
) - committer_time
;
2731 if (diff_time
> 60 * 60 * 24 * 365 * 2) {
2732 if (asprintf(repo_age
, "%lld %s",
2733 (diff_time
/ 60 / 60 / 24 / 365), years
) == -1)
2734 return got_error_from_errno("asprintf");
2735 } else if (diff_time
> 60 * 60 * 24 * (365 / 12) * 2) {
2736 if (asprintf(repo_age
, "%lld %s",
2737 (diff_time
/ 60 / 60 / 24 / (365 / 12)),
2739 return got_error_from_errno("asprintf");
2740 } else if (diff_time
> 60 * 60 * 24 * 7 * 2) {
2741 if (asprintf(repo_age
, "%lld %s",
2742 (diff_time
/ 60 / 60 / 24 / 7), weeks
) == -1)
2743 return got_error_from_errno("asprintf");
2744 } else if (diff_time
> 60 * 60 * 24 * 2) {
2745 if (asprintf(repo_age
, "%lld %s",
2746 (diff_time
/ 60 / 60 / 24), days
) == -1)
2747 return got_error_from_errno("asprintf");
2748 } else if (diff_time
> 60 * 60 * 2) {
2749 if (asprintf(repo_age
, "%lld %s",
2750 (diff_time
/ 60 / 60), hours
) == -1)
2751 return got_error_from_errno("asprintf");
2752 } else if (diff_time
> 60 * 2) {
2753 if (asprintf(repo_age
, "%lld %s", (diff_time
/ 60),
2755 return got_error_from_errno("asprintf");
2756 } else if (diff_time
> 2) {
2757 if (asprintf(repo_age
, "%lld %s", diff_time
,
2759 return got_error_from_errno("asprintf");
2761 if (asprintf(repo_age
, "%s", now
) == -1)
2762 return got_error_from_errno("asprintf");
2766 if (gmtime_r(&committer_time
, &tm
) == NULL
)
2767 return got_error_from_errno("gmtime_r");
2769 s
= asctime_r(&tm
, datebuf
);
2771 return got_error_from_errno("asctime_r");
2773 if (asprintf(repo_age
, "%s UTC", datebuf
) == -1)
2774 return got_error_from_errno("asprintf");
2780 static const struct got_error
*
2781 gw_get_repo_age(char **repo_age
, struct gw_trans
*gw_trans
, char *dir
,
2782 const char *refname
, int ref_tm
)
2784 const struct got_error
*error
= NULL
;
2785 struct got_repository
*repo
= NULL
;
2786 struct got_commit_object
*commit
= NULL
;
2787 struct got_reflist_head refs
;
2788 struct got_reflist_entry
*re
;
2789 time_t committer_time
= 0, cmp_time
= 0;
2794 if (gw_trans
->gw_conf
->got_show_repo_age
== 0)
2798 repo
= gw_trans
->repo
;
2800 error
= got_repo_open(&repo
, dir
, NULL
, gw_trans
->pack_fds
);
2805 error
= got_ref_list(&refs
, repo
, "refs/heads",
2806 got_ref_cmp_by_name
, NULL
);
2811 * Find the youngest branch tip in the repository, or the age of
2812 * the a specific branch tip if a name was provided by the caller.
2814 TAILQ_FOREACH(re
, &refs
, entry
) {
2815 struct got_object_id
*id
= NULL
;
2817 if (refname
&& strcmp(got_ref_get_name(re
->ref
), refname
) != 0)
2820 error
= got_ref_resolve(&id
, repo
, re
->ref
);
2824 error
= got_object_open_as_commit(&commit
, repo
, id
);
2830 got_object_commit_get_committer_time(commit
);
2831 got_object_commit_close(commit
);
2832 if (cmp_time
< committer_time
)
2833 cmp_time
= committer_time
;
2839 if (cmp_time
!= 0) {
2840 committer_time
= cmp_time
;
2841 error
= gw_get_time_str(repo_age
, committer_time
, ref_tm
);
2844 got_ref_list_free(&refs
);
2845 if (gw_trans
->repo
== NULL
) {
2846 const struct got_error
*close_err
= got_repo_close(repo
);
2853 static const struct got_error
*
2854 gw_output_diff(struct gw_trans
*gw_trans
, struct gw_header
*header
)
2856 const struct got_error
*error
;
2857 FILE *f
= NULL
, *f1
= NULL
, *f2
= NULL
;
2858 int fd1
= -1, fd2
= -1;
2859 struct got_object_id
*id1
= NULL
, *id2
= NULL
;
2860 char *label1
= NULL
, *label2
= NULL
, *line
= NULL
;
2862 size_t linesize
= 0;
2864 enum kcgi_err kerr
= KCGI_OK
;
2870 f1
= got_opentemp();
2872 error
= got_error_from_errno("got_opentemp");
2876 f2
= got_opentemp();
2878 error
= got_error_from_errno("got_opentemp");
2882 fd1
= got_opentempfd();
2884 error
= got_error_from_errno("got_opentempfd");
2888 fd2
= got_opentempfd();
2890 error
= got_error_from_errno("got_opentempfd");
2894 if (header
->parent_id
!= NULL
&&
2895 strncmp(header
->parent_id
, "/dev/null", 9) != 0) {
2896 error
= got_repo_match_object_id(&id1
, &label1
,
2897 header
->parent_id
, GOT_OBJ_TYPE_ANY
,
2898 &header
->refs
, gw_trans
->repo
);
2903 error
= got_repo_match_object_id(&id2
, &label2
,
2904 header
->commit_id
, GOT_OBJ_TYPE_ANY
, &header
->refs
,
2909 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, id2
);
2913 case GOT_OBJ_TYPE_BLOB
:
2914 error
= got_diff_objects_as_blobs(NULL
, NULL
, f1
, f2
,
2915 fd1
, fd2
, id1
, id2
, NULL
, NULL
, GOT_DIFF_ALGORITHM_PATIENCE
,
2916 3, 0, 0, gw_trans
->repo
, f
);
2918 case GOT_OBJ_TYPE_TREE
:
2919 error
= got_diff_objects_as_trees(NULL
, NULL
, f1
, f2
,
2920 fd1
, fd2
, id1
, id2
, NULL
, "", "",
2921 GOT_DIFF_ALGORITHM_PATIENCE
, 3, 0, 0, gw_trans
->repo
, f
);
2923 case GOT_OBJ_TYPE_COMMIT
:
2924 error
= got_diff_objects_as_commits(NULL
, NULL
, f1
, f2
,
2925 fd1
, fd2
, id1
, id2
, NULL
, GOT_DIFF_ALGORITHM_PATIENCE
,
2926 3, 0, 0, gw_trans
->repo
, f
);
2929 error
= got_error(GOT_ERR_OBJ_TYPE
);
2934 if (fseek(f
, 0, SEEK_SET
) == -1) {
2935 error
= got_ferror(f
, GOT_ERR_IO
);
2939 while ((linelen
= getline(&line
, &linesize
, f
)) != -1) {
2940 error
= gw_colordiff_line(gw_trans
, line
);
2943 /* XXX: KHTML_PRETTY breaks this */
2944 kerr
= khtml_puts(gw_trans
->gw_html_req
, line
);
2945 if (kerr
!= KCGI_OK
)
2947 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2948 if (kerr
!= KCGI_OK
)
2951 if (linelen
== -1 && ferror(f
))
2952 error
= got_error_from_errno("getline");
2954 if (f
&& fclose(f
) == EOF
&& error
== NULL
)
2955 error
= got_error_from_errno("fclose");
2956 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
2957 error
= got_error_from_errno("fclose");
2958 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
2959 error
= got_error_from_errno("fclose");
2960 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
2961 error
= got_error_from_errno("close");
2962 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
2963 error
= got_error_from_errno("close");
2970 if (error
== NULL
&& kerr
!= KCGI_OK
)
2971 error
= gw_kcgi_error(kerr
);
2975 static const struct got_error
*
2976 gw_get_repo_owner(char **owner
, struct gw_trans
*gw_trans
, char *dir
)
2978 const struct got_error
*error
= NULL
, *close_err
;
2979 struct got_repository
*repo
;
2980 const char *gitconfig_owner
;
2984 if (gw_trans
->gw_conf
->got_show_repo_owner
== 0)
2987 error
= got_repo_open(&repo
, dir
, NULL
, gw_trans
->pack_fds
);
2991 gitconfig_owner
= got_repo_get_gitconfig_owner(repo
);
2992 if (gitconfig_owner
) {
2993 *owner
= strdup(gitconfig_owner
);
2995 error
= got_error_from_errno("strdup");
2997 close_err
= got_repo_close(repo
);
3003 static const struct got_error
*
3004 gw_get_clone_url(char **url
, struct gw_trans
*gw_trans
, char *dir
)
3006 const struct got_error
*error
= NULL
;
3008 char *d_file
= NULL
;
3014 if (asprintf(&d_file
, "%s/cloneurl", dir
) == -1)
3015 return got_error_from_errno("asprintf");
3017 f
= fopen(d_file
, "re");
3019 if (errno
!= ENOENT
&& errno
!= EACCES
)
3020 error
= got_error_from_errno2("fopen", d_file
);
3024 if (fseek(f
, 0, SEEK_END
) == -1) {
3025 error
= got_ferror(f
, GOT_ERR_IO
);
3030 error
= got_ferror(f
, GOT_ERR_IO
);
3033 if (fseek(f
, 0, SEEK_SET
) == -1) {
3034 error
= got_ferror(f
, GOT_ERR_IO
);
3038 *url
= calloc(len
+ 1, sizeof(**url
));
3040 error
= got_error_from_errno("calloc");
3044 n
= fread(*url
, 1, len
, f
);
3045 if (n
== 0 && ferror(f
))
3046 error
= got_ferror(f
, GOT_ERR_IO
);
3048 if (f
&& fclose(f
) == EOF
&& error
== NULL
)
3049 error
= got_error_from_errno("fclose");
3054 static const struct got_error
*
3055 gw_output_repo_tags(struct gw_trans
*gw_trans
, struct gw_header
*header
,
3056 int limit
, int tag_type
)
3058 const struct got_error
*error
= NULL
;
3059 struct got_reflist_head refs
;
3060 struct got_reflist_entry
*re
;
3062 char *id_str
= NULL
, *newline
, *href_commits
= NULL
;
3063 char *tag_commit0
= NULL
, *href_tag
= NULL
, *href_briefs
= NULL
;
3064 struct got_tag_object
*tag
= NULL
;
3065 enum kcgi_err kerr
= KCGI_OK
;
3066 int summary_header_displayed
= 0, chk_next
= 0;
3067 int tag_count
= 0, commit_found
= 0, c_cnt
= 0;
3071 error
= got_ref_list(&refs
, gw_trans
->repo
, "refs/tags",
3072 got_ref_cmp_tags
, gw_trans
->repo
);
3076 TAILQ_FOREACH(re
, &refs
, entry
) {
3077 const char *refname
;
3079 const char *tag_commit
;
3081 struct got_object_id
*id
;
3082 struct got_commit_object
*commit
= NULL
;
3084 refname
= got_ref_get_name(re
->ref
);
3085 if (strncmp(refname
, "refs/tags/", 10) != 0)
3089 error
= got_ref_resolve(&id
, gw_trans
->repo
, re
->ref
);
3093 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
, id
);
3095 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3099 /* "lightweight" tag */
3100 error
= got_object_open_as_commit(&commit
,
3101 gw_trans
->repo
, id
);
3106 tagger
= got_object_commit_get_committer(commit
);
3108 got_object_commit_get_committer_time(commit
);
3109 error
= got_object_id_str(&id_str
, id
);
3113 tagger
= got_object_tag_get_tagger(tag
);
3114 tagger_time
= got_object_tag_get_tagger_time(tag
);
3115 error
= got_object_id_str(&id_str
,
3116 got_object_tag_get_object_id(tag
));
3121 if (tag_type
== TAGFULL
&& strncmp(id_str
, header
->commit_id
,
3122 strlen(id_str
)) != 0)
3125 if (tag_type
== TAGBRIEF
&& gw_trans
->commit_id
&&
3126 commit_found
== 0 && strncmp(id_str
, gw_trans
->commit_id
,
3127 strlen(id_str
)) != 0)
3135 gw_trans
->next_id
= strdup(id_str
);
3136 if (gw_trans
->next_id
== NULL
)
3137 error
= got_error_from_errno("strdup");
3142 error
= got_object_commit_get_logmsg(&tag_commit0
,
3146 got_object_commit_close(commit
);
3148 tag_commit0
= strdup(got_object_tag_get_message(tag
));
3149 if (tag_commit0
== NULL
) {
3150 error
= got_error_from_errno("strdup");
3155 tag_commit
= tag_commit0
;
3156 while (*tag_commit
== '\n')
3161 newline
= strchr(tag_commit
, '\n');
3165 if (summary_header_displayed
== 0) {
3166 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3167 KELEM_DIV
, KATTR_ID
,
3168 "summary_tags_title_wrapper", KATTR__MAX
);
3169 if (kerr
!= KCGI_OK
)
3171 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3172 KELEM_DIV
, KATTR_ID
,
3173 "summary_tags_title", KATTR__MAX
);
3174 if (kerr
!= KCGI_OK
)
3176 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3178 if (kerr
!= KCGI_OK
)
3180 kerr
= khtml_closeelem(gw_trans
->gw_html_req
,
3182 if (kerr
!= KCGI_OK
)
3184 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3185 KELEM_DIV
, KATTR_ID
,
3186 "summary_tags_content", KATTR__MAX
);
3187 if (kerr
!= KCGI_OK
)
3189 summary_header_displayed
= 1;
3192 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3193 KATTR_ID
, "tag_wrapper", KATTR__MAX
);
3194 if (kerr
!= KCGI_OK
)
3196 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3197 KATTR_ID
, "tag_age", KATTR__MAX
);
3198 if (kerr
!= KCGI_OK
)
3200 error
= gw_get_time_str(&age
, tagger_time
, TM_DIFF
);
3203 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3205 if (kerr
!= KCGI_OK
)
3207 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3208 if (kerr
!= KCGI_OK
)
3210 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3211 KATTR_ID
, "tag", KATTR__MAX
);
3212 if (kerr
!= KCGI_OK
)
3214 kerr
= khtml_puts(gw_trans
->gw_html_req
, refname
);
3215 if (kerr
!= KCGI_OK
)
3217 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3218 if (kerr
!= KCGI_OK
)
3220 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3221 KATTR_ID
, "tag_name", KATTR__MAX
);
3222 if (kerr
!= KCGI_OK
)
3225 href_tag
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
3226 gw_trans
->repo_name
, "action", "tag", "commit",
3228 if (href_tag
== NULL
) {
3229 error
= got_error_from_errno("khttp_urlpart");
3232 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3233 KATTR_HREF
, href_tag
, KATTR__MAX
);
3234 if (kerr
!= KCGI_OK
)
3236 kerr
= khtml_puts(gw_trans
->gw_html_req
, tag_commit
);
3237 if (kerr
!= KCGI_OK
)
3239 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
3240 if (kerr
!= KCGI_OK
)
3243 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3244 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
3245 if (kerr
!= KCGI_OK
)
3247 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3248 KATTR_ID
, "navs", KATTR__MAX
);
3249 if (kerr
!= KCGI_OK
)
3252 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3253 KATTR_HREF
, href_tag
, KATTR__MAX
);
3254 if (kerr
!= KCGI_OK
)
3256 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tag");
3257 if (kerr
!= KCGI_OK
)
3259 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3260 if (kerr
!= KCGI_OK
)
3263 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
3264 if (kerr
!= KCGI_OK
)
3267 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb",
3268 "path", gw_trans
->repo_name
, "action", "briefs",
3269 "commit", id_str
, NULL
);
3270 if (href_briefs
== NULL
) {
3271 error
= got_error_from_errno("khttp_urlpart");
3274 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3275 KATTR_HREF
, href_briefs
, KATTR__MAX
);
3276 if (kerr
!= KCGI_OK
)
3278 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3280 if (kerr
!= KCGI_OK
)
3282 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3283 if (kerr
!= KCGI_OK
)
3286 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
3287 if (kerr
!= KCGI_OK
)
3290 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb",
3291 "path", gw_trans
->repo_name
, "action", "commits",
3292 "commit", id_str
, NULL
);
3293 if (href_commits
== NULL
) {
3294 error
= got_error_from_errno("khttp_urlpart");
3297 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3298 KATTR_HREF
, href_commits
, KATTR__MAX
);
3299 if (kerr
!= KCGI_OK
)
3301 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
3302 if (kerr
!= KCGI_OK
)
3304 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
3305 if (kerr
!= KCGI_OK
)
3308 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3309 KATTR_ID
, "dotted_line", KATTR__MAX
);
3310 if (kerr
!= KCGI_OK
)
3312 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3313 if (kerr
!= KCGI_OK
)
3317 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3318 KATTR_ID
, "tag_info_date_title", KATTR__MAX
);
3319 if (kerr
!= KCGI_OK
)
3321 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tag Date:");
3322 if (kerr
!= KCGI_OK
)
3324 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3325 if (kerr
!= KCGI_OK
)
3327 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3328 KATTR_ID
, "tag_info_date", KATTR__MAX
);
3329 if (kerr
!= KCGI_OK
)
3331 error
= gw_get_time_str(&age
, tagger_time
, TM_LONG
);
3334 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3336 if (kerr
!= KCGI_OK
)
3338 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3339 if (kerr
!= KCGI_OK
)
3342 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3343 KATTR_ID
, "tag_info_tagger_title", KATTR__MAX
);
3344 if (kerr
!= KCGI_OK
)
3346 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tagger:");
3347 if (kerr
!= KCGI_OK
)
3349 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3350 if (kerr
!= KCGI_OK
)
3352 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3353 KATTR_ID
, "tag_info_date", KATTR__MAX
);
3354 if (kerr
!= KCGI_OK
)
3356 kerr
= khtml_puts(gw_trans
->gw_html_req
, tagger
);
3357 if (kerr
!= KCGI_OK
)
3359 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3360 if (kerr
!= KCGI_OK
)
3363 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3364 KATTR_ID
, "tag_info", KATTR__MAX
);
3365 if (kerr
!= KCGI_OK
)
3367 kerr
= khttp_puts(gw_trans
->gw_req
, tag_commit
);
3368 if (kerr
!= KCGI_OK
)
3374 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3375 if (kerr
!= KCGI_OK
)
3378 if (limit
&& --limit
== 0)
3382 got_object_tag_close(tag
);
3395 href_commits
= NULL
;
3397 if (tag_count
== 0) {
3398 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3399 "summary_tags_title_wrapper", KATTR__MAX
);
3400 if (kerr
!= KCGI_OK
)
3402 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3403 "summary_tags_title", KATTR__MAX
);
3404 if (kerr
!= KCGI_OK
)
3406 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tags");
3407 if (kerr
!= KCGI_OK
)
3409 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3410 if (kerr
!= KCGI_OK
)
3412 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3413 "summary_tags_content", KATTR__MAX
);
3414 if (kerr
!= KCGI_OK
)
3416 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3417 "tags_info", KATTR__MAX
);
3418 if (kerr
!= KCGI_OK
)
3420 kerr
= khttp_puts(gw_trans
->gw_req
,
3421 "There are no tags for this repo.");
3422 if (kerr
!= KCGI_OK
)
3424 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3429 TAILQ_FOREACH_REVERSE(re
, &refs
, got_reflist_head
, entry
) {
3430 const char *refname
;
3431 struct got_object_id
*id
;
3432 struct got_commit_object
*commit
= NULL
;
3434 refname
= got_ref_get_name(re
->ref
);
3435 if (strncmp(refname
, "refs/tags/", 10) != 0)
3439 error
= got_ref_resolve(&id
, gw_trans
->repo
, re
->ref
);
3443 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
, id
);
3445 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3449 /* "lightweight" tag */
3450 error
= got_object_open_as_commit(&commit
,
3451 gw_trans
->repo
, id
);
3456 error
= got_object_id_str(&id_str
, id
);
3460 error
= got_object_id_str(&id_str
,
3461 got_object_tag_get_object_id(tag
));
3466 if (tag_type
== TAGFULL
&& strncmp(id_str
, header
->commit_id
,
3467 strlen(id_str
)) != 0)
3470 if (commit_found
== 0 && tag_type
== TAGBRIEF
&&
3471 gw_trans
->commit_id
!= NULL
&&
3472 strncmp(id_str
, gw_trans
->commit_id
, strlen(id_str
)) != 0)
3477 if (gw_trans
->commit_id
!= NULL
&&
3478 strcmp(id_str
, gw_trans
->commit_id
) != 0 &&
3479 (re
== TAILQ_FIRST(&refs
) ||
3480 c_cnt
== gw_trans
->gw_conf
->got_max_commits_display
)) {
3481 gw_trans
->prev_id
= strdup(id_str
);
3482 if (gw_trans
->prev_id
== NULL
) {
3483 error
= got_error_from_errno("strdup");
3492 got_object_tag_close(tag
);
3499 got_ref_list_free(&refs
);
3500 if (error
== NULL
&& kerr
!= KCGI_OK
)
3501 error
= gw_kcgi_error(kerr
);
3506 gw_free_header(struct gw_header
*header
)
3509 free(header
->author
);
3510 free(header
->committer
);
3511 free(header
->refs_str
);
3512 free(header
->commit_id
);
3513 free(header
->parent_id
);
3514 free(header
->tree_id
);
3515 free(header
->commit_msg
);
3518 static struct gw_header
*
3521 struct gw_header
*header
;
3523 header
= malloc(sizeof(*header
));
3527 header
->path
= NULL
;
3528 TAILQ_INIT(&header
->refs
);
3530 header
->refs_str
= NULL
;
3531 header
->commit_id
= NULL
;
3532 header
->committer
= NULL
;
3533 header
->author
= NULL
;
3534 header
->parent_id
= NULL
;
3535 header
->tree_id
= NULL
;
3536 header
->commit_msg
= NULL
;
3541 static const struct got_error
*
3542 gw_get_commits(struct gw_trans
* gw_trans
, struct gw_header
*header
,
3543 int limit
, struct got_object_id
*iter_start_id
)
3545 const struct got_error
*error
= NULL
;
3546 struct got_commit_graph
*graph
= NULL
;
3547 struct got_commit_object
*commit
= NULL
;
3548 int chk_next
= 0, chk_multi
= 0, c_cnt
= 0, commit_found
= 0;
3549 struct gw_header
*t_header
= NULL
;
3551 error
= got_commit_graph_open(&graph
, header
->path
, 0);
3555 error
= got_commit_graph_iter_start(graph
, iter_start_id
,
3556 gw_trans
->repo
, NULL
, NULL
);
3561 struct got_object_id id
;
3563 error
= got_commit_graph_iter_next(&id
, graph
, gw_trans
->repo
,
3566 if (error
->code
== GOT_ERR_ITER_COMPLETED
)
3571 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, &id
);
3574 if (limit
== 1 && chk_multi
== 0 &&
3575 gw_trans
->gw_conf
->got_max_commits_display
!= 1) {
3576 error
= gw_get_commit(gw_trans
, header
, commit
, &id
);
3582 struct gw_header
*n_header
= NULL
;
3583 if ((n_header
= gw_init_header()) == NULL
) {
3584 error
= got_error_from_errno("malloc");
3587 TAILQ_INSERT_TAIL(&gw_trans
->gw_headers
, n_header
,
3589 error
= got_ref_list(&n_header
->refs
, gw_trans
->repo
,
3590 NULL
, got_ref_cmp_by_name
, NULL
);
3594 error
= gw_get_commit(gw_trans
, n_header
, commit
, &id
);
3597 got_ref_list_free(&n_header
->refs
);
3599 if (gw_trans
->commit_id
!= NULL
) {
3600 if (strcmp(gw_trans
->commit_id
,
3601 n_header
->commit_id
) == 0)
3607 * check for one more commit before breaking,
3608 * so we know whether to navigate through gw_briefs
3609 * gw_commits and gw_summary
3611 if (chk_next
&& (gw_trans
->action
== GW_BRIEFS
||
3612 gw_trans
->action
== GW_COMMITS
||
3613 gw_trans
->action
== GW_SUMMARY
)) {
3614 gw_trans
->next_id
= strdup(n_header
->commit_id
);
3615 if (gw_trans
->next_id
== NULL
)
3616 error
= got_error_from_errno("strdup");
3617 TAILQ_REMOVE(&gw_trans
->gw_headers
, n_header
,
3623 if (commit_found
== 1 && (error
|| (limit
&& --limit
== 0))) {
3630 if (gw_trans
->prev_id
== NULL
&& gw_trans
->commit_id
!= NULL
&&
3631 (gw_trans
->action
== GW_BRIEFS
|| gw_trans
->action
== GW_COMMITS
)) {
3633 TAILQ_FOREACH_REVERSE(t_header
, &gw_trans
->gw_headers
,
3635 if (commit_found
== 0 &&
3636 strcmp(gw_trans
->commit_id
,
3637 t_header
->commit_id
) != 0)
3641 if (gw_trans
->commit_id
!= NULL
&&
3642 strcmp(gw_trans
->commit_id
,
3643 t_header
->commit_id
) != 0 &&
3644 (c_cnt
== gw_trans
->gw_conf
->got_max_commits_display
3646 TAILQ_FIRST(&gw_trans
->gw_headers
))) {
3647 gw_trans
->prev_id
= strdup(t_header
->commit_id
);
3648 if (gw_trans
->prev_id
== NULL
)
3649 error
= got_error_from_errno("strdup");
3657 got_object_commit_close(commit
);
3659 got_commit_graph_close(graph
);
3663 static const struct got_error
*
3664 gw_get_commit(struct gw_trans
*gw_trans
, struct gw_header
*header
,
3665 struct got_commit_object
*commit
, struct got_object_id
*id
)
3667 const struct got_error
*error
= NULL
;
3668 struct got_reflist_entry
*re
;
3669 struct got_object_id
*id2
= NULL
;
3670 struct got_object_qid
*parent_id
;
3671 char *commit_msg
= NULL
, *commit_msg0
;
3674 TAILQ_FOREACH(re
, &header
->refs
, entry
) {
3677 struct got_tag_object
*tag
= NULL
;
3678 struct got_object_id
*ref_id
;
3681 if (got_ref_is_symbolic(re
->ref
))
3684 name
= got_ref_get_name(re
->ref
);
3685 if (strncmp(name
, "refs/", 5) == 0)
3687 if (strncmp(name
, "got/", 4) == 0)
3689 if (strncmp(name
, "heads/", 6) == 0)
3691 if (strncmp(name
, "remotes/", 8) == 0) {
3693 s
= strstr(name
, "/" GOT_REF_HEAD
);
3694 if (s
!= NULL
&& s
[strlen(s
)] == '\0')
3697 error
= got_ref_resolve(&ref_id
, gw_trans
->repo
, re
->ref
);
3700 if (strncmp(name
, "tags/", 5) == 0) {
3701 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
,
3704 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3709 * Ref points at something other
3716 cmp
= got_object_id_cmp(tag
?
3717 got_object_tag_get_object_id(tag
) : ref_id
, id
);
3720 got_object_tag_close(tag
);
3723 s
= header
->refs_str
;
3724 if (asprintf(&header
->refs_str
, "%s%s%s", s
? s
: "",
3725 s
? ", " : "", name
) == -1) {
3726 error
= got_error_from_errno("asprintf");
3728 header
->refs_str
= NULL
;
3734 error
= got_object_id_str(&header
->commit_id
, id
);
3738 error
= got_object_id_str(&header
->tree_id
,
3739 got_object_commit_get_tree_id(commit
));
3743 if (gw_trans
->action
== GW_DIFF
) {
3744 parent_id
= STAILQ_FIRST(
3745 got_object_commit_get_parent_ids(commit
));
3746 if (parent_id
!= NULL
) {
3747 id2
= got_object_id_dup(&parent_id
->id
);
3749 error
= got_object_id_str(&header
->parent_id
, id2
);
3754 header
->parent_id
= strdup("/dev/null");
3755 if (header
->parent_id
== NULL
) {
3756 error
= got_error_from_errno("strdup");
3762 header
->committer_time
=
3763 got_object_commit_get_committer_time(commit
);
3766 strdup(got_object_commit_get_author(commit
));
3767 if (header
->author
== NULL
) {
3768 error
= got_error_from_errno("strdup");
3772 strdup(got_object_commit_get_committer(commit
));
3773 if (header
->committer
== NULL
) {
3774 error
= got_error_from_errno("strdup");
3777 error
= got_object_commit_get_logmsg(&commit_msg0
, commit
);
3781 commit_msg
= commit_msg0
;
3782 while (*commit_msg
== '\n')
3785 header
->commit_msg
= strdup(commit_msg
);
3786 if (header
->commit_msg
== NULL
)
3787 error
= got_error_from_errno("strdup");
3792 static const struct got_error
*
3793 gw_get_header(struct gw_trans
*gw_trans
, struct gw_header
*header
, int limit
)
3795 const struct got_error
*error
= NULL
;
3796 char *in_repo_path
= NULL
;
3797 struct got_object_id
*id
= NULL
;
3798 struct got_reference
*ref
;
3800 error
= got_repo_open(&gw_trans
->repo
, gw_trans
->repo_path
, NULL
,
3801 gw_trans
->pack_fds
);
3805 if (gw_trans
->commit_id
== NULL
|| gw_trans
->action
== GW_COMMITS
||
3806 gw_trans
->action
== GW_BRIEFS
|| gw_trans
->action
== GW_SUMMARY
||
3807 gw_trans
->action
== GW_TAGS
) {
3808 error
= got_ref_open(&ref
, gw_trans
->repo
,
3809 gw_trans
->headref
, 0);
3813 error
= got_ref_resolve(&id
, gw_trans
->repo
, ref
);
3818 error
= got_ref_open(&ref
, gw_trans
->repo
,
3819 gw_trans
->commit_id
, 0);
3820 if (error
== NULL
) {
3822 error
= got_ref_resolve(&id
, gw_trans
->repo
, ref
);
3826 error
= got_object_get_type(&obj_type
, gw_trans
->repo
,
3830 if (obj_type
== GOT_OBJ_TYPE_TAG
) {
3831 struct got_tag_object
*tag
;
3832 error
= got_object_open_as_tag(&tag
,
3833 gw_trans
->repo
, id
);
3836 if (got_object_tag_get_object_type(tag
) !=
3837 GOT_OBJ_TYPE_COMMIT
) {
3838 got_object_tag_close(tag
);
3839 error
= got_error(GOT_ERR_OBJ_TYPE
);
3843 id
= got_object_id_dup(
3844 got_object_tag_get_object_id(tag
));
3846 error
= got_error_from_errno(
3847 "got_object_id_dup");
3848 got_object_tag_close(tag
);
3851 } else if (obj_type
!= GOT_OBJ_TYPE_COMMIT
) {
3852 error
= got_error(GOT_ERR_OBJ_TYPE
);
3856 error
= got_repo_match_object_id_prefix(&id
,
3857 gw_trans
->commit_id
, GOT_OBJ_TYPE_COMMIT
,
3863 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
,
3864 gw_trans
->repo_path
);
3869 header
->path
= strdup(in_repo_path
);
3870 if (header
->path
== NULL
) {
3871 error
= got_error_from_errno("strdup");
3876 error
= got_ref_list(&header
->refs
, gw_trans
->repo
, NULL
,
3877 got_ref_cmp_by_name
, NULL
);
3881 error
= gw_get_commits(gw_trans
, header
, limit
, id
);
3892 char datebuf
[11]; /* YYYY-MM-DD + NUL */
3895 struct gw_blame_cb_args
{
3896 struct blame_line
*lines
;
3900 off_t
*line_offsets
;
3902 struct got_repository
*repo
;
3903 struct gw_trans
*gw_trans
;
3906 static const struct got_error
*
3907 gw_blame_cb(void *arg
, int nlines
, int lineno
,
3908 struct got_commit_object
*commit
, struct got_object_id
*id
)
3910 const struct got_error
*err
= NULL
;
3911 struct gw_blame_cb_args
*a
= arg
;
3912 struct blame_line
*bline
;
3914 size_t linesize
= 0;
3917 time_t committer_time
;
3918 enum kcgi_err kerr
= KCGI_OK
;
3920 if (nlines
!= a
->nlines
||
3921 (lineno
!= -1 && lineno
< 1) || lineno
> a
->nlines
)
3922 return got_error(GOT_ERR_RANGE
);
3925 return NULL
; /* no change in this commit */
3927 /* Annotate this line. */
3928 bline
= &a
->lines
[lineno
- 1];
3929 if (bline
->annotated
)
3931 err
= got_object_id_str(&bline
->id_str
, id
);
3935 bline
->committer
= strdup(got_object_commit_get_committer(commit
));
3936 if (bline
->committer
== NULL
) {
3937 err
= got_error_from_errno("strdup");
3941 committer_time
= got_object_commit_get_committer_time(commit
);
3942 if (gmtime_r(&committer_time
, &tm
) == NULL
)
3943 return got_error_from_errno("gmtime_r");
3944 if (strftime(bline
->datebuf
, sizeof(bline
->datebuf
), "%G-%m-%d",
3946 err
= got_error(GOT_ERR_NO_SPACE
);
3949 bline
->annotated
= 1;
3951 /* Print lines annotated so far. */
3952 bline
= &a
->lines
[a
->lineno_cur
- 1];
3953 if (!bline
->annotated
)
3956 offset
= a
->line_offsets
[a
->lineno_cur
- 1];
3957 if (fseeko(a
->f
, offset
, SEEK_SET
) == -1) {
3958 err
= got_error_from_errno("fseeko");
3962 while (a
->lineno_cur
<= a
->nlines
&& bline
->annotated
) {
3963 char *smallerthan
, *at
, *nl
, *committer
;
3964 char *href_diff
= NULL
;
3967 if (getline(&line
, &linesize
, a
->f
) == -1) {
3969 err
= got_error_from_errno("getline");
3973 committer
= bline
->committer
;
3974 smallerthan
= strchr(committer
, '<');
3975 if (smallerthan
&& smallerthan
[1] != '\0')
3976 committer
= smallerthan
+ 1;
3977 at
= strchr(committer
, '@');
3980 len
= strlen(committer
);
3982 committer
[8] = '\0';
3984 nl
= strchr(line
, '\n');
3988 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3989 "blame_wrapper", KATTR__MAX
);
3990 if (kerr
!= KCGI_OK
)
3992 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3993 "blame_number", KATTR__MAX
);
3994 if (kerr
!= KCGI_OK
)
3996 kerr
= khtml_printf(a
->gw_trans
->gw_html_req
, "%.*d",
3997 a
->nlines_prec
, a
->lineno_cur
);
3998 if (kerr
!= KCGI_OK
)
4000 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4001 if (kerr
!= KCGI_OK
)
4004 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4005 "blame_hash", KATTR__MAX
);
4006 if (kerr
!= KCGI_OK
)
4009 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4010 a
->gw_trans
->repo_name
, "action", "diff", "commit",
4011 bline
->id_str
, NULL
);
4012 if (href_diff
== NULL
) {
4013 err
= got_error_from_errno("khttp_urlpart");
4016 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_A
,
4017 KATTR_HREF
, href_diff
, KATTR__MAX
);
4018 if (kerr
!= KCGI_OK
)
4020 kerr
= khtml_printf(a
->gw_trans
->gw_html_req
, "%.8s",
4022 if (kerr
!= KCGI_OK
)
4024 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 2);
4025 if (kerr
!= KCGI_OK
)
4028 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4029 "blame_date", KATTR__MAX
);
4030 if (kerr
!= KCGI_OK
)
4032 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, bline
->datebuf
);
4033 if (kerr
!= KCGI_OK
)
4035 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4036 if (kerr
!= KCGI_OK
)
4039 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4040 "blame_author", KATTR__MAX
);
4041 if (kerr
!= KCGI_OK
)
4043 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, committer
);
4044 if (kerr
!= KCGI_OK
)
4046 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4047 if (kerr
!= KCGI_OK
)
4050 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4051 "blame_code", KATTR__MAX
);
4052 if (kerr
!= KCGI_OK
)
4054 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, line
);
4055 if (kerr
!= KCGI_OK
)
4057 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4058 if (kerr
!= KCGI_OK
)
4061 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4062 if (kerr
!= KCGI_OK
)
4066 bline
= &a
->lines
[a
->lineno_cur
- 1];
4072 if (err
== NULL
&& kerr
!= KCGI_OK
)
4073 err
= gw_kcgi_error(kerr
);
4077 static const struct got_error
*
4078 gw_output_file_blame(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4080 const struct got_error
*error
= NULL
;
4081 struct got_object_id
*obj_id
= NULL
;
4082 struct got_object_id
*commit_id
= NULL
;
4083 struct got_commit_object
*commit
= NULL
;
4084 struct got_blob_object
*blob
= NULL
;
4085 char *path
= NULL
, *in_repo_path
= NULL
;
4086 struct gw_blame_cb_args bca
;
4087 int i
, obj_type
, fd1
= -1, fd2
= -1, fd3
= -1;
4089 FILE *f1
= NULL
, *f2
= NULL
;
4091 fd1
= got_opentempfd();
4093 return got_error_from_errno("got_opentempfd");
4094 fd2
= got_opentempfd();
4096 error
= got_error_from_errno("got_opentempfd");
4099 fd3
= got_opentempfd();
4101 error
= got_error_from_errno("got_opentempfd");
4105 memset(&bca
, 0, sizeof(bca
));
4107 if (asprintf(&path
, "%s%s%s",
4108 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4109 gw_trans
->repo_folder
? "/" : "",
4110 gw_trans
->repo_file
) == -1) {
4111 error
= got_error_from_errno("asprintf");
4115 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
, path
);
4119 error
= got_repo_match_object_id(&commit_id
, NULL
, gw_trans
->commit_id
,
4120 GOT_OBJ_TYPE_COMMIT
, &header
->refs
, gw_trans
->repo
);
4124 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, commit_id
);
4128 error
= got_object_id_by_path(&obj_id
, gw_trans
->repo
, commit
,
4133 if (obj_id
== NULL
) {
4134 error
= got_error(GOT_ERR_NO_OBJ
);
4138 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, obj_id
);
4142 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
4143 error
= got_error(GOT_ERR_OBJ_TYPE
);
4147 error
= got_object_open_as_blob(&blob
, gw_trans
->repo
, obj_id
, 8192,
4152 bca
.f
= got_opentemp();
4153 if (bca
.f
== NULL
) {
4154 error
= got_error_from_errno("got_opentemp");
4157 error
= got_object_blob_dump_to_file(&filesize
, &bca
.nlines
,
4158 &bca
.line_offsets
, bca
.f
, blob
);
4159 if (error
|| bca
.nlines
== 0)
4162 /* Don't include \n at EOF in the blame line count. */
4163 if (bca
.line_offsets
[bca
.nlines
- 1] == filesize
)
4166 bca
.lines
= calloc(bca
.nlines
, sizeof(*bca
.lines
));
4167 if (bca
.lines
== NULL
) {
4168 error
= got_error_from_errno("calloc");
4172 bca
.nlines_prec
= 0;
4178 bca
.repo
= gw_trans
->repo
;
4179 bca
.gw_trans
= gw_trans
;
4181 fd1
= got_opentempfd();
4183 error
= got_error_from_errno("got_opentempfd");
4187 f1
= got_opentemp();
4189 error
= got_error_from_errno("got_opentempfd");
4192 f2
= got_opentemp();
4194 error
= got_error_from_errno("got_opentempfd");
4198 error
= got_blame(in_repo_path
, commit_id
, gw_trans
->repo
,
4199 GOT_DIFF_ALGORITHM_PATIENCE
, gw_blame_cb
, &bca
, NULL
, NULL
,
4207 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
4208 error
= got_error_from_errno("close");
4209 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
4210 error
= got_error_from_errno("close");
4211 if (fd3
!= -1 && close(fd3
) == -1 && error
== NULL
)
4212 error
= got_error_from_errno("close");
4213 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
4214 error
= got_error_from_errno("fclose");
4215 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
4216 error
= got_error_from_errno("fclose");
4219 free(bca
.line_offsets
);
4220 for (i
= 0; i
< bca
.nlines
; i
++) {
4221 struct blame_line
*bline
= &bca
.lines
[i
];
4222 free(bline
->id_str
);
4223 free(bline
->committer
);
4226 if (bca
.f
&& fclose(bca
.f
) == EOF
&& error
== NULL
)
4227 error
= got_error_from_errno("fclose");
4230 got_object_blob_close(blob
);
4232 got_object_commit_close(commit
);
4236 static const struct got_error
*
4237 gw_output_blob_buf(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4239 const struct got_error
*error
= NULL
;
4240 struct got_object_id
*obj_id
= NULL
;
4241 struct got_object_id
*commit_id
= NULL
;
4242 struct got_commit_object
*commit
= NULL
;
4243 struct got_blob_object
*blob
= NULL
;
4244 char *path
= NULL
, *in_repo_path
= NULL
;
4245 int obj_type
, set_mime
= 0, fd
= -1;
4248 enum kcgi_err kerr
= KCGI_OK
;
4250 fd
= got_opentempfd();
4252 return got_error_from_errno("got_opentempfd");
4254 if (asprintf(&path
, "%s%s%s",
4255 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4256 gw_trans
->repo_folder
? "/" : "",
4257 gw_trans
->repo_file
) == -1) {
4258 error
= got_error_from_errno("asprintf");
4262 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
, path
);
4266 error
= got_repo_match_object_id(&commit_id
, NULL
, gw_trans
->commit_id
,
4267 GOT_OBJ_TYPE_COMMIT
, &header
->refs
, gw_trans
->repo
);
4271 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, commit_id
);
4275 error
= got_object_id_by_path(&obj_id
, gw_trans
->repo
, commit
,
4280 if (obj_id
== NULL
) {
4281 error
= got_error(GOT_ERR_NO_OBJ
);
4285 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, obj_id
);
4289 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
4290 error
= got_error(GOT_ERR_OBJ_TYPE
);
4294 error
= got_object_open_as_blob(&blob
, gw_trans
->repo
, obj_id
, 8192,
4299 hdrlen
= got_object_blob_get_hdrlen(blob
);
4301 error
= got_object_blob_read_block(&len
, blob
);
4304 buf
= got_object_blob_get_read_buf(blob
);
4307 * Skip blob object header first time around,
4308 * which also contains a zero byte.
4311 if (set_mime
== 0) {
4312 if (isbinary(buf
, len
- hdrlen
))
4313 gw_trans
->mime
= KMIME_APP_OCTET_STREAM
;
4315 gw_trans
->mime
= KMIME_TEXT_PLAIN
;
4317 error
= gw_display_index(gw_trans
);
4321 kerr
= khttp_write(gw_trans
->gw_req
, buf
, len
- hdrlen
);
4322 if (kerr
!= KCGI_OK
)
4331 if (fd
!= -1 && close(fd
) == -1 && error
== NULL
)
4332 error
= got_error_from_errno("close");
4334 got_object_blob_close(blob
);
4336 got_object_commit_close(commit
);
4337 if (error
== NULL
&& kerr
!= KCGI_OK
)
4338 error
= gw_kcgi_error(kerr
);
4342 static const struct got_error
*
4343 gw_output_repo_tree(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4345 const struct got_error
*error
= NULL
;
4346 struct got_object_id
*tree_id
= NULL
, *commit_id
= NULL
;
4347 struct got_tree_object
*tree
= NULL
;
4348 struct got_commit_object
*commit
= NULL
;
4349 char *path
= NULL
, *in_repo_path
= NULL
;
4350 char *id_str
= NULL
;
4351 char *build_folder
= NULL
;
4352 char *href_blob
= NULL
, *href_blame
= NULL
;
4353 const char *class = NULL
;
4354 int nentries
, i
, class_flip
= 0;
4355 enum kcgi_err kerr
= KCGI_OK
;
4357 if (gw_trans
->repo_folder
!= NULL
) {
4358 path
= strdup(gw_trans
->repo_folder
);
4360 error
= got_error_from_errno("strdup");
4364 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
,
4365 gw_trans
->repo_path
);
4369 path
= in_repo_path
;
4372 if (gw_trans
->commit_id
== NULL
) {
4373 struct got_reference
*head_ref
;
4374 error
= got_ref_open(&head_ref
, gw_trans
->repo
,
4375 gw_trans
->headref
, 0);
4378 error
= got_ref_resolve(&commit_id
, gw_trans
->repo
, head_ref
);
4381 got_ref_close(head_ref
);
4383 * gw_trans->commit_id was not parsed from the querystring
4384 * we hit this code path from gw_index, where we don't know the
4385 * commit values for the tree link yet, so set
4386 * gw_trans->commit_id here to continue further into the tree
4388 error
= got_object_id_str(&gw_trans
->commit_id
, commit_id
);
4393 error
= got_repo_match_object_id(&commit_id
, NULL
,
4394 gw_trans
->commit_id
, GOT_OBJ_TYPE_COMMIT
, &header
->refs
,
4400 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, commit_id
);
4404 error
= got_object_id_by_path(&tree_id
, gw_trans
->repo
, commit
,
4409 error
= got_object_open_as_tree(&tree
, gw_trans
->repo
, tree_id
);
4413 nentries
= got_object_tree_get_nentries(tree
);
4414 for (i
= 0; i
< nentries
; i
++) {
4415 struct got_tree_entry
*te
;
4416 const char *modestr
= "";
4419 te
= got_object_tree_get_entry(tree
, i
);
4421 error
= got_object_id_str(&id_str
, got_tree_entry_get_id(te
));
4425 mode
= got_tree_entry_get_mode(te
);
4426 if (got_object_tree_entry_is_submodule(te
))
4428 else if (S_ISLNK(mode
))
4430 else if (S_ISDIR(mode
))
4432 else if (mode
& S_IXUSR
)
4435 if (class_flip
== 0) {
4436 class = "back_lightgray";
4439 class = "back_white";
4443 if (S_ISDIR(mode
)) {
4444 if (asprintf(&build_folder
, "%s/%s",
4445 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4446 got_tree_entry_get_name(te
)) == -1) {
4447 error
= got_error_from_errno("asprintf");
4451 href_blob
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4452 gw_trans
->repo_name
, "action",
4453 gw_get_action_name(gw_trans
), "commit",
4454 gw_trans
->commit_id
, "folder", build_folder
, NULL
);
4455 if (href_blob
== NULL
) {
4456 error
= got_error_from_errno("khttp_urlpart");
4459 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4460 KATTR_ID
, "tree_wrapper", KATTR__MAX
);
4461 if (kerr
!= KCGI_OK
)
4463 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4464 KATTR_ID
, "tree_line", KATTR_CLASS
, class,
4466 if (kerr
!= KCGI_OK
)
4468 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4469 KATTR_HREF
, href_blob
, KATTR_CLASS
,
4470 "diff_directory", KATTR__MAX
);
4471 if (kerr
!= KCGI_OK
)
4473 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s%s",
4474 got_tree_entry_get_name(te
), modestr
);
4475 if (kerr
!= KCGI_OK
)
4477 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4478 if (kerr
!= KCGI_OK
)
4480 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4481 KATTR_ID
, "tree_line_blank", KATTR_CLASS
, class,
4483 if (kerr
!= KCGI_OK
)
4485 kerr
= khtml_entity(gw_trans
->gw_html_req
,
4487 if (kerr
!= KCGI_OK
)
4489 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4490 if (kerr
!= KCGI_OK
)
4493 href_blob
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4494 gw_trans
->repo_name
, "action", "blob", "commit",
4495 gw_trans
->commit_id
, "file",
4496 got_tree_entry_get_name(te
), "folder",
4497 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4499 if (href_blob
== NULL
) {
4500 error
= got_error_from_errno("khttp_urlpart");
4503 href_blame
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4504 gw_trans
->repo_name
, "action", "blame", "commit",
4505 gw_trans
->commit_id
, "file",
4506 got_tree_entry_get_name(te
), "folder",
4507 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4509 if (href_blame
== NULL
) {
4510 error
= got_error_from_errno("khttp_urlpart");
4513 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4514 KATTR_ID
, "tree_wrapper", KATTR__MAX
);
4515 if (kerr
!= KCGI_OK
)
4517 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4518 KATTR_ID
, "tree_line", KATTR_CLASS
, class,
4520 if (kerr
!= KCGI_OK
)
4522 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4523 KATTR_HREF
, href_blob
, KATTR__MAX
);
4524 if (kerr
!= KCGI_OK
)
4526 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s%s",
4527 got_tree_entry_get_name(te
), modestr
);
4528 if (kerr
!= KCGI_OK
)
4530 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4531 if (kerr
!= KCGI_OK
)
4533 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4534 KATTR_ID
, "tree_line_navs", KATTR_CLASS
, class,
4536 if (kerr
!= KCGI_OK
)
4539 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4540 KATTR_HREF
, href_blob
, KATTR__MAX
);
4541 if (kerr
!= KCGI_OK
)
4543 kerr
= khtml_puts(gw_trans
->gw_html_req
, "blob");
4544 if (kerr
!= KCGI_OK
)
4546 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4547 if (kerr
!= KCGI_OK
)
4550 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4551 if (kerr
!= KCGI_OK
)
4554 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4555 KATTR_HREF
, href_blame
, KATTR__MAX
);
4556 if (kerr
!= KCGI_OK
)
4558 kerr
= khtml_puts(gw_trans
->gw_html_req
, "blame");
4559 if (kerr
!= KCGI_OK
)
4562 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4563 if (kerr
!= KCGI_OK
)
4571 build_folder
= NULL
;
4575 got_object_tree_close(tree
);
4577 got_object_commit_close(commit
);
4584 if (error
== NULL
&& kerr
!= KCGI_OK
)
4585 error
= gw_kcgi_error(kerr
);
4589 static const struct got_error
*
4590 gw_output_repo_heads(struct gw_trans
*gw_trans
)
4592 const struct got_error
*error
= NULL
;
4593 struct got_reflist_head refs
;
4594 struct got_reflist_entry
*re
;
4595 char *age
= NULL
, *href_summary
= NULL
, *href_briefs
= NULL
;
4596 char *href_commits
= NULL
;
4597 enum kcgi_err kerr
= KCGI_OK
;
4601 error
= got_ref_list(&refs
, gw_trans
->repo
, "refs/heads",
4602 got_ref_cmp_by_name
, NULL
);
4606 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4607 KATTR_ID
, "summary_heads_title_wrapper", KATTR__MAX
);
4608 if (kerr
!= KCGI_OK
)
4610 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4611 KATTR_ID
, "summary_heads_title", KATTR__MAX
);
4612 if (kerr
!= KCGI_OK
)
4614 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Heads");
4615 if (kerr
!= KCGI_OK
)
4617 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4618 if (kerr
!= KCGI_OK
)
4620 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4621 KATTR_ID
, "summary_heads_content", KATTR__MAX
);
4622 if (kerr
!= KCGI_OK
)
4625 TAILQ_FOREACH(re
, &refs
, entry
) {
4626 const char *refname
;
4628 if (got_ref_is_symbolic(re
->ref
))
4631 refname
= got_ref_get_name(re
->ref
);
4632 if (strncmp(refname
, "refs/heads/", 11) != 0)
4635 error
= gw_get_repo_age(&age
, gw_trans
, gw_trans
->gw_dir
->path
,
4640 if (strncmp(refname
, "refs/heads/", 11) == 0)
4643 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4644 KATTR_ID
, "heads_wrapper", KATTR__MAX
);
4645 if (kerr
!= KCGI_OK
)
4647 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4648 KATTR_ID
, "heads_age", KATTR__MAX
);
4649 if (kerr
!= KCGI_OK
)
4651 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
? age
: "");
4652 if (kerr
!= KCGI_OK
)
4654 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4655 if (kerr
!= KCGI_OK
)
4657 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4658 KATTR_ID
, "heads_space", KATTR__MAX
);
4659 if (kerr
!= KCGI_OK
)
4661 kerr
= khtml_entity(gw_trans
->gw_html_req
, KENTITY_nbsp
);
4662 if (kerr
!= KCGI_OK
)
4664 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4665 if (kerr
!= KCGI_OK
)
4667 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4668 KATTR_ID
, "head", KATTR__MAX
);
4669 if (kerr
!= KCGI_OK
)
4672 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4673 gw_trans
->repo_name
, "action", "summary", "headref",
4675 if (href_summary
== NULL
) {
4676 error
= got_error_from_errno("khttp_urlpart");
4679 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4680 href_summary
, KATTR__MAX
);
4681 kerr
= khtml_puts(gw_trans
->gw_html_req
, refname
);
4682 if (kerr
!= KCGI_OK
)
4684 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4685 if (kerr
!= KCGI_OK
)
4688 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4689 "navs_wrapper", KATTR__MAX
);
4690 if (kerr
!= KCGI_OK
)
4692 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4693 "navs", KATTR__MAX
);
4694 if (kerr
!= KCGI_OK
)
4697 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4698 href_summary
, KATTR__MAX
);
4699 if (kerr
!= KCGI_OK
)
4701 kerr
= khtml_puts(gw_trans
->gw_html_req
, "summary");
4702 if (kerr
!= KCGI_OK
)
4704 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4705 if (kerr
!= KCGI_OK
)
4708 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4709 if (kerr
!= KCGI_OK
)
4712 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4713 gw_trans
->repo_name
, "action", "briefs", "headref",
4715 if (href_briefs
== NULL
) {
4716 error
= got_error_from_errno("khttp_urlpart");
4719 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4720 href_briefs
, KATTR__MAX
);
4721 if (kerr
!= KCGI_OK
)
4723 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commit briefs");
4724 if (kerr
!= KCGI_OK
)
4726 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4727 if (kerr
!= KCGI_OK
)
4730 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4731 if (kerr
!= KCGI_OK
)
4734 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4735 gw_trans
->repo_name
, "action", "commits", "headref",
4737 if (href_commits
== NULL
) {
4738 error
= got_error_from_errno("khttp_urlpart");
4741 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4742 href_commits
, KATTR__MAX
);
4743 if (kerr
!= KCGI_OK
)
4745 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
4746 if (kerr
!= KCGI_OK
)
4748 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4749 if (kerr
!= KCGI_OK
)
4752 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4753 "dotted_line", KATTR__MAX
);
4754 if (kerr
!= KCGI_OK
)
4756 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4757 if (kerr
!= KCGI_OK
)
4760 href_summary
= NULL
;
4764 href_commits
= NULL
;
4767 got_ref_list_free(&refs
);
4774 static const struct got_error
*
4775 gw_output_site_link(struct gw_trans
*gw_trans
)
4777 const struct got_error
*error
= NULL
;
4778 char *href_summary
= NULL
;
4779 enum kcgi_err kerr
= KCGI_OK
;
4781 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4782 "site_link", KATTR__MAX
);
4783 if (kerr
!= KCGI_OK
)
4785 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
, GOTWEB
,
4787 if (kerr
!= KCGI_OK
)
4789 kerr
= khtml_puts(gw_trans
->gw_html_req
,
4790 gw_trans
->gw_conf
->got_site_link
);
4791 if (kerr
!= KCGI_OK
)
4793 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4794 if (kerr
!= KCGI_OK
)
4797 if (gw_trans
->repo_name
!= NULL
) {
4798 kerr
= khtml_puts(gw_trans
->gw_html_req
, " / ");
4799 if (kerr
!= KCGI_OK
)
4802 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4803 gw_trans
->repo_name
, "action", "summary", NULL
);
4804 if (href_summary
== NULL
) {
4805 error
= got_error_from_errno("khttp_urlpart");
4808 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4809 href_summary
, KATTR__MAX
);
4810 if (kerr
!= KCGI_OK
)
4812 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->repo_name
);
4813 if (kerr
!= KCGI_OK
)
4815 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4816 if (kerr
!= KCGI_OK
)
4818 kerr
= khtml_printf(gw_trans
->gw_html_req
, " / %s",
4819 gw_get_action_name(gw_trans
));
4820 if (kerr
!= KCGI_OK
)
4824 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4825 if (kerr
!= KCGI_OK
)
4829 if (error
== NULL
&& kerr
!= KCGI_OK
)
4830 error
= gw_kcgi_error(kerr
);
4834 static const struct got_error
*
4835 gw_colordiff_line(struct gw_trans
*gw_trans
, char *buf
)
4837 const struct got_error
*error
= NULL
;
4838 const char *color
= NULL
;
4839 enum kcgi_err kerr
= KCGI_OK
;
4841 if (strncmp(buf
, "-", 1) == 0)
4842 color
= "diff_minus";
4843 else if (strncmp(buf
, "+", 1) == 0)
4844 color
= "diff_plus";
4845 else if (strncmp(buf
, "@@", 2) == 0)
4846 color
= "diff_chunk_header";
4847 else if (strncmp(buf
, "@@", 2) == 0)
4848 color
= "diff_chunk_header";
4849 else if (strncmp(buf
, "commit +", 8) == 0)
4850 color
= "diff_meta";
4851 else if (strncmp(buf
, "commit -", 8) == 0)
4852 color
= "diff_meta";
4853 else if (strncmp(buf
, "blob +", 6) == 0)
4854 color
= "diff_meta";
4855 else if (strncmp(buf
, "blob -", 6) == 0)
4856 color
= "diff_meta";
4857 else if (strncmp(buf
, "file +", 6) == 0)
4858 color
= "diff_meta";
4859 else if (strncmp(buf
, "file -", 6) == 0)
4860 color
= "diff_meta";
4861 else if (strncmp(buf
, "from:", 5) == 0)
4862 color
= "diff_author";
4863 else if (strncmp(buf
, "via:", 4) == 0)
4864 color
= "diff_author";
4865 else if (strncmp(buf
, "date:", 5) == 0)
4866 color
= "diff_date";
4867 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4868 "diff_line", KATTR_CLASS
, color
? color
: "", KATTR__MAX
);
4869 if (error
== NULL
&& kerr
!= KCGI_OK
)
4870 error
= gw_kcgi_error(kerr
);
4875 main(int argc
, char *argv
[])
4877 const struct got_error
*error
= NULL
, *error2
= NULL
;
4878 struct gw_trans
*gw_trans
;
4879 struct gw_dir
*dir
= NULL
, *tdir
;
4880 const char *page
= "index";
4881 enum kcgi_err kerr
= KCGI_OK
;
4883 if ((gw_trans
= malloc(sizeof(struct gw_trans
))) == NULL
)
4886 if ((gw_trans
->gw_req
= malloc(sizeof(struct kreq
))) == NULL
)
4889 if ((gw_trans
->gw_html_req
= malloc(sizeof(struct khtmlreq
))) == NULL
)
4892 if ((gw_trans
->gw_tmpl
= malloc(sizeof(struct ktemplate
))) == NULL
)
4895 kerr
= khttp_parse(gw_trans
->gw_req
, gw_keys
, KEY__ZMAX
, &page
, 1, 0);
4896 if (kerr
!= KCGI_OK
) {
4897 error
= gw_kcgi_error(kerr
);
4901 TAILQ_INIT(&gw_trans
->gw_dirs
);
4902 TAILQ_INIT(&gw_trans
->gw_headers
);
4904 gw_trans
->action
= -1;
4906 gw_trans
->repos_total
= 0;
4907 gw_trans
->repo_path
= NULL
;
4908 gw_trans
->commit_id
= NULL
;
4909 gw_trans
->next_id
= NULL
;
4910 gw_trans
->prev_id
= NULL
;
4911 gw_trans
->headref
= GOT_REF_HEAD
;
4912 gw_trans
->mime
= KMIME_TEXT_HTML
;
4913 gw_trans
->gw_tmpl
->key
= gw_templs
;
4914 gw_trans
->gw_tmpl
->keysz
= TEMPL__MAX
;
4915 gw_trans
->gw_tmpl
->arg
= gw_trans
;
4916 gw_trans
->gw_tmpl
->cb
= gw_template
;
4918 error
= got_repo_pack_fds_open(&gw_trans
->pack_fds
);
4922 error
= parse_gotweb_config(&gw_trans
->gw_conf
, GOTWEB_CONF
);
4926 error
= gw_parse_querystring(gw_trans
);
4930 if (gw_trans
->repo
) {
4931 const struct got_error
*close_err
;
4932 close_err
= got_repo_close(gw_trans
->repo
);
4936 if (gw_trans
->action
== GW_BLOB
)
4937 error
= gw_blob(gw_trans
);
4939 error
= gw_display_index(gw_trans
);
4942 gw_trans
->error
= error
;
4943 gw_trans
->action
= GW_ERR
;
4944 error2
= gw_display_open(gw_trans
, KHTTP_200
, gw_trans
->mime
);
4946 goto cleanup
; /* we can't display an error page */
4947 kerr
= khtml_open(gw_trans
->gw_html_req
, gw_trans
->gw_req
, 0);
4948 if (kerr
!= KCGI_OK
)
4949 goto cleanup
; /* we can't display an error page */
4950 kerr
= khttp_template(gw_trans
->gw_req
, gw_trans
->gw_tmpl
,
4951 gw_query_funcs
[gw_trans
->action
].template);
4952 if (kerr
!= KCGI_OK
) {
4953 khtml_close(gw_trans
->gw_html_req
);
4954 goto cleanup
; /* we can't display an error page */
4959 if (gw_trans
->pack_fds
) {
4960 const struct got_error
*pack_err
=
4961 got_repo_pack_fds_close(gw_trans
->pack_fds
);
4964 gw_trans
->pack_fds
= NULL
;
4966 free(gw_trans
->gw_conf
->got_repos_path
);
4967 free(gw_trans
->gw_conf
->got_www_path
);
4968 free(gw_trans
->gw_conf
->got_site_name
);
4969 free(gw_trans
->gw_conf
->got_site_owner
);
4970 free(gw_trans
->gw_conf
->got_site_link
);
4971 free(gw_trans
->gw_conf
->got_logo
);
4972 free(gw_trans
->gw_conf
->got_logo_url
);
4973 free(gw_trans
->gw_conf
);
4974 free(gw_trans
->commit_id
);
4975 free(gw_trans
->next_id
);
4976 free(gw_trans
->prev_id
);
4977 free(gw_trans
->repo_path
);
4978 TAILQ_FOREACH_SAFE(dir
, &gw_trans
->gw_dirs
, entry
, tdir
) {
4980 free(dir
->description
);
4987 khttp_free(gw_trans
->gw_req
);