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.
19 #include <sys/types.h>
34 #include <got_error.h>
35 #include <got_object.h>
36 #include <got_reference.h>
37 #include <got_repository.h>
39 #include <got_cancel.h>
40 #include <got_worktree.h>
42 #include <got_commit_graph.h>
43 #include <got_blame.h>
44 #include <got_privsep.h>
45 #include <got_opentemp.h>
53 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
57 TAILQ_HEAD(headers
, gw_header
) gw_headers
;
58 TAILQ_HEAD(dirs
, gw_dir
) gw_dirs
;
59 struct got_repository
*repo
;
60 struct gw_dir
*gw_dir
;
61 struct gotweb_config
*gw_conf
;
62 struct ktemplate
*gw_tmpl
;
63 struct khtmlreq
*gw_html_req
;
65 const struct got_error
*error
;
66 const char *repo_name
;
71 const char *repo_file
;
76 unsigned int repos_total
;
82 TAILQ_ENTRY(gw_header
) entry
;
83 struct got_reflist_head refs
;
87 char *commit_id
; /* id_str1 */
88 char *parent_id
; /* id_str2 */
93 time_t committer_time
;
97 TAILQ_ENTRY(gw_dir
) entry
;
139 static const char *const gw_templs
[TEMPL__MAX
] = {
149 static const struct kvalid gw_keys
[KEY__ZMAX
] = {
150 { kvalid_stringne
, "action" },
151 { kvalid_stringne
, "commit" },
152 { kvalid_stringne
, "file" },
153 { kvalid_stringne
, "folder" },
154 { kvalid_stringne
, "headref" },
155 { kvalid_int
, "page" },
156 { kvalid_stringne
, "path" },
157 { kvalid_stringne
, "prev" },
160 static struct gw_header
*gw_init_header(void);
162 static void gw_free_header(struct gw_header
*);
164 static int gw_template(size_t, void *);
166 static const struct got_error
*gw_error(struct gw_trans
*);
167 static const struct got_error
*gw_init_gw_dir(struct gw_dir
**, const char *);
168 static const struct got_error
*gw_get_repo_description(char **,
169 struct gw_trans
*, char *);
170 static const struct got_error
*gw_get_repo_owner(char **, struct gw_trans
*,
172 static const struct got_error
*gw_get_time_str(char **, time_t, int);
173 static const struct got_error
*gw_get_repo_age(char **, struct gw_trans
*,
174 char *, const char *, int);
175 static const struct got_error
*gw_output_file_blame(struct gw_trans
*,
177 static const struct got_error
*gw_output_blob_buf(struct gw_trans
*,
179 static const struct got_error
*gw_output_repo_tree(struct gw_trans
*,
181 static const struct got_error
*gw_output_diff(struct gw_trans
*,
183 static const struct got_error
*gw_output_repo_tags(struct gw_trans
*,
184 struct gw_header
*, int, int);
185 static const struct got_error
*gw_output_repo_heads(struct gw_trans
*);
186 static const struct got_error
*gw_output_site_link(struct gw_trans
*);
187 static const struct got_error
*gw_get_clone_url(char **, struct gw_trans
*,
189 static const struct got_error
*gw_colordiff_line(struct gw_trans
*, char *);
191 static const struct got_error
*gw_gen_commit_header(struct gw_trans
*, char *,
193 static const struct got_error
*gw_gen_diff_header(struct gw_trans
*, char *,
195 static const struct got_error
*gw_gen_author_header(struct gw_trans
*,
197 static const struct got_error
*gw_gen_age_header(struct gw_trans
*,
199 static const struct got_error
*gw_gen_committer_header(struct gw_trans
*,
201 static const struct got_error
*gw_gen_commit_msg_header(struct gw_trans
*,
203 static const struct got_error
*gw_gen_tree_header(struct gw_trans
*, char *);
204 static const struct got_error
*gw_display_open(struct gw_trans
*, enum khttp
,
206 static const struct got_error
*gw_display_index(struct gw_trans
*);
207 static const struct got_error
*gw_get_header(struct gw_trans
*,
208 struct gw_header
*, int);
209 static const struct got_error
*gw_get_commits(struct gw_trans
*,
210 struct gw_header
*, int,
211 struct got_object_id
*);
212 static const struct got_error
*gw_get_commit(struct gw_trans
*,
214 struct got_commit_object
*,
215 struct got_object_id
*);
216 static const struct got_error
*gw_apply_unveil(const char *);
217 static const struct got_error
*gw_blame_cb(void *, int, int,
218 struct got_commit_object
*,
219 struct got_object_id
*);
220 static const struct got_error
*gw_load_got_paths(struct gw_trans
*);
221 static const struct got_error
*gw_load_got_path(struct gw_trans
*,
223 static const struct got_error
*gw_parse_querystring(struct gw_trans
*);
224 static const struct got_error
*gw_blame(struct gw_trans
*);
225 static const struct got_error
*gw_blob(struct gw_trans
*);
226 static const struct got_error
*gw_diff(struct gw_trans
*);
227 static const struct got_error
*gw_index(struct gw_trans
*);
228 static const struct got_error
*gw_commits(struct gw_trans
*);
229 static const struct got_error
*gw_briefs(struct gw_trans
*);
230 static const struct got_error
*gw_summary(struct gw_trans
*);
231 static const struct got_error
*gw_tree(struct gw_trans
*);
232 static const struct got_error
*gw_tag(struct gw_trans
*);
233 static const struct got_error
*gw_tags(struct gw_trans
*);
235 struct gw_query_action
{
236 unsigned int func_id
;
237 const char *func_name
;
238 const struct got_error
*(*func_main
)(struct gw_trans
*);
239 const char *template;
242 enum gw_query_actions
{
256 static const struct gw_query_action gw_query_funcs
[] = {
257 { GW_BLAME
, "blame", gw_blame
, "gw_tmpl/blame.tmpl" },
258 { GW_BLOB
, "blob", NULL
, NULL
},
259 { GW_BRIEFS
, "briefs", gw_briefs
, "gw_tmpl/briefs.tmpl" },
260 { GW_COMMITS
, "commits", gw_commits
, "gw_tmpl/commit.tmpl" },
261 { GW_DIFF
, "diff", gw_diff
, "gw_tmpl/diff.tmpl" },
262 { GW_ERR
, "error", gw_error
, "gw_tmpl/err.tmpl" },
263 { GW_INDEX
, "index", gw_index
, "gw_tmpl/index.tmpl" },
264 { GW_SUMMARY
, "summary", gw_summary
, "gw_tmpl/summry.tmpl" },
265 { GW_TAG
, "tag", gw_tag
, "gw_tmpl/tag.tmpl" },
266 { GW_TAGS
, "tags", gw_tags
, "gw_tmpl/tags.tmpl" },
267 { GW_TREE
, "tree", gw_tree
, "gw_tmpl/tree.tmpl" },
271 gw_get_action_name(struct gw_trans
*gw_trans
)
273 return gw_query_funcs
[gw_trans
->action
].func_name
;
276 static const struct got_error
*
277 gw_kcgi_error(enum kcgi_err kerr
)
282 if (kerr
== KCGI_EXIT
|| kerr
== KCGI_HUP
)
283 return got_error(GOT_ERR_CANCELLED
);
285 if (kerr
== KCGI_ENOMEM
)
286 return got_error_set_errno(ENOMEM
,
287 kcgi_strerror(kerr
));
289 if (kerr
== KCGI_ENFILE
)
290 return got_error_set_errno(ENFILE
,
291 kcgi_strerror(kerr
));
293 if (kerr
== KCGI_EAGAIN
)
294 return got_error_set_errno(EAGAIN
,
295 kcgi_strerror(kerr
));
297 if (kerr
== KCGI_FORM
)
298 return got_error_msg(GOT_ERR_IO
,
299 kcgi_strerror(kerr
));
301 return got_error_from_errno(kcgi_strerror(kerr
));
304 static const struct got_error
*
305 gw_apply_unveil(const char *repo_path
)
307 const struct got_error
*err
;
310 if (unveil("gmon.out", "rwc") != 0)
311 return got_error_from_errno2("unveil", "gmon.out");
313 if (repo_path
&& unveil(repo_path
, "r") != 0)
314 return got_error_from_errno2("unveil", repo_path
);
316 if (unveil(GOT_TMPDIR_STR
, "rwc") != 0)
317 return got_error_from_errno2("unveil", GOT_TMPDIR_STR
);
319 err
= got_privsep_unveil_exec_helpers();
323 if (unveil(NULL
, NULL
) != 0)
324 return got_error_from_errno("unveil");
330 isbinary(const uint8_t *buf
, size_t n
)
334 for (i
= 0; i
< n
; i
++)
340 static const struct got_error
*
341 gw_blame(struct gw_trans
*gw_trans
)
343 const struct got_error
*error
= NULL
;
344 struct gw_header
*header
= NULL
;
346 enum kcgi_err kerr
= KCGI_OK
;
349 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
351 return got_error_from_errno("pledge");
353 if ((header
= gw_init_header()) == NULL
)
354 return got_error_from_errno("malloc");
356 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
360 /* check querystring */
361 if (gw_trans
->repo_file
== NULL
) {
362 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
363 "file required in querystring");
366 if (gw_trans
->commit_id
== NULL
) {
367 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
368 "commit required in querystring");
372 error
= gw_get_header(gw_trans
, header
, 1);
375 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
376 "blame_header_wrapper", KATTR__MAX
);
379 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
380 "blame_header", KATTR__MAX
);
383 error
= gw_get_time_str(&age
, header
->committer_time
,
387 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
390 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
393 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
396 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
397 "dotted_line", KATTR__MAX
);
400 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
404 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
405 "blame", KATTR__MAX
);
408 error
= gw_output_file_blame(gw_trans
, header
);
411 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
413 gw_free_header(header
);
414 if (error
== NULL
&& kerr
!= KCGI_OK
)
415 error
= gw_kcgi_error(kerr
);
419 static const struct got_error
*
420 gw_blob(struct gw_trans
*gw_trans
)
422 const struct got_error
*error
= NULL
, *err
= NULL
;
423 struct gw_header
*header
= NULL
;
424 enum kcgi_err kerr
= KCGI_OK
;
427 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
429 return got_error_from_errno("pledge");
431 if ((header
= gw_init_header()) == NULL
)
432 return got_error_from_errno("malloc");
434 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
438 /* check querystring */
439 if (gw_trans
->repo_file
== NULL
) {
440 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
441 "file required in querystring");
444 if (gw_trans
->commit_id
== NULL
) {
445 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
446 "commit required in querystring");
449 error
= gw_get_header(gw_trans
, header
, 1);
453 error
= gw_output_blob_buf(gw_trans
, header
);
456 gw_trans
->mime
= KMIME_TEXT_PLAIN
;
457 err
= gw_display_index(gw_trans
);
462 kerr
= khttp_puts(gw_trans
->gw_req
, error
->msg
);
465 gw_free_header(header
);
466 if (error
== NULL
&& kerr
!= KCGI_OK
)
467 error
= gw_kcgi_error(kerr
);
471 static const struct got_error
*
472 gw_diff(struct gw_trans
*gw_trans
)
474 const struct got_error
*error
= NULL
;
475 struct gw_header
*header
= NULL
;
477 enum kcgi_err kerr
= KCGI_OK
;
480 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
482 return got_error_from_errno("pledge");
484 if ((header
= gw_init_header()) == NULL
)
485 return got_error_from_errno("malloc");
487 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
491 error
= gw_get_header(gw_trans
, header
, 1);
495 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
496 "diff_header_wrapper", KATTR__MAX
);
499 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
500 "diff_header", KATTR__MAX
);
503 error
= gw_gen_diff_header(gw_trans
, header
->parent_id
,
507 error
= gw_gen_commit_header(gw_trans
, header
->commit_id
,
511 error
= gw_gen_tree_header(gw_trans
, header
->tree_id
);
514 error
= gw_gen_author_header(gw_trans
, header
->author
);
517 error
= gw_gen_committer_header(gw_trans
, header
->author
);
520 error
= gw_get_time_str(&age
, header
->committer_time
,
524 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
527 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
530 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
533 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
534 "dotted_line", KATTR__MAX
);
537 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
541 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
545 error
= gw_output_diff(gw_trans
, header
);
549 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
551 gw_free_header(header
);
553 if (error
== NULL
&& kerr
!= KCGI_OK
)
554 error
= gw_kcgi_error(kerr
);
558 static const struct got_error
*
559 gw_index(struct gw_trans
*gw_trans
)
561 const struct got_error
*error
= NULL
;
562 struct gw_dir
*gw_dir
= NULL
;
563 char *href_next
= NULL
, *href_prev
= NULL
, *href_summary
= NULL
;
564 char *href_briefs
= NULL
, *href_commits
= NULL
, *href_tree
= NULL
;
565 char *href_tags
= NULL
;
566 unsigned int prev_disp
= 0, next_disp
= 1, dir_c
= 0;
567 enum kcgi_err kerr
= KCGI_OK
;
570 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
572 error
= got_error_from_errno("pledge");
576 error
= gw_apply_unveil(gw_trans
->gw_conf
->got_repos_path
);
580 error
= gw_load_got_paths(gw_trans
);
584 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
585 "index_header", KATTR__MAX
);
587 return gw_kcgi_error(kerr
);
588 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
589 "index_header_project", KATTR__MAX
);
591 return gw_kcgi_error(kerr
);
592 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Project");
594 return gw_kcgi_error(kerr
);
595 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
597 return gw_kcgi_error(kerr
);
599 if (gw_trans
->gw_conf
->got_show_repo_description
) {
600 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
601 "index_header_description", KATTR__MAX
);
603 return gw_kcgi_error(kerr
);
604 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Description");
606 return gw_kcgi_error(kerr
);
607 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
609 return gw_kcgi_error(kerr
);
612 if (gw_trans
->gw_conf
->got_show_repo_owner
) {
613 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
614 "index_header_owner", KATTR__MAX
);
616 return gw_kcgi_error(kerr
);
617 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Owner");
619 return gw_kcgi_error(kerr
);
620 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
622 return gw_kcgi_error(kerr
);
625 if (gw_trans
->gw_conf
->got_show_repo_age
) {
626 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
627 "index_header_age", KATTR__MAX
);
629 return gw_kcgi_error(kerr
);
630 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Last Change");
632 return gw_kcgi_error(kerr
);
633 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
635 return gw_kcgi_error(kerr
);
638 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
640 return gw_kcgi_error(kerr
);
642 if (TAILQ_EMPTY(&gw_trans
->gw_dirs
)) {
643 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
644 "index_wrapper", KATTR__MAX
);
646 return gw_kcgi_error(kerr
);
647 kerr
= khtml_printf(gw_trans
->gw_html_req
,
648 "No repositories found in %s",
649 gw_trans
->gw_conf
->got_repos_path
);
651 return gw_kcgi_error(kerr
);
652 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
654 return gw_kcgi_error(kerr
);
655 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
656 "dotted_line", KATTR__MAX
);
658 return gw_kcgi_error(kerr
);
659 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
661 return gw_kcgi_error(kerr
);
665 TAILQ_FOREACH(gw_dir
, &gw_trans
->gw_dirs
, entry
)
668 TAILQ_FOREACH(gw_dir
, &gw_trans
->gw_dirs
, entry
) {
669 if (gw_trans
->page
> 0 && (gw_trans
->page
*
670 gw_trans
->gw_conf
->got_max_repos_display
) > prev_disp
) {
677 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
678 "index_wrapper", KATTR__MAX
);
682 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
683 gw_dir
->name
, "action", "summary", NULL
);
684 if (href_summary
== NULL
) {
685 error
= got_error_from_errno("khttp_urlpart");
688 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
689 "index_project", KATTR__MAX
);
692 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
693 href_summary
, KATTR__MAX
);
696 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_dir
->name
);
699 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
702 if (gw_trans
->gw_conf
->got_show_repo_description
) {
703 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
704 KATTR_ID
, "index_project_description", KATTR__MAX
);
707 kerr
= khtml_puts(gw_trans
->gw_html_req
,
708 gw_dir
->description
? gw_dir
->description
: "");
711 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
715 if (gw_trans
->gw_conf
->got_show_repo_owner
) {
716 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
717 KATTR_ID
, "index_project_owner", KATTR__MAX
);
720 kerr
= khtml_puts(gw_trans
->gw_html_req
,
721 gw_dir
->owner
? gw_dir
->owner
: "");
724 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
728 if (gw_trans
->gw_conf
->got_show_repo_age
) {
729 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
730 KATTR_ID
, "index_project_age", KATTR__MAX
);
733 kerr
= khtml_puts(gw_trans
->gw_html_req
,
734 gw_dir
->age
? gw_dir
->age
: "");
737 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
742 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
743 "navs_wrapper", KATTR__MAX
);
746 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
751 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
752 href_summary
, KATTR__MAX
);
755 kerr
= khtml_puts(gw_trans
->gw_html_req
, "summary");
758 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
762 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
766 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
767 gw_dir
->name
, "action", "briefs", NULL
);
768 if (href_briefs
== NULL
) {
769 error
= got_error_from_errno("khttp_urlpart");
772 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
773 href_briefs
, KATTR__MAX
);
776 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commit briefs");
779 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
781 error
= gw_kcgi_error(kerr
);
783 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
787 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
788 gw_dir
->name
, "action", "commits", NULL
);
789 if (href_commits
== NULL
) {
790 error
= got_error_from_errno("khttp_urlpart");
793 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
794 href_commits
, KATTR__MAX
);
797 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
800 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
804 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
808 href_tags
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
809 gw_dir
->name
, "action", "tags", NULL
);
810 if (href_tags
== NULL
) {
811 error
= got_error_from_errno("khttp_urlpart");
814 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
815 href_tags
, KATTR__MAX
);
818 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tags");
821 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
825 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
829 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
830 gw_dir
->name
, "action", "tree", NULL
);
831 if (href_tree
== NULL
) {
832 error
= got_error_from_errno("khttp_urlpart");
835 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
836 href_tree
, KATTR__MAX
);
839 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tree");
843 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 4);
846 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
847 "dotted_line", KATTR__MAX
);
850 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
865 if (gw_trans
->gw_conf
->got_max_repos_display
== 0)
868 if ((next_disp
== gw_trans
->gw_conf
->got_max_repos_display
) ||
869 ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
870 (gw_trans
->page
> 0) &&
871 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
872 prev_disp
== gw_trans
->repos_total
))) {
873 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
874 KATTR_ID
, "np_wrapper", KATTR__MAX
);
877 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
878 KATTR_ID
, "nav_prev", KATTR__MAX
);
883 if ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
884 (gw_trans
->page
> 0) &&
885 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
886 prev_disp
== gw_trans
->repos_total
)) {
887 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "page",
888 KATTRX_INT
, (int64_t)(gw_trans
->page
- 1), NULL
);
889 if (href_prev
== NULL
) {
890 error
= got_error_from_errno("khttp_urlpartx");
893 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
894 KATTR_HREF
, href_prev
, KATTR__MAX
);
897 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
900 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
905 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
907 return gw_kcgi_error(kerr
);
909 if (gw_trans
->gw_conf
->got_max_repos_display
> 0 &&
910 next_disp
== gw_trans
->gw_conf
->got_max_repos_display
&&
911 dir_c
!= (gw_trans
->page
+ 1) *
912 gw_trans
->gw_conf
->got_max_repos_display
) {
913 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
914 KATTR_ID
, "nav_next", KATTR__MAX
);
917 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "page",
918 KATTRX_INT
, (int64_t)(gw_trans
->page
+ 1), NULL
);
919 if (href_next
== NULL
) {
920 error
= got_error_from_errno("khttp_urlpartx");
923 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
924 KATTR_HREF
, href_next
, KATTR__MAX
);
927 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
930 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
937 if ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
938 (gw_trans
->page
> 0) &&
939 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
940 prev_disp
== gw_trans
->repos_total
)) {
941 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
955 if (error
== NULL
&& kerr
!= KCGI_OK
)
956 error
= gw_kcgi_error(kerr
);
960 static const struct got_error
*
961 gw_commits(struct gw_trans
*gw_trans
)
963 const struct got_error
*error
= NULL
;
964 struct gw_header
*header
= NULL
, *n_header
= NULL
;
965 char *age
= NULL
, *href_diff
= NULL
, *href_tree
= NULL
;
966 char *href_prev
= NULL
, *href_next
= NULL
;
967 enum kcgi_err kerr
= KCGI_OK
;
968 int commit_found
= 0;
970 if ((header
= gw_init_header()) == NULL
)
971 return got_error_from_errno("malloc");
974 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
976 error
= got_error_from_errno("pledge");
980 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
984 error
= gw_get_header(gw_trans
, header
,
985 gw_trans
->gw_conf
->got_max_commits_display
);
989 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
) {
990 if (commit_found
== 0 && gw_trans
->commit_id
!= NULL
) {
991 if (strcmp(gw_trans
->commit_id
,
992 n_header
->commit_id
) != 0)
997 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
998 "commits_line_wrapper", KATTR__MAX
);
1001 error
= gw_gen_commit_header(gw_trans
, n_header
->commit_id
,
1002 n_header
->refs_str
);
1005 error
= gw_gen_author_header(gw_trans
, n_header
->author
);
1008 error
= gw_gen_committer_header(gw_trans
, n_header
->author
);
1011 error
= gw_get_time_str(&age
, n_header
->committer_time
,
1015 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
1018 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1019 if (kerr
!= KCGI_OK
)
1022 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1023 "dotted_line", KATTR__MAX
);
1024 if (kerr
!= KCGI_OK
)
1026 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1027 if (kerr
!= KCGI_OK
)
1030 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1031 "commit", KATTR__MAX
);
1032 if (kerr
!= KCGI_OK
)
1034 kerr
= khttp_puts(gw_trans
->gw_req
, n_header
->commit_msg
);
1035 if (kerr
!= KCGI_OK
)
1037 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1038 if (kerr
!= KCGI_OK
)
1041 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1042 gw_trans
->repo_name
, "action", "diff", "commit",
1043 n_header
->commit_id
, NULL
);
1044 if (href_diff
== NULL
) {
1045 error
= got_error_from_errno("khttp_urlpart");
1048 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1049 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
1050 if (kerr
!= KCGI_OK
)
1052 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1053 KATTR_ID
, "navs", KATTR__MAX
);
1054 if (kerr
!= KCGI_OK
)
1056 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1057 KATTR_HREF
, href_diff
, KATTR__MAX
);
1058 if (kerr
!= KCGI_OK
)
1060 kerr
= khtml_puts(gw_trans
->gw_html_req
, "diff");
1061 if (kerr
!= KCGI_OK
)
1063 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1064 if (kerr
!= KCGI_OK
)
1067 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
1068 if (kerr
!= KCGI_OK
)
1071 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1072 gw_trans
->repo_name
, "action", "tree", "commit",
1073 n_header
->commit_id
, NULL
);
1074 if (href_tree
== NULL
) {
1075 error
= got_error_from_errno("khttp_urlpart");
1078 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1079 KATTR_HREF
, href_tree
, KATTR__MAX
);
1080 if (kerr
!= KCGI_OK
)
1082 khtml_puts(gw_trans
->gw_html_req
, "tree");
1083 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1084 if (kerr
!= KCGI_OK
)
1086 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1087 if (kerr
!= KCGI_OK
)
1090 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1091 "solid_line", KATTR__MAX
);
1092 if (kerr
!= KCGI_OK
)
1094 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1095 if (kerr
!= KCGI_OK
)
1102 if (gw_trans
->next_id
|| gw_trans
->prev_id
) {
1103 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1104 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1105 if (kerr
!= KCGI_OK
)
1107 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1108 KATTR_ID
, "nav_prev", KATTR__MAX
);
1109 if (kerr
!= KCGI_OK
)
1113 if (gw_trans
->prev_id
) {
1114 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1115 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1116 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1117 KATTRX_STRING
, "commits", "commit", KATTRX_STRING
,
1118 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1119 if (href_prev
== NULL
) {
1120 error
= got_error_from_errno("khttp_urlpartx");
1123 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1124 KATTR_HREF
, href_prev
, KATTR__MAX
);
1125 if (kerr
!= KCGI_OK
)
1127 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1128 if (kerr
!= KCGI_OK
)
1130 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1131 if (kerr
!= KCGI_OK
)
1135 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1136 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1137 if (kerr
!= KCGI_OK
)
1138 return gw_kcgi_error(kerr
);
1141 if (gw_trans
->next_id
) {
1142 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1143 KATTR_ID
, "nav_next", KATTR__MAX
);
1144 if (kerr
!= KCGI_OK
)
1146 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1147 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1148 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1149 KATTRX_STRING
, "commits", "commit", KATTRX_STRING
,
1150 gw_trans
->next_id
, NULL
);
1151 if (href_next
== NULL
) {
1152 error
= got_error_from_errno("khttp_urlpartx");
1155 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1156 KATTR_HREF
, href_next
, KATTR__MAX
);
1157 if (kerr
!= KCGI_OK
)
1159 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1160 if (kerr
!= KCGI_OK
)
1162 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1163 if (kerr
!= KCGI_OK
)
1167 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1168 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1169 if (kerr
!= KCGI_OK
)
1173 gw_free_header(header
);
1174 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
)
1175 gw_free_header(n_header
);
1181 if (error
== NULL
&& kerr
!= KCGI_OK
)
1182 error
= gw_kcgi_error(kerr
);
1186 static const struct got_error
*
1187 gw_briefs(struct gw_trans
*gw_trans
)
1189 const struct got_error
*error
= NULL
;
1190 struct gw_header
*header
= NULL
, *n_header
= NULL
;
1191 char *age
= NULL
, *href_diff
= NULL
, *href_tree
= NULL
;
1192 char *href_prev
= NULL
, *href_next
= NULL
;
1193 char *newline
, *smallerthan
;
1194 enum kcgi_err kerr
= KCGI_OK
;
1195 int commit_found
= 0;
1197 if ((header
= gw_init_header()) == NULL
)
1198 return got_error_from_errno("malloc");
1201 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
1203 error
= got_error_from_errno("pledge");
1207 if (gw_trans
->action
!= GW_SUMMARY
) {
1208 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1213 if (gw_trans
->action
== GW_SUMMARY
)
1214 error
= gw_get_header(gw_trans
, header
, D_MAXSLCOMMDISP
);
1216 error
= gw_get_header(gw_trans
, header
,
1217 gw_trans
->gw_conf
->got_max_commits_display
);
1221 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
) {
1222 if (commit_found
== 0 && gw_trans
->commit_id
!= NULL
) {
1223 if (strcmp(gw_trans
->commit_id
,
1224 n_header
->commit_id
) != 0)
1229 error
= gw_get_time_str(&age
, n_header
->committer_time
,
1234 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1235 KATTR_ID
, "briefs_wrapper", KATTR__MAX
);
1236 if (kerr
!= KCGI_OK
)
1239 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1240 KATTR_ID
, "briefs_age", KATTR__MAX
);
1241 if (kerr
!= KCGI_OK
)
1243 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
? age
: "");
1244 if (kerr
!= KCGI_OK
)
1246 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1247 if (kerr
!= KCGI_OK
)
1250 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1251 KATTR_ID
, "briefs_author", KATTR__MAX
);
1252 if (kerr
!= KCGI_OK
)
1254 smallerthan
= strchr(n_header
->author
, '<');
1256 *smallerthan
= '\0';
1257 kerr
= khtml_puts(gw_trans
->gw_html_req
, n_header
->author
);
1258 if (kerr
!= KCGI_OK
)
1260 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1261 if (kerr
!= KCGI_OK
)
1264 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1265 gw_trans
->repo_name
, "action", "diff", "commit",
1266 n_header
->commit_id
, NULL
);
1267 if (href_diff
== NULL
) {
1268 error
= got_error_from_errno("khttp_urlpart");
1271 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1272 KATTR_ID
, "briefs_log", KATTR__MAX
);
1273 if (kerr
!= KCGI_OK
)
1275 newline
= strchr(n_header
->commit_msg
, '\n');
1278 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1279 KATTR_HREF
, href_diff
, KATTR__MAX
);
1280 if (kerr
!= KCGI_OK
)
1282 kerr
= khtml_puts(gw_trans
->gw_html_req
, n_header
->commit_msg
);
1283 if (kerr
!= KCGI_OK
)
1285 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1286 if (kerr
!= KCGI_OK
)
1289 if (n_header
->refs_str
) {
1290 kerr
= khtml_puts(gw_trans
->gw_html_req
, " ");
1291 if (kerr
!= KCGI_OK
)
1293 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_SPAN
,
1294 KATTR_ID
, "refs_str", KATTR__MAX
);
1295 if (kerr
!= KCGI_OK
)
1297 kerr
= khtml_printf(gw_trans
->gw_html_req
, "(%s)",
1298 n_header
->refs_str
);
1299 if (kerr
!= KCGI_OK
)
1301 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1302 if (kerr
!= KCGI_OK
)
1306 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1307 if (kerr
!= KCGI_OK
)
1310 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1311 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
1312 if (kerr
!= KCGI_OK
)
1314 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1315 KATTR_ID
, "navs", KATTR__MAX
);
1316 if (kerr
!= KCGI_OK
)
1318 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1319 KATTR_HREF
, href_diff
, KATTR__MAX
);
1320 if (kerr
!= KCGI_OK
)
1322 kerr
= khtml_puts(gw_trans
->gw_html_req
, "diff");
1323 if (kerr
!= KCGI_OK
)
1325 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1326 if (kerr
!= KCGI_OK
)
1329 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
1330 if (kerr
!= KCGI_OK
)
1333 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1334 gw_trans
->repo_name
, "action", "tree", "commit",
1335 n_header
->commit_id
, NULL
);
1336 if (href_tree
== NULL
) {
1337 error
= got_error_from_errno("khttp_urlpart");
1340 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1341 KATTR_HREF
, href_tree
, KATTR__MAX
);
1342 if (kerr
!= KCGI_OK
)
1344 khtml_puts(gw_trans
->gw_html_req
, "tree");
1345 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1346 if (kerr
!= KCGI_OK
)
1348 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1349 if (kerr
!= KCGI_OK
)
1352 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1353 KATTR_ID
, "dotted_line", KATTR__MAX
);
1354 if (kerr
!= KCGI_OK
)
1356 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1357 if (kerr
!= KCGI_OK
)
1368 if (gw_trans
->next_id
|| gw_trans
->prev_id
) {
1369 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1370 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1371 if (kerr
!= KCGI_OK
)
1373 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1374 KATTR_ID
, "nav_prev", KATTR__MAX
);
1375 if (kerr
!= KCGI_OK
)
1379 if (gw_trans
->prev_id
) {
1380 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1381 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1382 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1383 KATTRX_STRING
, "briefs", "commit", KATTRX_STRING
,
1384 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1385 if (href_prev
== NULL
) {
1386 error
= got_error_from_errno("khttp_urlpartx");
1389 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1390 KATTR_HREF
, href_prev
, KATTR__MAX
);
1391 if (kerr
!= KCGI_OK
)
1393 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1394 if (kerr
!= KCGI_OK
)
1396 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1397 if (kerr
!= KCGI_OK
)
1401 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1402 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1403 if (kerr
!= KCGI_OK
)
1404 return gw_kcgi_error(kerr
);
1407 if (gw_trans
->next_id
) {
1408 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1409 KATTR_ID
, "nav_next", KATTR__MAX
);
1410 if (kerr
!= KCGI_OK
)
1413 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1414 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1415 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1416 KATTRX_STRING
, "briefs", "commit", KATTRX_STRING
,
1417 gw_trans
->next_id
, NULL
);
1418 if (href_next
== NULL
) {
1419 error
= got_error_from_errno("khttp_urlpartx");
1422 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1423 KATTR_HREF
, href_next
, KATTR__MAX
);
1424 if (kerr
!= KCGI_OK
)
1426 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1427 if (kerr
!= KCGI_OK
)
1429 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1430 if (kerr
!= KCGI_OK
)
1434 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1435 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1436 if (kerr
!= KCGI_OK
)
1440 gw_free_header(header
);
1441 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
)
1442 gw_free_header(n_header
);
1448 if (error
== NULL
&& kerr
!= KCGI_OK
)
1449 error
= gw_kcgi_error(kerr
);
1453 static const struct got_error
*
1454 gw_summary(struct gw_trans
*gw_trans
)
1456 const struct got_error
*error
= NULL
;
1458 enum kcgi_err kerr
= KCGI_OK
;
1461 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
1463 return got_error_from_errno("pledge");
1465 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1469 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1470 "summary_wrapper", KATTR__MAX
);
1471 if (kerr
!= KCGI_OK
)
1472 return gw_kcgi_error(kerr
);
1474 if (gw_trans
->gw_conf
->got_show_repo_description
&&
1475 gw_trans
->gw_dir
->description
!= NULL
&&
1476 (strcmp(gw_trans
->gw_dir
->description
, "") != 0)) {
1477 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1478 KATTR_ID
, "description_title", KATTR__MAX
);
1479 if (kerr
!= KCGI_OK
)
1481 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Description: ");
1482 if (kerr
!= KCGI_OK
)
1484 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1485 if (kerr
!= KCGI_OK
)
1487 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1488 KATTR_ID
, "description", KATTR__MAX
);
1489 if (kerr
!= KCGI_OK
)
1491 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1492 gw_trans
->gw_dir
->description
);
1493 if (kerr
!= KCGI_OK
)
1495 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1496 if (kerr
!= KCGI_OK
)
1500 if (gw_trans
->gw_conf
->got_show_repo_owner
&&
1501 gw_trans
->gw_dir
->owner
!= NULL
&&
1502 (strcmp(gw_trans
->gw_dir
->owner
, "") != 0)) {
1503 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1504 KATTR_ID
, "repo_owner_title", KATTR__MAX
);
1505 if (kerr
!= KCGI_OK
)
1507 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Owner: ");
1508 if (kerr
!= KCGI_OK
)
1510 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1511 if (kerr
!= KCGI_OK
)
1513 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1514 KATTR_ID
, "repo_owner", KATTR__MAX
);
1515 if (kerr
!= KCGI_OK
)
1517 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1518 gw_trans
->gw_dir
->owner
);
1519 if (kerr
!= KCGI_OK
)
1521 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1522 if (kerr
!= KCGI_OK
)
1526 if (gw_trans
->gw_conf
->got_show_repo_age
) {
1527 error
= gw_get_repo_age(&age
, gw_trans
, gw_trans
->gw_dir
->path
,
1532 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1533 KATTR_ID
, "last_change_title", KATTR__MAX
);
1534 if (kerr
!= KCGI_OK
)
1536 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1538 if (kerr
!= KCGI_OK
)
1540 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1541 if (kerr
!= KCGI_OK
)
1543 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1544 KATTR_ID
, "last_change", KATTR__MAX
);
1545 if (kerr
!= KCGI_OK
)
1547 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
);
1548 if (kerr
!= KCGI_OK
)
1550 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1551 if (kerr
!= KCGI_OK
)
1556 if (gw_trans
->gw_conf
->got_show_repo_cloneurl
&&
1557 gw_trans
->gw_dir
->url
!= NULL
&&
1558 (strcmp(gw_trans
->gw_dir
->url
, "") != 0)) {
1559 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1560 KATTR_ID
, "cloneurl_title", KATTR__MAX
);
1561 if (kerr
!= KCGI_OK
)
1563 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Clone URL: ");
1564 if (kerr
!= KCGI_OK
)
1566 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1567 if (kerr
!= KCGI_OK
)
1569 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1570 KATTR_ID
, "cloneurl", KATTR__MAX
);
1571 if (kerr
!= KCGI_OK
)
1573 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->gw_dir
->url
);
1574 if (kerr
!= KCGI_OK
)
1576 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1577 if (kerr
!= KCGI_OK
)
1581 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1582 if (kerr
!= KCGI_OK
)
1585 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1586 "briefs_title_wrapper", KATTR__MAX
);
1587 if (kerr
!= KCGI_OK
)
1589 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1590 "briefs_title", KATTR__MAX
);
1591 if (kerr
!= KCGI_OK
)
1593 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Commit Briefs");
1594 if (kerr
!= KCGI_OK
)
1596 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1597 if (kerr
!= KCGI_OK
)
1599 error
= gw_briefs(gw_trans
);
1603 error
= gw_tags(gw_trans
);
1607 error
= gw_output_repo_heads(gw_trans
);
1610 if (error
== NULL
&& kerr
!= KCGI_OK
)
1611 error
= gw_kcgi_error(kerr
);
1615 static const struct got_error
*
1616 gw_tree(struct gw_trans
*gw_trans
)
1618 const struct got_error
*error
= NULL
;
1619 struct gw_header
*header
= NULL
;
1620 char *tree
= NULL
, *tree_html
= NULL
, *tree_html_disp
= NULL
;
1622 enum kcgi_err kerr
= KCGI_OK
;
1625 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
1627 return got_error_from_errno("pledge");
1629 if ((header
= gw_init_header()) == NULL
)
1630 return got_error_from_errno("malloc");
1632 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1636 error
= gw_get_header(gw_trans
, header
, 1);
1640 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1641 "tree_header_wrapper", KATTR__MAX
);
1642 if (kerr
!= KCGI_OK
)
1644 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1645 "tree_header", KATTR__MAX
);
1646 if (kerr
!= KCGI_OK
)
1648 error
= gw_gen_tree_header(gw_trans
, header
->tree_id
);
1651 error
= gw_get_time_str(&age
, header
->committer_time
,
1655 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
1658 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
1661 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1662 if (kerr
!= KCGI_OK
)
1664 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1665 "dotted_line", KATTR__MAX
);
1666 if (kerr
!= KCGI_OK
)
1668 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1669 if (kerr
!= KCGI_OK
)
1672 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1673 "tree", KATTR__MAX
);
1674 if (kerr
!= KCGI_OK
)
1676 error
= gw_output_repo_tree(gw_trans
, header
);
1680 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1682 gw_free_header(header
);
1683 free(tree_html_disp
);
1687 if (error
== NULL
&& kerr
!= KCGI_OK
)
1688 error
= gw_kcgi_error(kerr
);
1692 static const struct got_error
*
1693 gw_tags(struct gw_trans
*gw_trans
)
1695 const struct got_error
*error
= NULL
;
1696 struct gw_header
*header
= NULL
;
1697 char *href_next
= NULL
, *href_prev
= NULL
;
1698 enum kcgi_err kerr
= KCGI_OK
;
1701 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
1703 return got_error_from_errno("pledge");
1705 if ((header
= gw_init_header()) == NULL
)
1706 return got_error_from_errno("malloc");
1708 if (gw_trans
->action
!= GW_SUMMARY
) {
1709 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1714 error
= gw_get_header(gw_trans
, header
, 1);
1718 if (gw_trans
->action
== GW_SUMMARY
) {
1719 gw_trans
->next_id
= NULL
;
1720 error
= gw_output_repo_tags(gw_trans
, header
,
1721 D_MAXSLCOMMDISP
, TAGBRIEF
);
1725 error
= gw_output_repo_tags(gw_trans
, header
,
1726 gw_trans
->gw_conf
->got_max_commits_display
, TAGBRIEF
);
1731 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1732 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1733 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1734 if (kerr
!= KCGI_OK
)
1736 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1737 KATTR_ID
, "nav_prev", KATTR__MAX
);
1738 if (kerr
!= KCGI_OK
)
1742 if (gw_trans
->prev_id
) {
1743 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1744 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1745 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1746 KATTRX_STRING
, "tags", "commit", KATTRX_STRING
,
1747 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1748 if (href_prev
== NULL
) {
1749 error
= got_error_from_errno("khttp_urlpartx");
1752 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1753 KATTR_HREF
, href_prev
, KATTR__MAX
);
1754 if (kerr
!= KCGI_OK
)
1756 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1757 if (kerr
!= KCGI_OK
)
1759 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1760 if (kerr
!= KCGI_OK
)
1764 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1765 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1766 if (kerr
!= KCGI_OK
)
1767 return gw_kcgi_error(kerr
);
1770 if (gw_trans
->next_id
) {
1771 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1772 KATTR_ID
, "nav_next", KATTR__MAX
);
1773 if (kerr
!= KCGI_OK
)
1775 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1776 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1777 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1778 KATTRX_STRING
, "tags", "commit", KATTRX_STRING
,
1779 gw_trans
->next_id
, NULL
);
1780 if (href_next
== NULL
) {
1781 error
= got_error_from_errno("khttp_urlpartx");
1784 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1785 KATTR_HREF
, href_next
, KATTR__MAX
);
1786 if (kerr
!= KCGI_OK
)
1788 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1789 if (kerr
!= KCGI_OK
)
1791 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1792 if (kerr
!= KCGI_OK
)
1796 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1797 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1798 if (kerr
!= KCGI_OK
)
1802 gw_free_header(header
);
1805 if (error
== NULL
&& kerr
!= KCGI_OK
)
1806 error
= gw_kcgi_error(kerr
);
1810 static const struct got_error
*
1811 gw_tag(struct gw_trans
*gw_trans
)
1813 const struct got_error
*error
= NULL
;
1814 struct gw_header
*header
= NULL
;
1815 enum kcgi_err kerr
= KCGI_OK
;
1818 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil", NULL
) == -1)
1819 return got_error_from_errno("pledge");
1821 if ((header
= gw_init_header()) == NULL
)
1822 return got_error_from_errno("malloc");
1824 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1828 if (gw_trans
->commit_id
== NULL
) {
1829 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
1830 "commit required in querystring");
1834 error
= gw_get_header(gw_trans
, header
, 1);
1838 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1839 "tag_header_wrapper", KATTR__MAX
);
1840 if (kerr
!= KCGI_OK
)
1842 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1843 "tag_header", KATTR__MAX
);
1844 if (kerr
!= KCGI_OK
)
1846 error
= gw_gen_commit_header(gw_trans
, header
->commit_id
,
1850 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
1853 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1854 if (kerr
!= KCGI_OK
)
1856 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1857 "dotted_line", KATTR__MAX
);
1858 if (kerr
!= KCGI_OK
)
1860 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1861 if (kerr
!= KCGI_OK
)
1864 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1865 "tree", KATTR__MAX
);
1866 if (kerr
!= KCGI_OK
)
1869 error
= gw_output_repo_tags(gw_trans
, header
, 1, TAGFULL
);
1873 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1875 gw_free_header(header
);
1876 if (error
== NULL
&& kerr
!= KCGI_OK
)
1877 error
= gw_kcgi_error(kerr
);
1881 static const struct got_error
*
1882 gw_load_got_path(struct gw_trans
*gw_trans
, struct gw_dir
*gw_dir
)
1884 const struct got_error
*error
= NULL
;
1889 if (asprintf(&dir_test
, "%s/%s/%s",
1890 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
,
1891 GOTWEB_GIT_DIR
) == -1)
1892 return got_error_from_errno("asprintf");
1894 dt
= opendir(dir_test
);
1898 gw_dir
->path
= strdup(dir_test
);
1899 if (gw_dir
->path
== NULL
) {
1901 error
= got_error_from_errno("strdup");
1908 if (asprintf(&dir_test
, "%s/%s/%s",
1909 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
,
1910 GOTWEB_GOT_DIR
) == -1) {
1912 error
= got_error_from_errno("asprintf");
1916 dt
= opendir(dir_test
);
1921 error
= got_error(GOT_ERR_NOT_GIT_REPO
);
1925 if (asprintf(&dir_test
, "%s/%s",
1926 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
) == -1) {
1927 error
= got_error_from_errno("asprintf");
1932 gw_dir
->path
= strdup(dir_test
);
1933 if (gw_dir
->path
== NULL
) {
1935 error
= got_error_from_errno("strdup");
1939 dt
= opendir(dir_test
);
1941 error
= got_error_path(gw_dir
->name
, GOT_ERR_NOT_GIT_REPO
);
1946 error
= gw_get_repo_description(&gw_dir
->description
, gw_trans
,
1950 error
= gw_get_repo_owner(&gw_dir
->owner
, gw_trans
, gw_dir
->path
);
1953 error
= gw_get_repo_age(&gw_dir
->age
, gw_trans
, gw_dir
->path
,
1957 error
= gw_get_clone_url(&gw_dir
->url
, gw_trans
, gw_dir
->path
);
1961 if (dt
&& closedir(dt
) == -1 && error
== NULL
)
1962 error
= got_error_from_errno("closedir");
1966 static const struct got_error
*
1967 gw_load_got_paths(struct gw_trans
*gw_trans
)
1969 const struct got_error
*error
= NULL
;
1971 struct dirent
**sd_dent
;
1972 struct gw_dir
*gw_dir
;
1974 unsigned int d_cnt
, d_i
;
1976 d
= opendir(gw_trans
->gw_conf
->got_repos_path
);
1978 error
= got_error_from_errno2("opendir",
1979 gw_trans
->gw_conf
->got_repos_path
);
1983 d_cnt
= scandir(gw_trans
->gw_conf
->got_repos_path
, &sd_dent
, NULL
,
1986 error
= got_error_from_errno2("scandir",
1987 gw_trans
->gw_conf
->got_repos_path
);
1991 for (d_i
= 0; d_i
< d_cnt
; d_i
++) {
1992 if (gw_trans
->gw_conf
->got_max_repos
> 0 &&
1993 (d_i
- 2) == gw_trans
->gw_conf
->got_max_repos
)
1994 break; /* account for parent and self */
1996 if (strcmp(sd_dent
[d_i
]->d_name
, ".") == 0 ||
1997 strcmp(sd_dent
[d_i
]->d_name
, "..") == 0)
2000 error
= gw_init_gw_dir(&gw_dir
, sd_dent
[d_i
]->d_name
);
2004 error
= gw_load_got_path(gw_trans
, gw_dir
);
2005 if (error
&& error
->code
== GOT_ERR_NOT_GIT_REPO
) {
2008 } else if (error
&& error
->code
!= GOT_ERR_LONELY_PACKIDX
)
2011 if (lstat(gw_dir
->path
, &st
) == 0 && S_ISDIR(st
.st_mode
) &&
2012 !got_path_dir_is_empty(gw_dir
->path
)) {
2013 TAILQ_INSERT_TAIL(&gw_trans
->gw_dirs
, gw_dir
,
2015 gw_trans
->repos_total
++;
2019 if (d
&& closedir(d
) == -1 && error
== NULL
)
2020 error
= got_error_from_errno("closedir");
2024 static const struct got_error
*
2025 gw_parse_querystring(struct gw_trans
*gw_trans
)
2027 const struct got_error
*error
= NULL
;
2029 const struct gw_query_action
*action
= NULL
;
2032 if (gw_trans
->gw_req
->fieldnmap
[0]) {
2033 return got_error(GOT_ERR_QUERYSTRING
);
2034 } else if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PATH
])) {
2035 /* define gw_trans->repo_path */
2036 gw_trans
->repo_name
= p
->parsed
.s
;
2038 if (asprintf(&gw_trans
->repo_path
, "%s/%s",
2039 gw_trans
->gw_conf
->got_repos_path
, p
->parsed
.s
) == -1)
2040 return got_error_from_errno("asprintf");
2042 /* get action and set function */
2043 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_ACTION
])) {
2044 for (i
= 0; i
< nitems(gw_query_funcs
); i
++) {
2045 action
= &gw_query_funcs
[i
];
2046 if (action
->func_name
== NULL
)
2048 if (strcmp(action
->func_name
,
2049 p
->parsed
.s
) == 0) {
2050 gw_trans
->action
= i
;
2055 if (gw_trans
->action
== -1) {
2056 gw_trans
->action
= GW_ERR
;
2057 gw_trans
->error
= got_error_msg(GOT_ERR_QUERYSTRING
,
2058 p
!= NULL
? "bad action in querystring" :
2059 "no action in querystring");
2063 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_COMMIT_ID
])) {
2064 if (asprintf(&gw_trans
->commit_id
, "%s",
2066 return got_error_from_errno("asprintf");
2069 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_FILE
]))
2070 gw_trans
->repo_file
= p
->parsed
.s
;
2072 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_FOLDER
])) {
2073 if (asprintf(&gw_trans
->repo_folder
, "%s",
2075 return got_error_from_errno("asprintf");
2078 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PREV_ID
])) {
2079 if (asprintf(&gw_trans
->prev_id
, "%s",
2081 return got_error_from_errno("asprintf");
2084 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_HEADREF
]))
2085 gw_trans
->headref
= p
->parsed
.s
;
2087 error
= gw_init_gw_dir(&gw_trans
->gw_dir
, gw_trans
->repo_name
);
2091 gw_trans
->error
= gw_load_got_path(gw_trans
, gw_trans
->gw_dir
);
2093 gw_trans
->action
= GW_INDEX
;
2095 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PAGE
]))
2096 gw_trans
->page
= p
->parsed
.i
;
2101 static const struct got_error
*
2102 gw_init_gw_dir(struct gw_dir
**gw_dir
, const char *dir
)
2104 const struct got_error
*error
;
2106 *gw_dir
= malloc(sizeof(**gw_dir
));
2107 if (*gw_dir
== NULL
)
2108 return got_error_from_errno("malloc");
2110 if (asprintf(&(*gw_dir
)->name
, "%s", dir
) == -1) {
2111 error
= got_error_from_errno("asprintf");
2120 static const struct got_error
*
2121 gw_display_open(struct gw_trans
*gw_trans
, enum khttp code
, enum kmime mime
)
2123 enum kcgi_err kerr
= KCGI_OK
;
2125 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_ALLOW
], "GET");
2126 if (kerr
!= KCGI_OK
)
2127 return gw_kcgi_error(kerr
);
2128 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_STATUS
], "%s",
2130 if (kerr
!= KCGI_OK
)
2131 return gw_kcgi_error(kerr
);
2132 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_CONTENT_TYPE
], "%s",
2134 if (kerr
!= KCGI_OK
)
2135 return gw_kcgi_error(kerr
);
2136 kerr
= khttp_head(gw_trans
->gw_req
, "X-Content-Type-Options",
2138 if (kerr
!= KCGI_OK
)
2139 return gw_kcgi_error(kerr
);
2140 kerr
= khttp_head(gw_trans
->gw_req
, "X-Frame-Options", "DENY");
2141 if (kerr
!= KCGI_OK
)
2142 return gw_kcgi_error(kerr
);
2143 kerr
= khttp_head(gw_trans
->gw_req
, "X-XSS-Protection",
2145 if (kerr
!= KCGI_OK
)
2146 return gw_kcgi_error(kerr
);
2148 if (gw_trans
->mime
== KMIME_APP_OCTET_STREAM
) {
2149 kerr
= khttp_head(gw_trans
->gw_req
,
2150 kresps
[KRESP_CONTENT_DISPOSITION
],
2151 "attachment; filename=%s", gw_trans
->repo_file
);
2152 if (kerr
!= KCGI_OK
)
2153 return gw_kcgi_error(kerr
);
2156 kerr
= khttp_body(gw_trans
->gw_req
);
2157 return gw_kcgi_error(kerr
);
2160 static const struct got_error
*
2161 gw_display_index(struct gw_trans
*gw_trans
)
2163 const struct got_error
*error
;
2164 enum kcgi_err kerr
= KCGI_OK
;
2166 /* catch early querystring errors */
2167 if (gw_trans
->error
)
2168 gw_trans
->action
= GW_ERR
;
2170 error
= gw_display_open(gw_trans
, KHTTP_200
, gw_trans
->mime
);
2174 kerr
= khtml_open(gw_trans
->gw_html_req
, gw_trans
->gw_req
, 0);
2175 if (kerr
!= KCGI_OK
)
2176 return gw_kcgi_error(kerr
);
2178 if (gw_trans
->action
!= GW_BLOB
) {
2179 kerr
= khttp_template(gw_trans
->gw_req
, gw_trans
->gw_tmpl
,
2180 gw_query_funcs
[gw_trans
->action
].template);
2181 if (kerr
!= KCGI_OK
) {
2182 khtml_close(gw_trans
->gw_html_req
);
2183 return gw_kcgi_error(kerr
);
2187 return gw_kcgi_error(khtml_close(gw_trans
->gw_html_req
));
2190 static const struct got_error
*
2191 gw_error(struct gw_trans
*gw_trans
)
2193 enum kcgi_err kerr
= KCGI_OK
;
2195 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->error
->msg
);
2197 return gw_kcgi_error(kerr
);
2201 gw_template(size_t key
, void *arg
)
2203 const struct got_error
*error
= NULL
;
2204 enum kcgi_err kerr
= KCGI_OK
;
2205 struct gw_trans
*gw_trans
= arg
;
2206 char *ati
= NULL
, *fic32
= NULL
, *fic16
= NULL
;
2207 char *swm
= NULL
, *spt
= NULL
, *css
= NULL
, *logo
= NULL
;
2209 if (asprintf(&ati
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2210 "/apple-touch-icon.png") == -1)
2212 if (asprintf(&fic32
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2213 "/favicon-32x32.png") == -1)
2215 if (asprintf(&fic16
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2216 "/favicon-16x16.png") == -1)
2218 if (asprintf(&swm
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2219 "/site.webmanifest") == -1)
2221 if (asprintf(&spt
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2222 "/safari-pinned-tab.svg") == -1)
2224 if (asprintf(&css
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2225 "/gotweb.css") == -1)
2227 if (asprintf(&logo
, "%s%s%s", gw_trans
->gw_conf
->got_www_path
,
2228 gw_trans
->gw_conf
->got_www_path
? "/" : "",
2229 gw_trans
->gw_conf
->got_logo
) == -1)
2234 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2235 KATTR_NAME
, "viewport",
2236 KATTR_CONTENT
, "initial-scale=.75, user-scalable=yes",
2238 if (kerr
!= KCGI_OK
)
2240 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2241 if (kerr
!= KCGI_OK
)
2243 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2244 KATTR_CHARSET
, "utf-8",
2246 if (kerr
!= KCGI_OK
)
2248 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2249 if (kerr
!= KCGI_OK
)
2251 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2252 KATTR_NAME
, "msapplication-TileColor",
2253 KATTR_CONTENT
, "#da532c", KATTR__MAX
);
2254 if (kerr
!= KCGI_OK
)
2256 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2257 if (kerr
!= KCGI_OK
)
2259 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2260 KATTR_NAME
, "theme-color",
2261 KATTR_CONTENT
, "#ffffff", KATTR__MAX
);
2262 if (kerr
!= KCGI_OK
)
2264 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2265 if (kerr
!= KCGI_OK
)
2267 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2268 KATTR_REL
, "apple-touch-icon", KATTR_SIZES
, "180x180",
2269 KATTR_HREF
, ati
, KATTR__MAX
);
2270 if (kerr
!= KCGI_OK
)
2272 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2273 if (kerr
!= KCGI_OK
)
2275 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2276 KATTR_REL
, "icon", KATTR_TYPE
, "image/png", KATTR_SIZES
,
2277 "32x32", KATTR_HREF
, fic32
, KATTR__MAX
);
2278 if (kerr
!= KCGI_OK
)
2280 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2281 if (kerr
!= KCGI_OK
)
2283 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2284 KATTR_REL
, "icon", KATTR_TYPE
, "image/png", KATTR_SIZES
,
2285 "16x16", KATTR_HREF
, fic16
, KATTR__MAX
);
2286 if (kerr
!= KCGI_OK
)
2288 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2289 if (kerr
!= KCGI_OK
)
2291 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2292 KATTR_REL
, "manifest", KATTR_HREF
, swm
,
2294 if (kerr
!= KCGI_OK
)
2296 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2297 if (kerr
!= KCGI_OK
)
2299 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2300 KATTR_REL
, "mask-icon", KATTR_HREF
,
2302 if (kerr
!= KCGI_OK
)
2304 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2305 if (kerr
!= KCGI_OK
)
2307 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2308 KATTR_REL
, "stylesheet", KATTR_TYPE
, "text/css",
2309 KATTR_HREF
, css
, KATTR__MAX
);
2310 if (kerr
!= KCGI_OK
)
2312 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2313 if (kerr
!= KCGI_OK
)
2317 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2318 KATTR_ID
, "got_link", KATTR__MAX
);
2319 if (kerr
!= KCGI_OK
)
2321 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
2322 KATTR_HREF
, gw_trans
->gw_conf
->got_logo_url
,
2323 KATTR_TARGET
, "_sotd", KATTR__MAX
);
2324 if (kerr
!= KCGI_OK
)
2326 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_IMG
,
2327 KATTR_SRC
, logo
, KATTR__MAX
);
2328 if (kerr
!= KCGI_OK
)
2330 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
2331 if (kerr
!= KCGI_OK
)
2334 case (TEMPL_SITEPATH
):
2335 error
= gw_output_site_link(gw_trans
);
2340 if (gw_trans
->gw_conf
->got_site_name
!= NULL
) {
2341 kerr
= khtml_puts(gw_trans
->gw_html_req
,
2342 gw_trans
->gw_conf
->got_site_name
);
2343 if (kerr
!= KCGI_OK
)
2347 case (TEMPL_SEARCH
):
2349 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
2350 "search", KATTR__MAX
);
2351 if (kerr
!= KCGI_OK
)
2353 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_FORM
,
2354 KATTR_METHOD
, "POST", KATTR__MAX
);
2355 if (kerr
!= KCGI_OK
)
2357 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_INPUT
, KATTR_ID
,
2358 "got-search", KATTR_NAME
, "got-search", KATTR_SIZE
, "15",
2359 KATTR_MAXLENGTH
, "50", KATTR__MAX
);
2360 if (kerr
!= KCGI_OK
)
2362 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_BUTTON
,
2364 if (kerr
!= KCGI_OK
)
2366 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Search");
2367 if (kerr
!= KCGI_OK
)
2369 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 4);
2370 if (kerr
!= KCGI_OK
)
2373 case(TEMPL_SITEOWNER
):
2374 if (gw_trans
->gw_conf
->got_site_owner
!= NULL
&&
2375 gw_trans
->gw_conf
->got_show_site_owner
) {
2376 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2377 KATTR_ID
, "site_owner_wrapper", KATTR__MAX
);
2378 if (kerr
!= KCGI_OK
)
2380 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2381 KATTR_ID
, "site_owner", KATTR__MAX
);
2382 if (kerr
!= KCGI_OK
)
2384 kerr
= khtml_puts(gw_trans
->gw_html_req
,
2385 gw_trans
->gw_conf
->got_site_owner
);
2386 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
2387 if (kerr
!= KCGI_OK
)
2391 case(TEMPL_CONTENT
):
2392 error
= gw_query_funcs
[gw_trans
->action
].func_main(gw_trans
);
2394 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2395 KATTR_ID
, "tmpl_err", KATTR__MAX
);
2396 if (kerr
!= KCGI_OK
)
2398 kerr
= khttp_printf(gw_trans
->gw_req
, "Error: %s",
2400 if (kerr
!= KCGI_OK
)
2402 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2403 if (kerr
!= KCGI_OK
)
2429 static const struct got_error
*
2430 gw_gen_commit_header(struct gw_trans
*gw_trans
, char *str1
, char *str2
)
2432 const struct got_error
*error
= NULL
;
2433 enum kcgi_err kerr
= KCGI_OK
;
2435 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2436 KATTR_ID
, "header_commit_title", KATTR__MAX
);
2437 if (kerr
!= KCGI_OK
)
2439 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Commit: ");
2440 if (kerr
!= KCGI_OK
)
2442 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2443 if (kerr
!= KCGI_OK
)
2445 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2446 KATTR_ID
, "header_commit", KATTR__MAX
);
2447 if (kerr
!= KCGI_OK
)
2449 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s ", str1
);
2450 if (kerr
!= KCGI_OK
)
2453 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_SPAN
,
2454 KATTR_ID
, "refs_str", KATTR__MAX
);
2455 if (kerr
!= KCGI_OK
)
2457 kerr
= khtml_printf(gw_trans
->gw_html_req
, "(%s)", str2
);
2458 if (kerr
!= KCGI_OK
)
2460 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2461 if (kerr
!= KCGI_OK
)
2464 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2466 if (error
== NULL
&& kerr
!= KCGI_OK
)
2467 error
= gw_kcgi_error(kerr
);
2471 static const struct got_error
*
2472 gw_gen_diff_header(struct gw_trans
*gw_trans
, char *str1
, char *str2
)
2474 const struct got_error
*error
= NULL
;
2475 enum kcgi_err kerr
= KCGI_OK
;
2477 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2478 KATTR_ID
, "header_diff_title", KATTR__MAX
);
2479 if (kerr
!= KCGI_OK
)
2481 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Diff: ");
2482 if (kerr
!= KCGI_OK
)
2484 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2485 if (kerr
!= KCGI_OK
)
2487 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2488 KATTR_ID
, "header_diff", KATTR__MAX
);
2489 if (kerr
!= KCGI_OK
)
2492 kerr
= khtml_puts(gw_trans
->gw_html_req
, str1
);
2493 if (kerr
!= KCGI_OK
)
2496 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_BR
, KATTR__MAX
);
2497 if (kerr
!= KCGI_OK
)
2499 kerr
= khtml_puts(gw_trans
->gw_html_req
, str2
);
2500 if (kerr
!= KCGI_OK
)
2502 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2504 if (error
== NULL
&& kerr
!= KCGI_OK
)
2505 error
= gw_kcgi_error(kerr
);
2509 static const struct got_error
*
2510 gw_gen_age_header(struct gw_trans
*gw_trans
, const char *str
)
2512 const struct got_error
*error
= NULL
;
2513 enum kcgi_err kerr
= KCGI_OK
;
2515 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2516 KATTR_ID
, "header_age_title", KATTR__MAX
);
2517 if (kerr
!= KCGI_OK
)
2519 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Date: ");
2520 if (kerr
!= KCGI_OK
)
2522 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2523 if (kerr
!= KCGI_OK
)
2525 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2526 KATTR_ID
, "header_age", KATTR__MAX
);
2527 if (kerr
!= KCGI_OK
)
2529 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2530 if (kerr
!= KCGI_OK
)
2532 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2534 if (error
== NULL
&& kerr
!= KCGI_OK
)
2535 error
= gw_kcgi_error(kerr
);
2539 static const struct got_error
*
2540 gw_gen_author_header(struct gw_trans
*gw_trans
, const char *str
)
2542 const struct got_error
*error
= NULL
;
2543 enum kcgi_err kerr
= KCGI_OK
;
2545 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2546 KATTR_ID
, "header_author_title", KATTR__MAX
);
2547 if (kerr
!= KCGI_OK
)
2549 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Author: ");
2550 if (kerr
!= KCGI_OK
)
2552 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2553 if (kerr
!= KCGI_OK
)
2555 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2556 KATTR_ID
, "header_author", KATTR__MAX
);
2557 if (kerr
!= KCGI_OK
)
2559 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2560 if (kerr
!= KCGI_OK
)
2562 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2564 if (error
== NULL
&& kerr
!= KCGI_OK
)
2565 error
= gw_kcgi_error(kerr
);
2569 static const struct got_error
*
2570 gw_gen_committer_header(struct gw_trans
*gw_trans
, const char *str
)
2572 const struct got_error
*error
= NULL
;
2573 enum kcgi_err kerr
= KCGI_OK
;
2575 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2576 KATTR_ID
, "header_committer_title", KATTR__MAX
);
2577 if (kerr
!= KCGI_OK
)
2579 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Committer: ");
2580 if (kerr
!= KCGI_OK
)
2582 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2583 if (kerr
!= KCGI_OK
)
2585 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2586 KATTR_ID
, "header_committer", KATTR__MAX
);
2587 if (kerr
!= KCGI_OK
)
2589 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2590 if (kerr
!= KCGI_OK
)
2592 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2594 if (error
== NULL
&& kerr
!= KCGI_OK
)
2595 error
= gw_kcgi_error(kerr
);
2599 static const struct got_error
*
2600 gw_gen_commit_msg_header(struct gw_trans
*gw_trans
, char *str
)
2602 const struct got_error
*error
= NULL
;
2603 enum kcgi_err kerr
= KCGI_OK
;
2605 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2606 KATTR_ID
, "header_commit_msg_title", KATTR__MAX
);
2607 if (kerr
!= KCGI_OK
)
2609 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Message: ");
2610 if (kerr
!= KCGI_OK
)
2612 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2613 if (kerr
!= KCGI_OK
)
2615 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2616 KATTR_ID
, "header_commit_msg", KATTR__MAX
);
2617 if (kerr
!= KCGI_OK
)
2619 kerr
= khttp_puts(gw_trans
->gw_req
, str
);
2620 if (kerr
!= KCGI_OK
)
2622 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2624 if (error
== NULL
&& kerr
!= KCGI_OK
)
2625 error
= gw_kcgi_error(kerr
);
2629 static const struct got_error
*
2630 gw_gen_tree_header(struct gw_trans
*gw_trans
, char *str
)
2632 const struct got_error
*error
= NULL
;
2633 enum kcgi_err kerr
= KCGI_OK
;
2635 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2636 KATTR_ID
, "header_tree_title", KATTR__MAX
);
2637 if (kerr
!= KCGI_OK
)
2639 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tree: ");
2640 if (kerr
!= KCGI_OK
)
2642 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2643 if (kerr
!= KCGI_OK
)
2645 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2646 KATTR_ID
, "header_tree", KATTR__MAX
);
2647 if (kerr
!= KCGI_OK
)
2649 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2650 if (kerr
!= KCGI_OK
)
2652 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2654 if (error
== NULL
&& kerr
!= KCGI_OK
)
2655 error
= gw_kcgi_error(kerr
);
2659 static const struct got_error
*
2660 gw_get_repo_description(char **description
, struct gw_trans
*gw_trans
,
2663 const struct got_error
*error
= NULL
;
2665 char *d_file
= NULL
;
2669 *description
= NULL
;
2670 if (gw_trans
->gw_conf
->got_show_repo_description
== 0)
2673 if (asprintf(&d_file
, "%s/description", dir
) == -1)
2674 return got_error_from_errno("asprintf");
2676 f
= fopen(d_file
, "re");
2678 if (errno
== ENOENT
|| errno
== EACCES
)
2680 error
= got_error_from_errno2("fopen", d_file
);
2684 if (fseek(f
, 0, SEEK_END
) == -1) {
2685 error
= got_ferror(f
, GOT_ERR_IO
);
2690 error
= got_ferror(f
, GOT_ERR_IO
);
2693 if (fseek(f
, 0, SEEK_SET
) == -1) {
2694 error
= got_ferror(f
, GOT_ERR_IO
);
2697 *description
= calloc(len
+ 1, sizeof(**description
));
2698 if (*description
== NULL
) {
2699 error
= got_error_from_errno("calloc");
2703 n
= fread(*description
, 1, len
, f
);
2704 if (n
== 0 && ferror(f
))
2705 error
= got_ferror(f
, GOT_ERR_IO
);
2707 if (f
!= NULL
&& fclose(f
) == EOF
&& error
== NULL
)
2708 error
= got_error_from_errno("fclose");
2713 static const struct got_error
*
2714 gw_get_time_str(char **repo_age
, time_t committer_time
, int ref_tm
)
2718 const char *years
= "years ago", *months
= "months ago";
2719 const char *weeks
= "weeks ago", *days
= "days ago", *hours
= "hours ago";
2720 const char *minutes
= "minutes ago", *seconds
= "seconds ago";
2721 const char *now
= "right now";
2729 diff_time
= time(NULL
) - committer_time
;
2730 if (diff_time
> 60 * 60 * 24 * 365 * 2) {
2731 if (asprintf(repo_age
, "%lld %s",
2732 (diff_time
/ 60 / 60 / 24 / 365), years
) == -1)
2733 return got_error_from_errno("asprintf");
2734 } else if (diff_time
> 60 * 60 * 24 * (365 / 12) * 2) {
2735 if (asprintf(repo_age
, "%lld %s",
2736 (diff_time
/ 60 / 60 / 24 / (365 / 12)),
2738 return got_error_from_errno("asprintf");
2739 } else if (diff_time
> 60 * 60 * 24 * 7 * 2) {
2740 if (asprintf(repo_age
, "%lld %s",
2741 (diff_time
/ 60 / 60 / 24 / 7), weeks
) == -1)
2742 return got_error_from_errno("asprintf");
2743 } else if (diff_time
> 60 * 60 * 24 * 2) {
2744 if (asprintf(repo_age
, "%lld %s",
2745 (diff_time
/ 60 / 60 / 24), days
) == -1)
2746 return got_error_from_errno("asprintf");
2747 } else if (diff_time
> 60 * 60 * 2) {
2748 if (asprintf(repo_age
, "%lld %s",
2749 (diff_time
/ 60 / 60), hours
) == -1)
2750 return got_error_from_errno("asprintf");
2751 } else if (diff_time
> 60 * 2) {
2752 if (asprintf(repo_age
, "%lld %s", (diff_time
/ 60),
2754 return got_error_from_errno("asprintf");
2755 } else if (diff_time
> 2) {
2756 if (asprintf(repo_age
, "%lld %s", diff_time
,
2758 return got_error_from_errno("asprintf");
2760 if (asprintf(repo_age
, "%s", now
) == -1)
2761 return got_error_from_errno("asprintf");
2765 if (gmtime_r(&committer_time
, &tm
) == NULL
)
2766 return got_error_from_errno("gmtime_r");
2768 s
= asctime_r(&tm
, datebuf
);
2770 return got_error_from_errno("asctime_r");
2772 if (asprintf(repo_age
, "%s UTC", datebuf
) == -1)
2773 return got_error_from_errno("asprintf");
2779 static const struct got_error
*
2780 gw_get_repo_age(char **repo_age
, struct gw_trans
*gw_trans
, char *dir
,
2781 const char *refname
, int ref_tm
)
2783 const struct got_error
*error
= NULL
;
2784 struct got_repository
*repo
= NULL
;
2785 struct got_commit_object
*commit
= NULL
;
2786 struct got_reflist_head refs
;
2787 struct got_reflist_entry
*re
;
2788 time_t committer_time
= 0, cmp_time
= 0;
2793 if (gw_trans
->gw_conf
->got_show_repo_age
== 0)
2797 repo
= gw_trans
->repo
;
2799 error
= got_repo_open(&repo
, dir
, NULL
, gw_trans
->pack_fds
);
2804 error
= got_ref_list(&refs
, repo
, "refs/heads",
2805 got_ref_cmp_by_name
, NULL
);
2810 * Find the youngest branch tip in the repository, or the age of
2811 * the a specific branch tip if a name was provided by the caller.
2813 TAILQ_FOREACH(re
, &refs
, entry
) {
2814 struct got_object_id
*id
= NULL
;
2816 if (refname
&& strcmp(got_ref_get_name(re
->ref
), refname
) != 0)
2819 error
= got_ref_resolve(&id
, repo
, re
->ref
);
2823 error
= got_object_open_as_commit(&commit
, repo
, id
);
2829 got_object_commit_get_committer_time(commit
);
2830 got_object_commit_close(commit
);
2831 if (cmp_time
< committer_time
)
2832 cmp_time
= committer_time
;
2838 if (cmp_time
!= 0) {
2839 committer_time
= cmp_time
;
2840 error
= gw_get_time_str(repo_age
, committer_time
, ref_tm
);
2843 got_ref_list_free(&refs
);
2844 if (gw_trans
->repo
== NULL
) {
2845 const struct got_error
*close_err
= got_repo_close(repo
);
2852 static const struct got_error
*
2853 gw_output_diff(struct gw_trans
*gw_trans
, struct gw_header
*header
)
2855 const struct got_error
*error
;
2856 FILE *f
= NULL
, *f1
= NULL
, *f2
= NULL
;
2857 int fd1
= -1, fd2
= -1;
2858 struct got_object_id
*id1
= NULL
, *id2
= NULL
;
2859 char *label1
= NULL
, *label2
= NULL
, *line
= NULL
;
2861 size_t linesize
= 0;
2863 enum kcgi_err kerr
= KCGI_OK
;
2869 f1
= got_opentemp();
2871 error
= got_error_from_errno("got_opentemp");
2875 f2
= got_opentemp();
2877 error
= got_error_from_errno("got_opentemp");
2881 fd1
= got_opentempfd();
2883 error
= got_error_from_errno("got_opentempfd");
2887 fd2
= got_opentempfd();
2889 error
= got_error_from_errno("got_opentempfd");
2893 if (header
->parent_id
!= NULL
&&
2894 strncmp(header
->parent_id
, "/dev/null", 9) != 0) {
2895 error
= got_repo_match_object_id(&id1
, &label1
,
2896 header
->parent_id
, GOT_OBJ_TYPE_ANY
,
2897 &header
->refs
, gw_trans
->repo
);
2902 error
= got_repo_match_object_id(&id2
, &label2
,
2903 header
->commit_id
, GOT_OBJ_TYPE_ANY
, &header
->refs
,
2908 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, id2
);
2912 case GOT_OBJ_TYPE_BLOB
:
2913 error
= got_diff_objects_as_blobs(NULL
, NULL
, f1
, f2
,
2914 fd1
, fd2
, id1
, id2
, NULL
, NULL
, GOT_DIFF_ALGORITHM_PATIENCE
,
2915 3, 0, 0, gw_trans
->repo
, f
);
2917 case GOT_OBJ_TYPE_TREE
:
2918 error
= got_diff_objects_as_trees(NULL
, NULL
, f1
, f2
,
2919 fd1
, fd2
, id1
, id2
, NULL
, "", "",
2920 GOT_DIFF_ALGORITHM_PATIENCE
, 3, 0, 0, gw_trans
->repo
, f
);
2922 case GOT_OBJ_TYPE_COMMIT
:
2923 error
= got_diff_objects_as_commits(NULL
, NULL
, f1
, f2
,
2924 fd1
, fd2
, id1
, id2
, NULL
, GOT_DIFF_ALGORITHM_PATIENCE
,
2925 3, 0, 0, gw_trans
->repo
, f
);
2928 error
= got_error(GOT_ERR_OBJ_TYPE
);
2933 if (fseek(f
, 0, SEEK_SET
) == -1) {
2934 error
= got_ferror(f
, GOT_ERR_IO
);
2938 while ((linelen
= getline(&line
, &linesize
, f
)) != -1) {
2939 error
= gw_colordiff_line(gw_trans
, line
);
2942 /* XXX: KHTML_PRETTY breaks this */
2943 kerr
= khtml_puts(gw_trans
->gw_html_req
, line
);
2944 if (kerr
!= KCGI_OK
)
2946 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2947 if (kerr
!= KCGI_OK
)
2950 if (linelen
== -1 && ferror(f
))
2951 error
= got_error_from_errno("getline");
2953 if (f
&& fclose(f
) == EOF
&& error
== NULL
)
2954 error
= got_error_from_errno("fclose");
2955 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
2956 error
= got_error_from_errno("fclose");
2957 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
2958 error
= got_error_from_errno("fclose");
2959 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
2960 error
= got_error_from_errno("close");
2961 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
2962 error
= got_error_from_errno("close");
2969 if (error
== NULL
&& kerr
!= KCGI_OK
)
2970 error
= gw_kcgi_error(kerr
);
2974 static const struct got_error
*
2975 gw_get_repo_owner(char **owner
, struct gw_trans
*gw_trans
, char *dir
)
2977 const struct got_error
*error
= NULL
, *close_err
;
2978 struct got_repository
*repo
;
2979 const char *gitconfig_owner
;
2983 if (gw_trans
->gw_conf
->got_show_repo_owner
== 0)
2986 error
= got_repo_open(&repo
, dir
, NULL
, gw_trans
->pack_fds
);
2990 gitconfig_owner
= got_repo_get_gitconfig_owner(repo
);
2991 if (gitconfig_owner
) {
2992 *owner
= strdup(gitconfig_owner
);
2994 error
= got_error_from_errno("strdup");
2996 close_err
= got_repo_close(repo
);
3002 static const struct got_error
*
3003 gw_get_clone_url(char **url
, struct gw_trans
*gw_trans
, char *dir
)
3005 const struct got_error
*error
= NULL
;
3007 char *d_file
= NULL
;
3013 if (asprintf(&d_file
, "%s/cloneurl", dir
) == -1)
3014 return got_error_from_errno("asprintf");
3016 f
= fopen(d_file
, "re");
3018 if (errno
!= ENOENT
&& errno
!= EACCES
)
3019 error
= got_error_from_errno2("fopen", d_file
);
3023 if (fseek(f
, 0, SEEK_END
) == -1) {
3024 error
= got_ferror(f
, GOT_ERR_IO
);
3029 error
= got_ferror(f
, GOT_ERR_IO
);
3032 if (fseek(f
, 0, SEEK_SET
) == -1) {
3033 error
= got_ferror(f
, GOT_ERR_IO
);
3037 *url
= calloc(len
+ 1, sizeof(**url
));
3039 error
= got_error_from_errno("calloc");
3043 n
= fread(*url
, 1, len
, f
);
3044 if (n
== 0 && ferror(f
))
3045 error
= got_ferror(f
, GOT_ERR_IO
);
3047 if (f
&& fclose(f
) == EOF
&& error
== NULL
)
3048 error
= got_error_from_errno("fclose");
3053 static const struct got_error
*
3054 gw_output_repo_tags(struct gw_trans
*gw_trans
, struct gw_header
*header
,
3055 int limit
, int tag_type
)
3057 const struct got_error
*error
= NULL
;
3058 struct got_reflist_head refs
;
3059 struct got_reflist_entry
*re
;
3061 char *id_str
= NULL
, *newline
, *href_commits
= NULL
;
3062 char *tag_commit0
= NULL
, *href_tag
= NULL
, *href_briefs
= NULL
;
3063 struct got_tag_object
*tag
= NULL
;
3064 enum kcgi_err kerr
= KCGI_OK
;
3065 int summary_header_displayed
= 0, chk_next
= 0;
3066 int tag_count
= 0, commit_found
= 0, c_cnt
= 0;
3070 error
= got_ref_list(&refs
, gw_trans
->repo
, "refs/tags",
3071 got_ref_cmp_tags
, gw_trans
->repo
);
3075 TAILQ_FOREACH(re
, &refs
, entry
) {
3076 const char *refname
;
3078 const char *tag_commit
;
3080 struct got_object_id
*id
;
3081 struct got_commit_object
*commit
= NULL
;
3083 refname
= got_ref_get_name(re
->ref
);
3084 if (strncmp(refname
, "refs/tags/", 10) != 0)
3088 error
= got_ref_resolve(&id
, gw_trans
->repo
, re
->ref
);
3092 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
, id
);
3094 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3098 /* "lightweight" tag */
3099 error
= got_object_open_as_commit(&commit
,
3100 gw_trans
->repo
, id
);
3105 tagger
= got_object_commit_get_committer(commit
);
3107 got_object_commit_get_committer_time(commit
);
3108 error
= got_object_id_str(&id_str
, id
);
3112 tagger
= got_object_tag_get_tagger(tag
);
3113 tagger_time
= got_object_tag_get_tagger_time(tag
);
3114 error
= got_object_id_str(&id_str
,
3115 got_object_tag_get_object_id(tag
));
3120 if (tag_type
== TAGFULL
&& strncmp(id_str
, header
->commit_id
,
3121 strlen(id_str
)) != 0)
3124 if (tag_type
== TAGBRIEF
&& gw_trans
->commit_id
&&
3125 commit_found
== 0 && strncmp(id_str
, gw_trans
->commit_id
,
3126 strlen(id_str
)) != 0)
3134 gw_trans
->next_id
= strdup(id_str
);
3135 if (gw_trans
->next_id
== NULL
)
3136 error
= got_error_from_errno("strdup");
3141 error
= got_object_commit_get_logmsg(&tag_commit0
,
3145 got_object_commit_close(commit
);
3147 tag_commit0
= strdup(got_object_tag_get_message(tag
));
3148 if (tag_commit0
== NULL
) {
3149 error
= got_error_from_errno("strdup");
3154 tag_commit
= tag_commit0
;
3155 while (*tag_commit
== '\n')
3160 newline
= strchr(tag_commit
, '\n');
3164 if (summary_header_displayed
== 0) {
3165 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3166 KELEM_DIV
, KATTR_ID
,
3167 "summary_tags_title_wrapper", KATTR__MAX
);
3168 if (kerr
!= KCGI_OK
)
3170 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3171 KELEM_DIV
, KATTR_ID
,
3172 "summary_tags_title", KATTR__MAX
);
3173 if (kerr
!= KCGI_OK
)
3175 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3177 if (kerr
!= KCGI_OK
)
3179 kerr
= khtml_closeelem(gw_trans
->gw_html_req
,
3181 if (kerr
!= KCGI_OK
)
3183 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3184 KELEM_DIV
, KATTR_ID
,
3185 "summary_tags_content", KATTR__MAX
);
3186 if (kerr
!= KCGI_OK
)
3188 summary_header_displayed
= 1;
3191 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3192 KATTR_ID
, "tag_wrapper", KATTR__MAX
);
3193 if (kerr
!= KCGI_OK
)
3195 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3196 KATTR_ID
, "tag_age", KATTR__MAX
);
3197 if (kerr
!= KCGI_OK
)
3199 error
= gw_get_time_str(&age
, tagger_time
, TM_DIFF
);
3202 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3204 if (kerr
!= KCGI_OK
)
3206 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3207 if (kerr
!= KCGI_OK
)
3209 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3210 KATTR_ID
, "tag", KATTR__MAX
);
3211 if (kerr
!= KCGI_OK
)
3213 kerr
= khtml_puts(gw_trans
->gw_html_req
, refname
);
3214 if (kerr
!= KCGI_OK
)
3216 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3217 if (kerr
!= KCGI_OK
)
3219 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3220 KATTR_ID
, "tag_name", KATTR__MAX
);
3221 if (kerr
!= KCGI_OK
)
3224 href_tag
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
3225 gw_trans
->repo_name
, "action", "tag", "commit",
3227 if (href_tag
== NULL
) {
3228 error
= got_error_from_errno("khttp_urlpart");
3231 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3232 KATTR_HREF
, href_tag
, KATTR__MAX
);
3233 if (kerr
!= KCGI_OK
)
3235 kerr
= khtml_puts(gw_trans
->gw_html_req
, tag_commit
);
3236 if (kerr
!= KCGI_OK
)
3238 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
3239 if (kerr
!= KCGI_OK
)
3242 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3243 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
3244 if (kerr
!= KCGI_OK
)
3246 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3247 KATTR_ID
, "navs", KATTR__MAX
);
3248 if (kerr
!= KCGI_OK
)
3251 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3252 KATTR_HREF
, href_tag
, KATTR__MAX
);
3253 if (kerr
!= KCGI_OK
)
3255 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tag");
3256 if (kerr
!= KCGI_OK
)
3258 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3259 if (kerr
!= KCGI_OK
)
3262 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
3263 if (kerr
!= KCGI_OK
)
3266 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb",
3267 "path", gw_trans
->repo_name
, "action", "briefs",
3268 "commit", id_str
, NULL
);
3269 if (href_briefs
== NULL
) {
3270 error
= got_error_from_errno("khttp_urlpart");
3273 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3274 KATTR_HREF
, href_briefs
, KATTR__MAX
);
3275 if (kerr
!= KCGI_OK
)
3277 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3279 if (kerr
!= KCGI_OK
)
3281 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3282 if (kerr
!= KCGI_OK
)
3285 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
3286 if (kerr
!= KCGI_OK
)
3289 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb",
3290 "path", gw_trans
->repo_name
, "action", "commits",
3291 "commit", id_str
, NULL
);
3292 if (href_commits
== NULL
) {
3293 error
= got_error_from_errno("khttp_urlpart");
3296 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3297 KATTR_HREF
, href_commits
, KATTR__MAX
);
3298 if (kerr
!= KCGI_OK
)
3300 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
3301 if (kerr
!= KCGI_OK
)
3303 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
3304 if (kerr
!= KCGI_OK
)
3307 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3308 KATTR_ID
, "dotted_line", KATTR__MAX
);
3309 if (kerr
!= KCGI_OK
)
3311 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3312 if (kerr
!= KCGI_OK
)
3316 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3317 KATTR_ID
, "tag_info_date_title", KATTR__MAX
);
3318 if (kerr
!= KCGI_OK
)
3320 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tag Date:");
3321 if (kerr
!= KCGI_OK
)
3323 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3324 if (kerr
!= KCGI_OK
)
3326 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3327 KATTR_ID
, "tag_info_date", KATTR__MAX
);
3328 if (kerr
!= KCGI_OK
)
3330 error
= gw_get_time_str(&age
, tagger_time
, TM_LONG
);
3333 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3335 if (kerr
!= KCGI_OK
)
3337 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3338 if (kerr
!= KCGI_OK
)
3341 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3342 KATTR_ID
, "tag_info_tagger_title", KATTR__MAX
);
3343 if (kerr
!= KCGI_OK
)
3345 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tagger:");
3346 if (kerr
!= KCGI_OK
)
3348 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3349 if (kerr
!= KCGI_OK
)
3351 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3352 KATTR_ID
, "tag_info_date", KATTR__MAX
);
3353 if (kerr
!= KCGI_OK
)
3355 kerr
= khtml_puts(gw_trans
->gw_html_req
, tagger
);
3356 if (kerr
!= KCGI_OK
)
3358 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3359 if (kerr
!= KCGI_OK
)
3362 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3363 KATTR_ID
, "tag_info", KATTR__MAX
);
3364 if (kerr
!= KCGI_OK
)
3366 kerr
= khttp_puts(gw_trans
->gw_req
, tag_commit
);
3367 if (kerr
!= KCGI_OK
)
3373 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3374 if (kerr
!= KCGI_OK
)
3377 if (limit
&& --limit
== 0)
3381 got_object_tag_close(tag
);
3394 href_commits
= NULL
;
3396 if (tag_count
== 0) {
3397 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3398 "summary_tags_title_wrapper", KATTR__MAX
);
3399 if (kerr
!= KCGI_OK
)
3401 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3402 "summary_tags_title", KATTR__MAX
);
3403 if (kerr
!= KCGI_OK
)
3405 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tags");
3406 if (kerr
!= KCGI_OK
)
3408 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3409 if (kerr
!= KCGI_OK
)
3411 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3412 "summary_tags_content", KATTR__MAX
);
3413 if (kerr
!= KCGI_OK
)
3415 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3416 "tags_info", KATTR__MAX
);
3417 if (kerr
!= KCGI_OK
)
3419 kerr
= khttp_puts(gw_trans
->gw_req
,
3420 "There are no tags for this repo.");
3421 if (kerr
!= KCGI_OK
)
3423 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3428 TAILQ_FOREACH_REVERSE(re
, &refs
, got_reflist_head
, entry
) {
3429 const char *refname
;
3430 struct got_object_id
*id
;
3431 struct got_commit_object
*commit
= NULL
;
3433 refname
= got_ref_get_name(re
->ref
);
3434 if (strncmp(refname
, "refs/tags/", 10) != 0)
3438 error
= got_ref_resolve(&id
, gw_trans
->repo
, re
->ref
);
3442 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
, id
);
3444 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3448 /* "lightweight" tag */
3449 error
= got_object_open_as_commit(&commit
,
3450 gw_trans
->repo
, id
);
3455 error
= got_object_id_str(&id_str
, id
);
3459 error
= got_object_id_str(&id_str
,
3460 got_object_tag_get_object_id(tag
));
3465 if (tag_type
== TAGFULL
&& strncmp(id_str
, header
->commit_id
,
3466 strlen(id_str
)) != 0)
3469 if (commit_found
== 0 && tag_type
== TAGBRIEF
&&
3470 gw_trans
->commit_id
!= NULL
&&
3471 strncmp(id_str
, gw_trans
->commit_id
, strlen(id_str
)) != 0)
3476 if (gw_trans
->commit_id
!= NULL
&&
3477 strcmp(id_str
, gw_trans
->commit_id
) != 0 &&
3478 (re
== TAILQ_FIRST(&refs
) ||
3479 c_cnt
== gw_trans
->gw_conf
->got_max_commits_display
)) {
3480 gw_trans
->prev_id
= strdup(id_str
);
3481 if (gw_trans
->prev_id
== NULL
) {
3482 error
= got_error_from_errno("strdup");
3491 got_object_tag_close(tag
);
3498 got_ref_list_free(&refs
);
3499 if (error
== NULL
&& kerr
!= KCGI_OK
)
3500 error
= gw_kcgi_error(kerr
);
3505 gw_free_header(struct gw_header
*header
)
3508 free(header
->author
);
3509 free(header
->committer
);
3510 free(header
->refs_str
);
3511 free(header
->commit_id
);
3512 free(header
->parent_id
);
3513 free(header
->tree_id
);
3514 free(header
->commit_msg
);
3517 static struct gw_header
*
3520 struct gw_header
*header
;
3522 header
= malloc(sizeof(*header
));
3526 header
->path
= NULL
;
3527 TAILQ_INIT(&header
->refs
);
3529 header
->refs_str
= NULL
;
3530 header
->commit_id
= NULL
;
3531 header
->committer
= NULL
;
3532 header
->author
= NULL
;
3533 header
->parent_id
= NULL
;
3534 header
->tree_id
= NULL
;
3535 header
->commit_msg
= NULL
;
3540 static const struct got_error
*
3541 gw_get_commits(struct gw_trans
* gw_trans
, struct gw_header
*header
,
3542 int limit
, struct got_object_id
*id
)
3544 const struct got_error
*error
= NULL
;
3545 struct got_commit_graph
*graph
= NULL
;
3546 struct got_commit_object
*commit
= NULL
;
3547 int chk_next
= 0, chk_multi
= 0, c_cnt
= 0, commit_found
= 0;
3548 struct gw_header
*t_header
= NULL
;
3550 error
= got_commit_graph_open(&graph
, header
->path
, 0);
3554 error
= got_commit_graph_iter_start(graph
, id
, gw_trans
->repo
, NULL
,
3560 error
= got_commit_graph_iter_next(&id
, graph
, gw_trans
->repo
,
3563 if (error
->code
== GOT_ERR_ITER_COMPLETED
)
3570 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, id
);
3573 if (limit
== 1 && chk_multi
== 0 &&
3574 gw_trans
->gw_conf
->got_max_commits_display
!= 1) {
3575 error
= gw_get_commit(gw_trans
, header
, commit
, id
);
3581 struct gw_header
*n_header
= NULL
;
3582 if ((n_header
= gw_init_header()) == NULL
) {
3583 error
= got_error_from_errno("malloc");
3586 TAILQ_INSERT_TAIL(&gw_trans
->gw_headers
, n_header
,
3588 error
= got_ref_list(&n_header
->refs
, gw_trans
->repo
,
3589 NULL
, got_ref_cmp_by_name
, NULL
);
3593 error
= gw_get_commit(gw_trans
, n_header
, commit
, id
);
3596 got_ref_list_free(&n_header
->refs
);
3598 if (gw_trans
->commit_id
!= NULL
) {
3599 if (strcmp(gw_trans
->commit_id
,
3600 n_header
->commit_id
) == 0)
3606 * check for one more commit before breaking,
3607 * so we know whether to navigate through gw_briefs
3608 * gw_commits and gw_summary
3610 if (chk_next
&& (gw_trans
->action
== GW_BRIEFS
||
3611 gw_trans
->action
== GW_COMMITS
||
3612 gw_trans
->action
== GW_SUMMARY
)) {
3613 gw_trans
->next_id
= strdup(n_header
->commit_id
);
3614 if (gw_trans
->next_id
== NULL
)
3615 error
= got_error_from_errno("strdup");
3616 TAILQ_REMOVE(&gw_trans
->gw_headers
, n_header
,
3622 if (commit_found
== 1 && (error
|| (limit
&& --limit
== 0))) {
3629 if (gw_trans
->prev_id
== NULL
&& gw_trans
->commit_id
!= NULL
&&
3630 (gw_trans
->action
== GW_BRIEFS
|| gw_trans
->action
== GW_COMMITS
)) {
3632 TAILQ_FOREACH_REVERSE(t_header
, &gw_trans
->gw_headers
,
3634 if (commit_found
== 0 &&
3635 strcmp(gw_trans
->commit_id
,
3636 t_header
->commit_id
) != 0)
3640 if (gw_trans
->commit_id
!= NULL
&&
3641 strcmp(gw_trans
->commit_id
,
3642 t_header
->commit_id
) != 0 &&
3643 (c_cnt
== gw_trans
->gw_conf
->got_max_commits_display
3645 TAILQ_FIRST(&gw_trans
->gw_headers
))) {
3646 gw_trans
->prev_id
= strdup(t_header
->commit_id
);
3647 if (gw_trans
->prev_id
== NULL
)
3648 error
= got_error_from_errno("strdup");
3656 got_object_commit_close(commit
);
3658 got_commit_graph_close(graph
);
3662 static const struct got_error
*
3663 gw_get_commit(struct gw_trans
*gw_trans
, struct gw_header
*header
,
3664 struct got_commit_object
*commit
, struct got_object_id
*id
)
3666 const struct got_error
*error
= NULL
;
3667 struct got_reflist_entry
*re
;
3668 struct got_object_id
*id2
= NULL
;
3669 struct got_object_qid
*parent_id
;
3670 char *commit_msg
= NULL
, *commit_msg0
;
3673 TAILQ_FOREACH(re
, &header
->refs
, entry
) {
3676 struct got_tag_object
*tag
= NULL
;
3677 struct got_object_id
*ref_id
;
3680 if (got_ref_is_symbolic(re
->ref
))
3683 name
= got_ref_get_name(re
->ref
);
3684 if (strncmp(name
, "refs/", 5) == 0)
3686 if (strncmp(name
, "got/", 4) == 0)
3688 if (strncmp(name
, "heads/", 6) == 0)
3690 if (strncmp(name
, "remotes/", 8) == 0) {
3692 s
= strstr(name
, "/" GOT_REF_HEAD
);
3693 if (s
!= NULL
&& s
[strlen(s
)] == '\0')
3696 error
= got_ref_resolve(&ref_id
, gw_trans
->repo
, re
->ref
);
3699 if (strncmp(name
, "tags/", 5) == 0) {
3700 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
,
3703 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3708 * Ref points at something other
3715 cmp
= got_object_id_cmp(tag
?
3716 got_object_tag_get_object_id(tag
) : ref_id
, id
);
3719 got_object_tag_close(tag
);
3722 s
= header
->refs_str
;
3723 if (asprintf(&header
->refs_str
, "%s%s%s", s
? s
: "",
3724 s
? ", " : "", name
) == -1) {
3725 error
= got_error_from_errno("asprintf");
3727 header
->refs_str
= NULL
;
3733 error
= got_object_id_str(&header
->commit_id
, id
);
3737 error
= got_object_id_str(&header
->tree_id
,
3738 got_object_commit_get_tree_id(commit
));
3742 if (gw_trans
->action
== GW_DIFF
) {
3743 parent_id
= STAILQ_FIRST(
3744 got_object_commit_get_parent_ids(commit
));
3745 if (parent_id
!= NULL
) {
3746 id2
= got_object_id_dup(&parent_id
->id
);
3748 error
= got_object_id_str(&header
->parent_id
, id2
);
3753 header
->parent_id
= strdup("/dev/null");
3754 if (header
->parent_id
== NULL
) {
3755 error
= got_error_from_errno("strdup");
3761 header
->committer_time
=
3762 got_object_commit_get_committer_time(commit
);
3765 strdup(got_object_commit_get_author(commit
));
3766 if (header
->author
== NULL
) {
3767 error
= got_error_from_errno("strdup");
3771 strdup(got_object_commit_get_committer(commit
));
3772 if (header
->committer
== NULL
) {
3773 error
= got_error_from_errno("strdup");
3776 error
= got_object_commit_get_logmsg(&commit_msg0
, commit
);
3780 commit_msg
= commit_msg0
;
3781 while (*commit_msg
== '\n')
3784 header
->commit_msg
= strdup(commit_msg
);
3785 if (header
->commit_msg
== NULL
)
3786 error
= got_error_from_errno("strdup");
3791 static const struct got_error
*
3792 gw_get_header(struct gw_trans
*gw_trans
, struct gw_header
*header
, int limit
)
3794 const struct got_error
*error
= NULL
;
3795 char *in_repo_path
= NULL
;
3796 struct got_object_id
*id
= NULL
;
3797 struct got_reference
*ref
;
3799 error
= got_repo_open(&gw_trans
->repo
, gw_trans
->repo_path
, NULL
,
3800 gw_trans
->pack_fds
);
3804 if (gw_trans
->commit_id
== NULL
|| gw_trans
->action
== GW_COMMITS
||
3805 gw_trans
->action
== GW_BRIEFS
|| gw_trans
->action
== GW_SUMMARY
||
3806 gw_trans
->action
== GW_TAGS
) {
3807 error
= got_ref_open(&ref
, gw_trans
->repo
,
3808 gw_trans
->headref
, 0);
3812 error
= got_ref_resolve(&id
, gw_trans
->repo
, ref
);
3817 error
= got_ref_open(&ref
, gw_trans
->repo
,
3818 gw_trans
->commit_id
, 0);
3819 if (error
== NULL
) {
3821 error
= got_ref_resolve(&id
, gw_trans
->repo
, ref
);
3825 error
= got_object_get_type(&obj_type
, gw_trans
->repo
,
3829 if (obj_type
== GOT_OBJ_TYPE_TAG
) {
3830 struct got_tag_object
*tag
;
3831 error
= got_object_open_as_tag(&tag
,
3832 gw_trans
->repo
, id
);
3835 if (got_object_tag_get_object_type(tag
) !=
3836 GOT_OBJ_TYPE_COMMIT
) {
3837 got_object_tag_close(tag
);
3838 error
= got_error(GOT_ERR_OBJ_TYPE
);
3842 id
= got_object_id_dup(
3843 got_object_tag_get_object_id(tag
));
3845 error
= got_error_from_errno(
3846 "got_object_id_dup");
3847 got_object_tag_close(tag
);
3850 } else if (obj_type
!= GOT_OBJ_TYPE_COMMIT
) {
3851 error
= got_error(GOT_ERR_OBJ_TYPE
);
3855 error
= got_repo_match_object_id_prefix(&id
,
3856 gw_trans
->commit_id
, GOT_OBJ_TYPE_COMMIT
,
3862 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
,
3863 gw_trans
->repo_path
);
3868 header
->path
= strdup(in_repo_path
);
3869 if (header
->path
== NULL
) {
3870 error
= got_error_from_errno("strdup");
3875 error
= got_ref_list(&header
->refs
, gw_trans
->repo
, NULL
,
3876 got_ref_cmp_by_name
, NULL
);
3880 error
= gw_get_commits(gw_trans
, header
, limit
, id
);
3891 char datebuf
[11]; /* YYYY-MM-DD + NUL */
3894 struct gw_blame_cb_args
{
3895 struct blame_line
*lines
;
3899 off_t
*line_offsets
;
3901 struct got_repository
*repo
;
3902 struct gw_trans
*gw_trans
;
3905 static const struct got_error
*
3906 gw_blame_cb(void *arg
, int nlines
, int lineno
,
3907 struct got_commit_object
*commit
, struct got_object_id
*id
)
3909 const struct got_error
*err
= NULL
;
3910 struct gw_blame_cb_args
*a
= arg
;
3911 struct blame_line
*bline
;
3913 size_t linesize
= 0;
3916 time_t committer_time
;
3917 enum kcgi_err kerr
= KCGI_OK
;
3919 if (nlines
!= a
->nlines
||
3920 (lineno
!= -1 && lineno
< 1) || lineno
> a
->nlines
)
3921 return got_error(GOT_ERR_RANGE
);
3924 return NULL
; /* no change in this commit */
3926 /* Annotate this line. */
3927 bline
= &a
->lines
[lineno
- 1];
3928 if (bline
->annotated
)
3930 err
= got_object_id_str(&bline
->id_str
, id
);
3934 bline
->committer
= strdup(got_object_commit_get_committer(commit
));
3935 if (bline
->committer
== NULL
) {
3936 err
= got_error_from_errno("strdup");
3940 committer_time
= got_object_commit_get_committer_time(commit
);
3941 if (gmtime_r(&committer_time
, &tm
) == NULL
)
3942 return got_error_from_errno("gmtime_r");
3943 if (strftime(bline
->datebuf
, sizeof(bline
->datebuf
), "%G-%m-%d",
3945 err
= got_error(GOT_ERR_NO_SPACE
);
3948 bline
->annotated
= 1;
3950 /* Print lines annotated so far. */
3951 bline
= &a
->lines
[a
->lineno_cur
- 1];
3952 if (!bline
->annotated
)
3955 offset
= a
->line_offsets
[a
->lineno_cur
- 1];
3956 if (fseeko(a
->f
, offset
, SEEK_SET
) == -1) {
3957 err
= got_error_from_errno("fseeko");
3961 while (bline
->annotated
) {
3962 char *smallerthan
, *at
, *nl
, *committer
;
3963 char *href_diff
= NULL
;
3966 if (getline(&line
, &linesize
, a
->f
) == -1) {
3968 err
= got_error_from_errno("getline");
3972 committer
= bline
->committer
;
3973 smallerthan
= strchr(committer
, '<');
3974 if (smallerthan
&& smallerthan
[1] != '\0')
3975 committer
= smallerthan
+ 1;
3976 at
= strchr(committer
, '@');
3979 len
= strlen(committer
);
3981 committer
[8] = '\0';
3983 nl
= strchr(line
, '\n');
3987 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3988 "blame_wrapper", KATTR__MAX
);
3989 if (kerr
!= KCGI_OK
)
3991 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3992 "blame_number", KATTR__MAX
);
3993 if (kerr
!= KCGI_OK
)
3995 kerr
= khtml_printf(a
->gw_trans
->gw_html_req
, "%.*d",
3996 a
->nlines_prec
, a
->lineno_cur
);
3997 if (kerr
!= KCGI_OK
)
3999 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4000 if (kerr
!= KCGI_OK
)
4003 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4004 "blame_hash", KATTR__MAX
);
4005 if (kerr
!= KCGI_OK
)
4008 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4009 a
->gw_trans
->repo_name
, "action", "diff", "commit",
4010 bline
->id_str
, NULL
);
4011 if (href_diff
== NULL
) {
4012 err
= got_error_from_errno("khttp_urlpart");
4015 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_A
,
4016 KATTR_HREF
, href_diff
, KATTR__MAX
);
4017 if (kerr
!= KCGI_OK
)
4019 kerr
= khtml_printf(a
->gw_trans
->gw_html_req
, "%.8s",
4021 if (kerr
!= KCGI_OK
)
4023 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 2);
4024 if (kerr
!= KCGI_OK
)
4027 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4028 "blame_date", KATTR__MAX
);
4029 if (kerr
!= KCGI_OK
)
4031 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, bline
->datebuf
);
4032 if (kerr
!= KCGI_OK
)
4034 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4035 if (kerr
!= KCGI_OK
)
4038 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4039 "blame_author", KATTR__MAX
);
4040 if (kerr
!= KCGI_OK
)
4042 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, committer
);
4043 if (kerr
!= KCGI_OK
)
4045 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4046 if (kerr
!= KCGI_OK
)
4049 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4050 "blame_code", KATTR__MAX
);
4051 if (kerr
!= KCGI_OK
)
4053 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, line
);
4054 if (kerr
!= KCGI_OK
)
4056 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4057 if (kerr
!= KCGI_OK
)
4060 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4061 if (kerr
!= KCGI_OK
)
4065 bline
= &a
->lines
[a
->lineno_cur
- 1];
4071 if (err
== NULL
&& kerr
!= KCGI_OK
)
4072 err
= gw_kcgi_error(kerr
);
4076 static const struct got_error
*
4077 gw_output_file_blame(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4079 const struct got_error
*error
= NULL
;
4080 struct got_object_id
*obj_id
= NULL
;
4081 struct got_object_id
*commit_id
= NULL
;
4082 struct got_commit_object
*commit
= NULL
;
4083 struct got_blob_object
*blob
= NULL
;
4084 char *path
= NULL
, *in_repo_path
= NULL
;
4085 struct gw_blame_cb_args bca
;
4086 int i
, obj_type
, fd1
= -1, fd2
= -1, fd3
= -1;
4088 FILE *f1
= NULL
, *f2
= NULL
;
4090 fd1
= got_opentempfd();
4092 return got_error_from_errno("got_opentempfd");
4093 fd2
= got_opentempfd();
4095 error
= got_error_from_errno("got_opentempfd");
4098 fd3
= got_opentempfd();
4100 error
= got_error_from_errno("got_opentempfd");
4104 memset(&bca
, 0, sizeof(bca
));
4106 if (asprintf(&path
, "%s%s%s",
4107 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4108 gw_trans
->repo_folder
? "/" : "",
4109 gw_trans
->repo_file
) == -1) {
4110 error
= got_error_from_errno("asprintf");
4114 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
, path
);
4118 error
= got_repo_match_object_id(&commit_id
, NULL
, gw_trans
->commit_id
,
4119 GOT_OBJ_TYPE_COMMIT
, &header
->refs
, gw_trans
->repo
);
4123 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, commit_id
);
4127 error
= got_object_id_by_path(&obj_id
, gw_trans
->repo
, commit
,
4132 if (obj_id
== NULL
) {
4133 error
= got_error(GOT_ERR_NO_OBJ
);
4137 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, obj_id
);
4141 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
4142 error
= got_error(GOT_ERR_OBJ_TYPE
);
4146 error
= got_object_open_as_blob(&blob
, gw_trans
->repo
, obj_id
, 8192,
4151 bca
.f
= got_opentemp();
4152 if (bca
.f
== NULL
) {
4153 error
= got_error_from_errno("got_opentemp");
4156 error
= got_object_blob_dump_to_file(&filesize
, &bca
.nlines
,
4157 &bca
.line_offsets
, bca
.f
, blob
);
4158 if (error
|| bca
.nlines
== 0)
4161 /* Don't include \n at EOF in the blame line count. */
4162 if (bca
.line_offsets
[bca
.nlines
- 1] == filesize
)
4165 bca
.lines
= calloc(bca
.nlines
, sizeof(*bca
.lines
));
4166 if (bca
.lines
== NULL
) {
4167 error
= got_error_from_errno("calloc");
4171 bca
.nlines_prec
= 0;
4177 bca
.repo
= gw_trans
->repo
;
4178 bca
.gw_trans
= gw_trans
;
4180 fd1
= got_opentempfd();
4182 error
= got_error_from_errno("got_opentempfd");
4186 f1
= got_opentemp();
4188 error
= got_error_from_errno("got_opentempfd");
4191 f2
= got_opentemp();
4193 error
= got_error_from_errno("got_opentempfd");
4197 error
= got_blame(in_repo_path
, commit_id
, gw_trans
->repo
,
4198 GOT_DIFF_ALGORITHM_PATIENCE
, gw_blame_cb
, &bca
, NULL
, NULL
,
4206 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
4207 error
= got_error_from_errno("close");
4208 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
4209 error
= got_error_from_errno("close");
4210 if (fd3
!= -1 && close(fd3
) == -1 && error
== NULL
)
4211 error
= got_error_from_errno("close");
4212 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
4213 error
= got_error_from_errno("fclose");
4214 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
4215 error
= got_error_from_errno("fclose");
4218 free(bca
.line_offsets
);
4219 for (i
= 0; i
< bca
.nlines
; i
++) {
4220 struct blame_line
*bline
= &bca
.lines
[i
];
4221 free(bline
->id_str
);
4222 free(bline
->committer
);
4225 if (bca
.f
&& fclose(bca
.f
) == EOF
&& error
== NULL
)
4226 error
= got_error_from_errno("fclose");
4229 got_object_blob_close(blob
);
4231 got_object_commit_close(commit
);
4235 static const struct got_error
*
4236 gw_output_blob_buf(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4238 const struct got_error
*error
= NULL
;
4239 struct got_object_id
*obj_id
= NULL
;
4240 struct got_object_id
*commit_id
= NULL
;
4241 struct got_commit_object
*commit
= NULL
;
4242 struct got_blob_object
*blob
= NULL
;
4243 char *path
= NULL
, *in_repo_path
= NULL
;
4244 int obj_type
, set_mime
= 0, fd
= -1;
4247 enum kcgi_err kerr
= KCGI_OK
;
4249 fd
= got_opentempfd();
4251 return got_error_from_errno("got_opentempfd");
4253 if (asprintf(&path
, "%s%s%s",
4254 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4255 gw_trans
->repo_folder
? "/" : "",
4256 gw_trans
->repo_file
) == -1) {
4257 error
= got_error_from_errno("asprintf");
4261 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
, path
);
4265 error
= got_repo_match_object_id(&commit_id
, NULL
, gw_trans
->commit_id
,
4266 GOT_OBJ_TYPE_COMMIT
, &header
->refs
, gw_trans
->repo
);
4270 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, commit_id
);
4274 error
= got_object_id_by_path(&obj_id
, gw_trans
->repo
, commit
,
4279 if (obj_id
== NULL
) {
4280 error
= got_error(GOT_ERR_NO_OBJ
);
4284 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, obj_id
);
4288 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
4289 error
= got_error(GOT_ERR_OBJ_TYPE
);
4293 error
= got_object_open_as_blob(&blob
, gw_trans
->repo
, obj_id
, 8192,
4298 hdrlen
= got_object_blob_get_hdrlen(blob
);
4300 error
= got_object_blob_read_block(&len
, blob
);
4303 buf
= got_object_blob_get_read_buf(blob
);
4306 * Skip blob object header first time around,
4307 * which also contains a zero byte.
4310 if (set_mime
== 0) {
4311 if (isbinary(buf
, len
- hdrlen
))
4312 gw_trans
->mime
= KMIME_APP_OCTET_STREAM
;
4314 gw_trans
->mime
= KMIME_TEXT_PLAIN
;
4316 error
= gw_display_index(gw_trans
);
4320 kerr
= khttp_write(gw_trans
->gw_req
, buf
, len
- hdrlen
);
4321 if (kerr
!= KCGI_OK
)
4330 if (fd
!= -1 && close(fd
) == -1 && error
== NULL
)
4331 error
= got_error_from_errno("close");
4333 got_object_blob_close(blob
);
4335 got_object_commit_close(commit
);
4336 if (error
== NULL
&& kerr
!= KCGI_OK
)
4337 error
= gw_kcgi_error(kerr
);
4341 static const struct got_error
*
4342 gw_output_repo_tree(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4344 const struct got_error
*error
= NULL
;
4345 struct got_object_id
*tree_id
= NULL
, *commit_id
= NULL
;
4346 struct got_tree_object
*tree
= NULL
;
4347 struct got_commit_object
*commit
= NULL
;
4348 char *path
= NULL
, *in_repo_path
= NULL
;
4349 char *id_str
= NULL
;
4350 char *build_folder
= NULL
;
4351 char *href_blob
= NULL
, *href_blame
= NULL
;
4352 const char *class = NULL
;
4353 int nentries
, i
, class_flip
= 0;
4354 enum kcgi_err kerr
= KCGI_OK
;
4356 if (gw_trans
->repo_folder
!= NULL
) {
4357 path
= strdup(gw_trans
->repo_folder
);
4359 error
= got_error_from_errno("strdup");
4363 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
,
4364 gw_trans
->repo_path
);
4368 path
= in_repo_path
;
4371 if (gw_trans
->commit_id
== NULL
) {
4372 struct got_reference
*head_ref
;
4373 error
= got_ref_open(&head_ref
, gw_trans
->repo
,
4374 gw_trans
->headref
, 0);
4377 error
= got_ref_resolve(&commit_id
, gw_trans
->repo
, head_ref
);
4380 got_ref_close(head_ref
);
4382 * gw_trans->commit_id was not parsed from the querystring
4383 * we hit this code path from gw_index, where we don't know the
4384 * commit values for the tree link yet, so set
4385 * gw_trans->commit_id here to continue further into the tree
4387 error
= got_object_id_str(&gw_trans
->commit_id
, commit_id
);
4392 error
= got_repo_match_object_id(&commit_id
, NULL
,
4393 gw_trans
->commit_id
, GOT_OBJ_TYPE_COMMIT
, &header
->refs
,
4399 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, commit_id
);
4403 error
= got_object_id_by_path(&tree_id
, gw_trans
->repo
, commit
,
4408 error
= got_object_open_as_tree(&tree
, gw_trans
->repo
, tree_id
);
4412 nentries
= got_object_tree_get_nentries(tree
);
4413 for (i
= 0; i
< nentries
; i
++) {
4414 struct got_tree_entry
*te
;
4415 const char *modestr
= "";
4418 te
= got_object_tree_get_entry(tree
, i
);
4420 error
= got_object_id_str(&id_str
, got_tree_entry_get_id(te
));
4424 mode
= got_tree_entry_get_mode(te
);
4425 if (got_object_tree_entry_is_submodule(te
))
4427 else if (S_ISLNK(mode
))
4429 else if (S_ISDIR(mode
))
4431 else if (mode
& S_IXUSR
)
4434 if (class_flip
== 0) {
4435 class = "back_lightgray";
4438 class = "back_white";
4442 if (S_ISDIR(mode
)) {
4443 if (asprintf(&build_folder
, "%s/%s",
4444 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4445 got_tree_entry_get_name(te
)) == -1) {
4446 error
= got_error_from_errno("asprintf");
4450 href_blob
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4451 gw_trans
->repo_name
, "action",
4452 gw_get_action_name(gw_trans
), "commit",
4453 gw_trans
->commit_id
, "folder", build_folder
, NULL
);
4454 if (href_blob
== NULL
) {
4455 error
= got_error_from_errno("khttp_urlpart");
4458 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4459 KATTR_ID
, "tree_wrapper", KATTR__MAX
);
4460 if (kerr
!= KCGI_OK
)
4462 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4463 KATTR_ID
, "tree_line", KATTR_CLASS
, class,
4465 if (kerr
!= KCGI_OK
)
4467 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4468 KATTR_HREF
, href_blob
, KATTR_CLASS
,
4469 "diff_directory", KATTR__MAX
);
4470 if (kerr
!= KCGI_OK
)
4472 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s%s",
4473 got_tree_entry_get_name(te
), modestr
);
4474 if (kerr
!= KCGI_OK
)
4476 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4477 if (kerr
!= KCGI_OK
)
4479 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4480 KATTR_ID
, "tree_line_blank", KATTR_CLASS
, class,
4482 if (kerr
!= KCGI_OK
)
4484 kerr
= khtml_entity(gw_trans
->gw_html_req
,
4486 if (kerr
!= KCGI_OK
)
4488 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4489 if (kerr
!= KCGI_OK
)
4492 href_blob
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4493 gw_trans
->repo_name
, "action", "blob", "commit",
4494 gw_trans
->commit_id
, "file",
4495 got_tree_entry_get_name(te
), "folder",
4496 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4498 if (href_blob
== NULL
) {
4499 error
= got_error_from_errno("khttp_urlpart");
4502 href_blame
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4503 gw_trans
->repo_name
, "action", "blame", "commit",
4504 gw_trans
->commit_id
, "file",
4505 got_tree_entry_get_name(te
), "folder",
4506 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4508 if (href_blame
== NULL
) {
4509 error
= got_error_from_errno("khttp_urlpart");
4512 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4513 KATTR_ID
, "tree_wrapper", KATTR__MAX
);
4514 if (kerr
!= KCGI_OK
)
4516 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4517 KATTR_ID
, "tree_line", KATTR_CLASS
, class,
4519 if (kerr
!= KCGI_OK
)
4521 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4522 KATTR_HREF
, href_blob
, KATTR__MAX
);
4523 if (kerr
!= KCGI_OK
)
4525 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s%s",
4526 got_tree_entry_get_name(te
), modestr
);
4527 if (kerr
!= KCGI_OK
)
4529 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4530 if (kerr
!= KCGI_OK
)
4532 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4533 KATTR_ID
, "tree_line_navs", KATTR_CLASS
, class,
4535 if (kerr
!= KCGI_OK
)
4538 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4539 KATTR_HREF
, href_blob
, KATTR__MAX
);
4540 if (kerr
!= KCGI_OK
)
4542 kerr
= khtml_puts(gw_trans
->gw_html_req
, "blob");
4543 if (kerr
!= KCGI_OK
)
4545 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4546 if (kerr
!= KCGI_OK
)
4549 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4550 if (kerr
!= KCGI_OK
)
4553 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4554 KATTR_HREF
, href_blame
, KATTR__MAX
);
4555 if (kerr
!= KCGI_OK
)
4557 kerr
= khtml_puts(gw_trans
->gw_html_req
, "blame");
4558 if (kerr
!= KCGI_OK
)
4561 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4562 if (kerr
!= KCGI_OK
)
4570 build_folder
= NULL
;
4574 got_object_tree_close(tree
);
4576 got_object_commit_close(commit
);
4583 if (error
== NULL
&& kerr
!= KCGI_OK
)
4584 error
= gw_kcgi_error(kerr
);
4588 static const struct got_error
*
4589 gw_output_repo_heads(struct gw_trans
*gw_trans
)
4591 const struct got_error
*error
= NULL
;
4592 struct got_reflist_head refs
;
4593 struct got_reflist_entry
*re
;
4594 char *age
= NULL
, *href_summary
= NULL
, *href_briefs
= NULL
;
4595 char *href_commits
= NULL
;
4596 enum kcgi_err kerr
= KCGI_OK
;
4600 error
= got_ref_list(&refs
, gw_trans
->repo
, "refs/heads",
4601 got_ref_cmp_by_name
, NULL
);
4605 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4606 KATTR_ID
, "summary_heads_title_wrapper", KATTR__MAX
);
4607 if (kerr
!= KCGI_OK
)
4609 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4610 KATTR_ID
, "summary_heads_title", KATTR__MAX
);
4611 if (kerr
!= KCGI_OK
)
4613 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Heads");
4614 if (kerr
!= KCGI_OK
)
4616 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4617 if (kerr
!= KCGI_OK
)
4619 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4620 KATTR_ID
, "summary_heads_content", KATTR__MAX
);
4621 if (kerr
!= KCGI_OK
)
4624 TAILQ_FOREACH(re
, &refs
, entry
) {
4625 const char *refname
;
4627 if (got_ref_is_symbolic(re
->ref
))
4630 refname
= got_ref_get_name(re
->ref
);
4631 if (strncmp(refname
, "refs/heads/", 11) != 0)
4634 error
= gw_get_repo_age(&age
, gw_trans
, gw_trans
->gw_dir
->path
,
4639 if (strncmp(refname
, "refs/heads/", 11) == 0)
4642 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4643 KATTR_ID
, "heads_wrapper", KATTR__MAX
);
4644 if (kerr
!= KCGI_OK
)
4646 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4647 KATTR_ID
, "heads_age", KATTR__MAX
);
4648 if (kerr
!= KCGI_OK
)
4650 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
? age
: "");
4651 if (kerr
!= KCGI_OK
)
4653 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4654 if (kerr
!= KCGI_OK
)
4656 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4657 KATTR_ID
, "heads_space", KATTR__MAX
);
4658 if (kerr
!= KCGI_OK
)
4660 kerr
= khtml_entity(gw_trans
->gw_html_req
, KENTITY_nbsp
);
4661 if (kerr
!= KCGI_OK
)
4663 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4664 if (kerr
!= KCGI_OK
)
4666 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4667 KATTR_ID
, "head", KATTR__MAX
);
4668 if (kerr
!= KCGI_OK
)
4671 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4672 gw_trans
->repo_name
, "action", "summary", "headref",
4674 if (href_summary
== NULL
) {
4675 error
= got_error_from_errno("khttp_urlpart");
4678 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4679 href_summary
, KATTR__MAX
);
4680 kerr
= khtml_puts(gw_trans
->gw_html_req
, refname
);
4681 if (kerr
!= KCGI_OK
)
4683 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4684 if (kerr
!= KCGI_OK
)
4687 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4688 "navs_wrapper", KATTR__MAX
);
4689 if (kerr
!= KCGI_OK
)
4691 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4692 "navs", KATTR__MAX
);
4693 if (kerr
!= KCGI_OK
)
4696 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4697 href_summary
, KATTR__MAX
);
4698 if (kerr
!= KCGI_OK
)
4700 kerr
= khtml_puts(gw_trans
->gw_html_req
, "summary");
4701 if (kerr
!= KCGI_OK
)
4703 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4704 if (kerr
!= KCGI_OK
)
4707 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4708 if (kerr
!= KCGI_OK
)
4711 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4712 gw_trans
->repo_name
, "action", "briefs", "headref",
4714 if (href_briefs
== NULL
) {
4715 error
= got_error_from_errno("khttp_urlpart");
4718 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4719 href_briefs
, KATTR__MAX
);
4720 if (kerr
!= KCGI_OK
)
4722 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commit briefs");
4723 if (kerr
!= KCGI_OK
)
4725 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4726 if (kerr
!= KCGI_OK
)
4729 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4730 if (kerr
!= KCGI_OK
)
4733 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4734 gw_trans
->repo_name
, "action", "commits", "headref",
4736 if (href_commits
== NULL
) {
4737 error
= got_error_from_errno("khttp_urlpart");
4740 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4741 href_commits
, KATTR__MAX
);
4742 if (kerr
!= KCGI_OK
)
4744 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
4745 if (kerr
!= KCGI_OK
)
4747 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4748 if (kerr
!= KCGI_OK
)
4751 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4752 "dotted_line", KATTR__MAX
);
4753 if (kerr
!= KCGI_OK
)
4755 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4756 if (kerr
!= KCGI_OK
)
4759 href_summary
= NULL
;
4763 href_commits
= NULL
;
4766 got_ref_list_free(&refs
);
4773 static const struct got_error
*
4774 gw_output_site_link(struct gw_trans
*gw_trans
)
4776 const struct got_error
*error
= NULL
;
4777 char *href_summary
= NULL
;
4778 enum kcgi_err kerr
= KCGI_OK
;
4780 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4781 "site_link", KATTR__MAX
);
4782 if (kerr
!= KCGI_OK
)
4784 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
, GOTWEB
,
4786 if (kerr
!= KCGI_OK
)
4788 kerr
= khtml_puts(gw_trans
->gw_html_req
,
4789 gw_trans
->gw_conf
->got_site_link
);
4790 if (kerr
!= KCGI_OK
)
4792 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4793 if (kerr
!= KCGI_OK
)
4796 if (gw_trans
->repo_name
!= NULL
) {
4797 kerr
= khtml_puts(gw_trans
->gw_html_req
, " / ");
4798 if (kerr
!= KCGI_OK
)
4801 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4802 gw_trans
->repo_name
, "action", "summary", NULL
);
4803 if (href_summary
== NULL
) {
4804 error
= got_error_from_errno("khttp_urlpart");
4807 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4808 href_summary
, KATTR__MAX
);
4809 if (kerr
!= KCGI_OK
)
4811 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->repo_name
);
4812 if (kerr
!= KCGI_OK
)
4814 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4815 if (kerr
!= KCGI_OK
)
4817 kerr
= khtml_printf(gw_trans
->gw_html_req
, " / %s",
4818 gw_get_action_name(gw_trans
));
4819 if (kerr
!= KCGI_OK
)
4823 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4824 if (kerr
!= KCGI_OK
)
4828 if (error
== NULL
&& kerr
!= KCGI_OK
)
4829 error
= gw_kcgi_error(kerr
);
4833 static const struct got_error
*
4834 gw_colordiff_line(struct gw_trans
*gw_trans
, char *buf
)
4836 const struct got_error
*error
= NULL
;
4837 const char *color
= NULL
;
4838 enum kcgi_err kerr
= KCGI_OK
;
4840 if (strncmp(buf
, "-", 1) == 0)
4841 color
= "diff_minus";
4842 else if (strncmp(buf
, "+", 1) == 0)
4843 color
= "diff_plus";
4844 else if (strncmp(buf
, "@@", 2) == 0)
4845 color
= "diff_chunk_header";
4846 else if (strncmp(buf
, "@@", 2) == 0)
4847 color
= "diff_chunk_header";
4848 else if (strncmp(buf
, "commit +", 8) == 0)
4849 color
= "diff_meta";
4850 else if (strncmp(buf
, "commit -", 8) == 0)
4851 color
= "diff_meta";
4852 else if (strncmp(buf
, "blob +", 6) == 0)
4853 color
= "diff_meta";
4854 else if (strncmp(buf
, "blob -", 6) == 0)
4855 color
= "diff_meta";
4856 else if (strncmp(buf
, "file +", 6) == 0)
4857 color
= "diff_meta";
4858 else if (strncmp(buf
, "file -", 6) == 0)
4859 color
= "diff_meta";
4860 else if (strncmp(buf
, "from:", 5) == 0)
4861 color
= "diff_author";
4862 else if (strncmp(buf
, "via:", 4) == 0)
4863 color
= "diff_author";
4864 else if (strncmp(buf
, "date:", 5) == 0)
4865 color
= "diff_date";
4866 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4867 "diff_line", KATTR_CLASS
, color
? color
: "", KATTR__MAX
);
4868 if (error
== NULL
&& kerr
!= KCGI_OK
)
4869 error
= gw_kcgi_error(kerr
);
4874 main(int argc
, char *argv
[])
4876 const struct got_error
*error
= NULL
, *error2
= NULL
;
4877 struct gw_trans
*gw_trans
;
4878 struct gw_dir
*dir
= NULL
, *tdir
;
4879 const char *page
= "index";
4880 enum kcgi_err kerr
= KCGI_OK
;
4882 if ((gw_trans
= malloc(sizeof(struct gw_trans
))) == NULL
)
4885 if ((gw_trans
->gw_req
= malloc(sizeof(struct kreq
))) == NULL
)
4888 if ((gw_trans
->gw_html_req
= malloc(sizeof(struct khtmlreq
))) == NULL
)
4891 if ((gw_trans
->gw_tmpl
= malloc(sizeof(struct ktemplate
))) == NULL
)
4894 kerr
= khttp_parse(gw_trans
->gw_req
, gw_keys
, KEY__ZMAX
, &page
, 1, 0);
4895 if (kerr
!= KCGI_OK
) {
4896 error
= gw_kcgi_error(kerr
);
4900 TAILQ_INIT(&gw_trans
->gw_dirs
);
4901 TAILQ_INIT(&gw_trans
->gw_headers
);
4903 gw_trans
->action
= -1;
4905 gw_trans
->repos_total
= 0;
4906 gw_trans
->repo_path
= NULL
;
4907 gw_trans
->commit_id
= NULL
;
4908 gw_trans
->next_id
= NULL
;
4909 gw_trans
->prev_id
= NULL
;
4910 gw_trans
->headref
= GOT_REF_HEAD
;
4911 gw_trans
->mime
= KMIME_TEXT_HTML
;
4912 gw_trans
->gw_tmpl
->key
= gw_templs
;
4913 gw_trans
->gw_tmpl
->keysz
= TEMPL__MAX
;
4914 gw_trans
->gw_tmpl
->arg
= gw_trans
;
4915 gw_trans
->gw_tmpl
->cb
= gw_template
;
4917 error
= got_repo_pack_fds_open(&gw_trans
->pack_fds
);
4921 error
= parse_gotweb_config(&gw_trans
->gw_conf
, GOTWEB_CONF
);
4925 error
= gw_parse_querystring(gw_trans
);
4929 if (gw_trans
->repo
) {
4930 const struct got_error
*close_err
;
4931 close_err
= got_repo_close(gw_trans
->repo
);
4935 if (gw_trans
->action
== GW_BLOB
)
4936 error
= gw_blob(gw_trans
);
4938 error
= gw_display_index(gw_trans
);
4941 gw_trans
->error
= error
;
4942 gw_trans
->action
= GW_ERR
;
4943 error2
= gw_display_open(gw_trans
, KHTTP_200
, gw_trans
->mime
);
4945 goto cleanup
; /* we can't display an error page */
4946 kerr
= khtml_open(gw_trans
->gw_html_req
, gw_trans
->gw_req
, 0);
4947 if (kerr
!= KCGI_OK
)
4948 goto cleanup
; /* we can't display an error page */
4949 kerr
= khttp_template(gw_trans
->gw_req
, gw_trans
->gw_tmpl
,
4950 gw_query_funcs
[gw_trans
->action
].template);
4951 if (kerr
!= KCGI_OK
) {
4952 khtml_close(gw_trans
->gw_html_req
);
4953 goto cleanup
; /* we can't display an error page */
4958 if (gw_trans
->pack_fds
) {
4959 const struct got_error
*pack_err
=
4960 got_repo_pack_fds_close(gw_trans
->pack_fds
);
4963 gw_trans
->pack_fds
= NULL
;
4965 free(gw_trans
->gw_conf
->got_repos_path
);
4966 free(gw_trans
->gw_conf
->got_www_path
);
4967 free(gw_trans
->gw_conf
->got_site_name
);
4968 free(gw_trans
->gw_conf
->got_site_owner
);
4969 free(gw_trans
->gw_conf
->got_site_link
);
4970 free(gw_trans
->gw_conf
->got_logo
);
4971 free(gw_trans
->gw_conf
->got_logo_url
);
4972 free(gw_trans
->gw_conf
);
4973 free(gw_trans
->commit_id
);
4974 free(gw_trans
->next_id
);
4975 free(gw_trans
->prev_id
);
4976 free(gw_trans
->repo_path
);
4977 TAILQ_FOREACH_SAFE(dir
, &gw_trans
->gw_dirs
, entry
, tdir
) {
4979 free(dir
->description
);
4986 khttp_free(gw_trans
->gw_req
);