2 * Copyright (c) 2020-2022 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2018, 2019 Stefan Sperling <stsp@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <sys/queue.h>
19 #include <sys/queue.h>
20 #include <sys/socket.h>
29 #include "got_error.h"
30 #include "got_object.h"
31 #include "got_reference.h"
32 #include "got_repository.h"
34 #include "got_cancel.h"
36 #include "got_commit_graph.h"
37 #include "got_blame.h"
38 #include "got_privsep.h"
40 #include "got_compat.h"
45 static const struct got_error
*got_init_repo_commit(struct repo_commit
**);
46 static const struct got_error
*got_init_repo_tag(struct repo_tag
**);
47 static const struct got_error
*got_get_repo_commit(struct request
*,
48 struct repo_commit
*, struct got_commit_object
*, struct got_reflist_head
*,
49 struct got_object_id
*);
50 static const struct got_error
*got_gotweb_dupfd(int *, int *);
51 static const struct got_error
*got_gotweb_openfile(FILE **, int *, int *);
52 static const struct got_error
*got_gotweb_flushfile(FILE *, int);
53 static const struct got_error
*got_gotweb_blame_cb(void *, int, int,
54 struct got_commit_object
*,struct got_object_id
*);
57 isbinary(const uint8_t *buf
, size_t n
)
61 for (i
= 0; i
< n
; i
++)
68 static const struct got_error
*
69 got_gotweb_flushfile(FILE *f
, int fd
)
71 if (fseek(f
, 0, SEEK_SET
) == -1)
72 return got_error_from_errno("fseek");
74 if (ftruncate(fd
, 0) == -1)
75 return got_error_from_errno("ftruncate");
78 return got_error_from_errno("fsync");
80 if (f
&& fclose(f
) == EOF
)
81 return got_error_from_errno("fclose");
83 if (fd
!= -1 && close(fd
) != -1)
84 return got_error_from_errno("close");
89 static const struct got_error
*
90 got_gotweb_openfile(FILE **f
, int *priv_fd
, int *fd
)
92 const struct got_error
*error
= NULL
;
99 *f
= fdopen(*fd
, "w+");
102 error
= got_error(GOT_ERR_PRIVSEP_NO_FD
);
108 static const struct got_error
*
109 got_gotweb_dupfd(int *priv_fd
, int *fd
)
111 const struct got_error
*error
= NULL
;
121 const struct got_error
*
122 got_get_repo_owner(char **owner
, struct request
*c
, char *dir
)
124 const struct got_error
*error
= NULL
;
125 struct server
*srv
= c
->srv
;
126 struct transport
*t
= c
->t
;
127 struct got_repository
*repo
= t
->repo
;
128 const char *gitconfig_owner
;
132 if (srv
->show_repo_owner
== 0)
135 gitconfig_owner
= got_repo_get_gitconfig_owner(repo
);
136 if (gitconfig_owner
) {
137 *owner
= strdup(gitconfig_owner
);
139 return got_error_from_errno("strdup");
144 const struct got_error
*
145 got_get_repo_age(char **repo_age
, struct request
*c
, char *dir
,
146 const char *refname
, int ref_tm
)
148 const struct got_error
*error
= NULL
;
149 struct server
*srv
= c
->srv
;
150 struct transport
*t
= c
->t
;
151 struct got_repository
*repo
= t
->repo
;
152 struct got_commit_object
*commit
= NULL
;
153 struct got_reflist_head refs
;
154 struct got_reflist_entry
*re
;
155 time_t committer_time
= 0, cmp_time
= 0;
160 if (srv
->show_repo_age
== 0)
163 error
= got_ref_list(&refs
, repo
, "refs/heads",
164 got_ref_cmp_by_name
, NULL
);
169 * Find the youngest branch tip in the repository, or the age of
170 * the a specific branch tip if a name was provided by the caller.
172 TAILQ_FOREACH(re
, &refs
, entry
) {
173 struct got_object_id
*id
= NULL
;
175 if (refname
&& strcmp(got_ref_get_name(re
->ref
), refname
) != 0)
178 error
= got_ref_resolve(&id
, repo
, re
->ref
);
182 error
= got_object_open_as_commit(&commit
, repo
, id
);
188 got_object_commit_get_committer_time(commit
);
189 got_object_commit_close(commit
);
190 if (cmp_time
< committer_time
)
191 cmp_time
= committer_time
;
198 committer_time
= cmp_time
;
199 error
= gotweb_get_time_str(repo_age
, committer_time
, ref_tm
);
202 got_ref_list_free(&refs
);
206 static const struct got_error
*
207 got_get_repo_commit(struct request
*c
, struct repo_commit
*repo_commit
,
208 struct got_commit_object
*commit
, struct got_reflist_head
*refs
,
209 struct got_object_id
*id
)
211 const struct got_error
*error
= NULL
;
212 struct got_reflist_entry
*re
;
213 struct got_object_id
*id2
= NULL
;
214 struct got_object_qid
*parent_id
;
215 struct transport
*t
= c
->t
;
216 struct querystring
*qs
= c
->t
->qs
;
217 char *commit_msg
= NULL
, *commit_msg0
;
219 TAILQ_FOREACH(re
, refs
, entry
) {
222 struct got_tag_object
*tag
= NULL
;
223 struct got_object_id
*ref_id
;
226 if (got_ref_is_symbolic(re
->ref
))
229 name
= got_ref_get_name(re
->ref
);
230 if (strncmp(name
, "refs/", 5) == 0)
232 if (strncmp(name
, "got/", 4) == 0)
234 if (strncmp(name
, "heads/", 6) == 0)
236 if (strncmp(name
, "remotes/", 8) == 0) {
238 s
= strstr(name
, "/" GOT_REF_HEAD
);
239 if (s
!= NULL
&& s
[strlen(s
)] == '\0')
242 error
= got_ref_resolve(&ref_id
, t
->repo
, re
->ref
);
245 if (strncmp(name
, "tags/", 5) == 0) {
246 error
= got_object_open_as_tag(&tag
, t
->repo
, ref_id
);
248 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
253 * Ref points at something other
260 cmp
= got_object_id_cmp(tag
?
261 got_object_tag_get_object_id(tag
) : ref_id
, id
);
264 got_object_tag_close(tag
);
267 s
= repo_commit
->refs_str
;
268 if (asprintf(&repo_commit
->refs_str
, "%s%s%s", s
? s
: "",
269 s
? ", " : "", name
) == -1) {
270 error
= got_error_from_errno("asprintf");
272 repo_commit
->refs_str
= NULL
;
278 error
= got_object_id_str(&repo_commit
->commit_id
, id
);
282 error
= got_object_id_str(&repo_commit
->tree_id
,
283 got_object_commit_get_tree_id(commit
));
287 if (qs
->action
== DIFF
) {
288 parent_id
= STAILQ_FIRST(
289 got_object_commit_get_parent_ids(commit
));
290 if (parent_id
!= NULL
) {
291 id2
= got_object_id_dup(&parent_id
->id
);
292 error
= got_object_id_str(&repo_commit
->parent_id
, id2
);
297 repo_commit
->parent_id
= strdup("/dev/null");
298 if (repo_commit
->parent_id
== NULL
) {
299 error
= got_error_from_errno("strdup");
305 repo_commit
->committer_time
=
306 got_object_commit_get_committer_time(commit
);
308 repo_commit
->author
=
309 strdup(got_object_commit_get_author(commit
));
310 if (repo_commit
->author
== NULL
) {
311 error
= got_error_from_errno("strdup");
314 repo_commit
->committer
=
315 strdup(got_object_commit_get_committer(commit
));
316 if (repo_commit
->committer
== NULL
) {
317 error
= got_error_from_errno("strdup");
320 error
= got_object_commit_get_logmsg(&commit_msg0
, commit
);
324 commit_msg
= commit_msg0
;
325 while (*commit_msg
== '\n')
328 repo_commit
->commit_msg
= strdup(commit_msg
);
329 if (repo_commit
->commit_msg
== NULL
)
330 error
= got_error_from_errno("strdup");
335 const struct got_error
*
336 got_get_repo_commits(struct request
*c
, int limit
)
338 const struct got_error
*error
= NULL
;
339 struct got_object_id
*id
= NULL
;
340 struct got_commit_graph
*graph
= NULL
;
341 struct got_commit_object
*commit
= NULL
;
342 struct got_reflist_head refs
;
343 struct got_reference
*ref
;
344 struct repo_commit
*repo_commit
= NULL
;
345 struct server
*srv
= c
->srv
;
346 struct transport
*t
= c
->t
;
347 struct got_repository
*repo
= t
->repo
;
348 struct querystring
*qs
= t
->qs
;
349 struct repo_dir
*repo_dir
= t
->repo_dir
;
350 char *in_repo_path
= NULL
, *repo_path
= NULL
, *file_path
= NULL
;
351 int chk_next
= 0, chk_multi
= 0, commit_found
= 0;
352 int obj_type
, limit_chk
= 0;
356 if (qs
->file
!= NULL
&& strlen(qs
->file
) > 0)
357 if (asprintf(&file_path
, "%s/%s", qs
->folder
? qs
->folder
: "",
359 return got_error_from_errno("asprintf");
361 if (asprintf(&repo_path
, "%s/%s", srv
->repos_path
,
362 repo_dir
->name
) == -1)
363 return got_error_from_errno("asprintf");
365 error
= got_init_repo_commit(&repo_commit
);
370 * XXX: jumping directly to a commit id via
371 * got_repo_match_object_id_prefix significantly improves performance,
372 * but does not allow us to create a PREVIOUS button, since commits can
373 * only be itereated forward. So, we have to match as we iterate from
376 if (qs
->action
== BRIEFS
|| qs
->action
== COMMITS
||
377 (qs
->action
== TREE
&& qs
->commit
== NULL
)) {
378 error
= got_ref_open(&ref
, repo
, qs
->headref
, 0);
382 error
= got_ref_resolve(&id
, repo
, ref
);
386 } else if (qs
->commit
!= NULL
) {
387 error
= got_ref_open(&ref
, repo
, qs
->commit
, 0);
389 error
= got_ref_resolve(&id
, repo
, ref
);
392 error
= got_object_get_type(&obj_type
, repo
, id
);
396 if (obj_type
== GOT_OBJ_TYPE_TAG
) {
397 struct got_tag_object
*tag
;
398 error
= got_object_open_as_tag(&tag
, repo
, id
);
401 if (got_object_tag_get_object_type(tag
) !=
402 GOT_OBJ_TYPE_COMMIT
) {
403 got_object_tag_close(tag
);
404 error
= got_error(GOT_ERR_OBJ_TYPE
);
408 id
= got_object_id_dup(
409 got_object_tag_get_object_id(tag
));
411 error
= got_error_from_errno(
412 "got_object_id_dup");
413 got_object_tag_close(tag
);
416 } else if (obj_type
!= GOT_OBJ_TYPE_COMMIT
) {
417 error
= got_error(GOT_ERR_OBJ_TYPE
);
421 error
= got_repo_match_object_id_prefix(&id
, qs
->commit
,
422 GOT_OBJ_TYPE_COMMIT
, repo
);
427 error
= got_repo_map_path(&in_repo_path
, repo
, repo_path
);
431 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
435 if (qs
->file
!= NULL
&& strlen(qs
->file
) > 0) {
436 error
= got_commit_graph_open(&graph
, file_path
, 0);
440 error
= got_commit_graph_open(&graph
, in_repo_path
, 0);
445 error
= got_commit_graph_iter_start(graph
, id
, repo
, NULL
, NULL
);
450 if (limit_chk
== ((limit
* qs
->page
) - (limit
- 1)) &&
451 commit_found
== 0 && repo_commit
->commit_id
!= NULL
) {
452 t
->prev_id
= strdup(repo_commit
->commit_id
);
453 if (t
->prev_id
== NULL
) {
454 error
= got_error_from_errno("strdup");
459 error
= got_commit_graph_iter_next(&id
, graph
, repo
, NULL
,
462 if (error
->code
== GOT_ERR_ITER_COMPLETED
)
469 error
= got_object_open_as_commit(&commit
, repo
, id
);
473 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
478 error
= got_get_repo_commit(c
, repo_commit
, commit
,
483 if (qs
->commit
!= NULL
&& commit_found
== 0 && limit
!= 1) {
484 if (strcmp(qs
->commit
, repo_commit
->commit_id
) == 0)
486 else if (qs
->file
!= NULL
&& strlen(qs
->file
) > 0 &&
497 struct repo_commit
*new_repo_commit
= NULL
;
498 error
= got_init_repo_commit(&new_repo_commit
);
502 TAILQ_INSERT_TAIL(&t
->repo_commits
, new_repo_commit
, entry
);
504 error
= got_get_repo_commit(c
, new_repo_commit
, commit
,
512 if (limit
== 1 && chk_multi
== 0 &&
513 srv
->max_commits_display
!= 1)
519 * check for one more commit before breaking,
520 * so we know whether to navigate through briefs
521 * commits and summary
523 if (chk_next
&& (qs
->action
== BRIEFS
||
524 qs
->action
== COMMITS
|| qs
->action
== SUMMARY
)) {
525 t
->next_id
= strdup(new_repo_commit
->commit_id
);
526 if (t
->next_id
== NULL
) {
527 error
= got_error_from_errno("strdup");
531 got_object_commit_close(commit
);
534 if (t
->next_id
== NULL
) {
535 error
= got_error_from_errno("strdup");
538 TAILQ_REMOVE(&t
->repo_commits
, new_repo_commit
,
540 gotweb_free_repo_commit(new_repo_commit
);
544 got_ref_list_free(&refs
);
545 if (error
|| (limit
&& --limit
== 0)) {
546 if (commit_found
|| (qs
->file
!= NULL
&&
547 strlen(qs
->file
) > 0))
553 got_object_commit_close(commit
);
558 gotweb_free_repo_commit(repo_commit
);
560 got_object_commit_close(commit
);
562 got_commit_graph_close(graph
);
563 got_ref_list_free(&refs
);
570 const struct got_error
*
571 got_get_repo_tags(struct request
*c
, int limit
)
573 const struct got_error
*error
= NULL
;
574 struct got_object_id
*id
= NULL
;
575 struct got_commit_object
*commit
= NULL
;
576 struct got_reflist_head refs
;
577 struct got_reference
*ref
;
578 struct got_reflist_entry
*re
;
579 struct server
*srv
= c
->srv
;
580 struct transport
*t
= c
->t
;
581 struct got_repository
*repo
= t
->repo
;
582 struct querystring
*qs
= t
->qs
;
583 struct repo_dir
*repo_dir
= t
->repo_dir
;
584 struct got_tag_object
*tag
= NULL
;
585 struct repo_tag
*rt
= NULL
, *trt
= NULL
;
586 char *in_repo_path
= NULL
, *repo_path
= NULL
, *id_str
= NULL
;
587 char *commit_msg
= NULL
, *commit_msg0
= NULL
;
588 int chk_next
= 0, chk_multi
= 1, commit_found
= 0, c_cnt
= 0;
592 if (asprintf(&repo_path
, "%s/%s", srv
->repos_path
,
593 repo_dir
->name
) == -1)
594 return got_error_from_errno("asprintf");
599 if (qs
->commit
== NULL
&& qs
->action
== TAGS
) {
600 error
= got_ref_open(&ref
, repo
, qs
->headref
, 0);
603 error
= got_ref_resolve(&id
, repo
, ref
);
607 } else if (qs
->commit
== NULL
&& qs
->action
== TAG
) {
608 error
= got_error_msg(GOT_ERR_EOF
, "commit id missing");
611 error
= got_repo_match_object_id_prefix(&id
, qs
->commit
,
612 GOT_OBJ_TYPE_COMMIT
, repo
);
617 if (qs
->action
!= SUMMARY
&& qs
->action
!= TAGS
) {
618 error
= got_object_open_as_commit(&commit
, repo
, id
);
621 error
= got_object_commit_get_logmsg(&commit_msg0
, commit
);
625 got_object_commit_close(commit
);
630 error
= got_repo_map_path(&in_repo_path
, repo
, repo_path
);
634 error
= got_ref_list(&refs
, repo
, "refs/tags", got_ref_cmp_tags
,
643 * XXX: again, see previous message about caching
646 TAILQ_FOREACH(re
, &refs
, entry
) {
647 struct repo_tag
*new_repo_tag
= NULL
;
648 error
= got_init_repo_tag(&new_repo_tag
);
652 TAILQ_INSERT_TAIL(&t
->repo_tags
, new_repo_tag
, entry
);
654 new_repo_tag
->tag_name
= strdup(got_ref_get_name(re
->ref
));
655 if (new_repo_tag
->tag_name
== NULL
) {
656 error
= got_error_from_errno("strdup");
660 error
= got_ref_resolve(&id
, repo
, re
->ref
);
664 error
= got_object_open_as_tag(&tag
, repo
, id
);
666 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
671 /* "lightweight" tag */
672 error
= got_object_open_as_commit(&commit
, repo
, id
);
678 new_repo_tag
->tagger
=
679 strdup(got_object_commit_get_committer(commit
));
680 if (new_repo_tag
->tagger
== NULL
) {
681 error
= got_error_from_errno("strdup");
684 new_repo_tag
->tagger_time
=
685 got_object_commit_get_committer_time(commit
);
686 error
= got_object_id_str(&id_str
, id
);
694 new_repo_tag
->tagger
=
695 strdup(got_object_tag_get_tagger(tag
));
696 if (new_repo_tag
->tagger
== NULL
) {
697 error
= got_error_from_errno("strdup");
700 new_repo_tag
->tagger_time
=
701 got_object_tag_get_tagger_time(tag
);
702 error
= got_object_id_str(&id_str
,
703 got_object_tag_get_object_id(tag
));
708 new_repo_tag
->commit_id
= strdup(id_str
);
709 if (new_repo_tag
->commit_id
== NULL
)
712 if (commit_found
== 0 && qs
->commit
!= NULL
&&
713 strncmp(id_str
, qs
->commit
, strlen(id_str
)) != 0)
721 * check for one more commit before breaking,
722 * so we know whether to navigate through briefs
723 * commits and summary
726 t
->next_id
= strdup(new_repo_tag
->commit_id
);
727 if (t
->next_id
== NULL
) {
728 error
= got_error_from_errno("strdup");
732 got_object_commit_close(commit
);
735 if (t
->next_id
== NULL
) {
736 error
= got_error_from_errno("strdup");
739 TAILQ_REMOVE(&t
->repo_tags
, new_repo_tag
, entry
);
740 gotweb_free_repo_tag(new_repo_tag
);
745 error
= got_object_commit_get_logmsg(&new_repo_tag
->
749 got_object_commit_close(commit
);
752 new_repo_tag
->tag_commit
=
753 strdup(got_object_tag_get_message(tag
));
754 if (new_repo_tag
->tag_commit
== NULL
) {
755 error
= got_error_from_errno("strdup");
760 while (*new_repo_tag
->tag_commit
== '\n')
761 new_repo_tag
->tag_commit
++;
763 if (qs
->action
!= SUMMARY
&& qs
->action
!= TAGS
) {
764 commit_msg
= commit_msg0
;
765 while (*commit_msg
== '\n')
768 new_repo_tag
->commit_msg
= strdup(commit_msg
);
769 if (new_repo_tag
->commit_msg
== NULL
) {
770 error
= got_error_from_errno("strdup");
777 if (limit
&& --limit
== 0) {
788 * we have tailq populated, so find previous commit id
789 * for navigation through briefs and commits
791 if (t
->tag_count
== 0) {
792 TAILQ_FOREACH_SAFE(rt
, &t
->repo_tags
, entry
, trt
) {
793 TAILQ_REMOVE(&t
->repo_tags
, rt
, entry
);
794 gotweb_free_repo_tag(rt
);
797 if (t
->tag_count
> 0 && t
->prev_id
== NULL
&& qs
->commit
!= NULL
) {
799 TAILQ_FOREACH_REVERSE(rt
, &t
->repo_tags
, repo_tags_head
,
801 if (commit_found
== 0 && rt
->commit_id
!= NULL
&&
802 strcmp(qs
->commit
, rt
->commit_id
) != 0) {
806 if (c_cnt
== srv
->max_commits_display
||
807 rt
== TAILQ_FIRST(&t
->repo_tags
)) {
808 t
->prev_id
= strdup(rt
->commit_id
);
809 if (t
->prev_id
== NULL
)
810 error
= got_error_from_errno("strdup");
818 got_object_commit_close(commit
);
819 got_ref_list_free(&refs
);
825 const struct got_error
*
826 got_output_repo_tree(struct request
*c
)
828 const struct got_error
*error
= NULL
;
829 struct transport
*t
= c
->t
;
830 struct got_commit_object
*commit
= NULL
;
831 struct got_repository
*repo
= t
->repo
;
832 struct querystring
*qs
= t
->qs
;
833 struct repo_commit
*rc
= NULL
;
834 struct got_object_id
*tree_id
= NULL
, *commit_id
= NULL
;
835 struct got_reflist_head refs
;
836 struct got_tree_object
*tree
= NULL
;
837 struct repo_dir
*repo_dir
= t
->repo_dir
;
839 char *path
= NULL
, *in_repo_path
= NULL
, *build_folder
= NULL
;
840 char *modestr
= NULL
, *name
= NULL
, *class = NULL
;
841 int nentries
, i
, class_flip
= 0;
845 rc
= TAILQ_FIRST(&t
->repo_commits
);
847 if (qs
->folder
!= NULL
) {
848 path
= strdup(qs
->folder
);
850 error
= got_error_from_errno("strdup");
854 error
= got_repo_map_path(&in_repo_path
, repo
, repo_dir
->path
);
861 error
= got_repo_match_object_id(&commit_id
, NULL
, rc
->commit_id
,
862 GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
866 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
870 error
= got_object_id_by_path(&tree_id
, repo
, commit
, path
);
874 error
= got_object_open_as_tree(&tree
, repo
, tree_id
);
878 nentries
= got_object_tree_get_nentries(tree
);
880 for (i
= 0; i
< nentries
; i
++) {
881 struct got_tree_entry
*te
;
884 te
= got_object_tree_get_entry(tree
, i
);
886 error
= got_object_id_str(&id_str
, got_tree_entry_get_id(te
));
890 modestr
= strdup("");
891 if (modestr
== NULL
) {
892 error
= got_error_from_errno("strdup");
895 mode
= got_tree_entry_get_mode(te
);
896 if (got_object_tree_entry_is_submodule(te
)) {
898 modestr
= strdup("$");
899 if (modestr
== NULL
) {
900 error
= got_error_from_errno("strdup");
903 } else if (S_ISLNK(mode
)) {
905 modestr
= strdup("@");
906 if (modestr
== NULL
) {
907 error
= got_error_from_errno("strdup");
910 } else if (S_ISDIR(mode
)) {
912 modestr
= strdup("/");
913 if (modestr
== NULL
) {
914 error
= got_error_from_errno("strdup");
917 } else if (mode
& S_IXUSR
) {
919 modestr
= strdup("*");
920 if (modestr
== NULL
) {
921 error
= got_error_from_errno("strdup");
926 if (class_flip
== 0) {
927 class = strdup("back_lightgray");
929 error
= got_error_from_errno("strdup");
934 class = strdup("back_white");
936 error
= got_error_from_errno("strdup");
942 name
= strdup(got_tree_entry_get_name(te
));
944 error
= got_error_from_errno("strdup");
948 if (asprintf(&build_folder
, "%s/%s",
949 qs
->folder
? qs
->folder
: "",
950 got_tree_entry_get_name(te
)) == -1) {
951 error
= got_error_from_errno("asprintf");
955 if (fcgi_gen_response(c
,
956 "<div id='tree_wrapper'>\n") == -1)
959 if (fcgi_gen_response(c
, "<div id='tree_line' "
962 if (fcgi_gen_response(c
, class) == -1)
964 if (fcgi_gen_response(c
, "'>") == -1)
967 if (fcgi_gen_response(c
, "<a class='diff_directory' "
968 "href='?index_page=") == -1)
970 if (fcgi_gen_response(c
, qs
->index_page_str
) == -1)
972 if (fcgi_gen_response(c
, "&path=") == -1)
974 if (fcgi_gen_response(c
, qs
->path
) == -1)
976 if (fcgi_gen_response(c
, "&action=tree") == -1)
978 if (fcgi_gen_response(c
, "&commit=") == -1)
980 if (fcgi_gen_response(c
, rc
->commit_id
) == -1)
982 if (fcgi_gen_response(c
, "&folder=") == -1)
984 if (fcgi_gen_response(c
, build_folder
) == -1)
986 if (fcgi_gen_response(c
, "'>") == -1)
988 if (fcgi_gen_response(c
, name
) == -1)
990 if (fcgi_gen_response(c
, modestr
) == -1)
992 if (fcgi_gen_response(c
, "</a>") == -1)
995 if (fcgi_gen_response(c
, "</div>\n") == -1)
998 if (fcgi_gen_response(c
, "<div id='tree_line_blank' "
1001 if (fcgi_gen_response(c
, class) == -1)
1003 if (fcgi_gen_response(c
, "'>") == -1)
1005 if (fcgi_gen_response(c
, " ") == -1)
1007 if (fcgi_gen_response(c
, "</div>\n") == -1)
1010 if (fcgi_gen_response(c
, "</div>\n") == -1)
1015 name
= strdup(got_tree_entry_get_name(te
));
1017 error
= got_error_from_errno("strdup");
1021 if (fcgi_gen_response(c
,
1022 "<div id='tree_wrapper'>\n") == -1)
1024 if (fcgi_gen_response(c
, "<div id='tree_line' "
1027 if (fcgi_gen_response(c
, class) == -1)
1029 if (fcgi_gen_response(c
, "'>") == -1)
1032 if (fcgi_gen_response(c
,
1033 "<a href='?index_page=") == -1)
1036 if (fcgi_gen_response(c
, qs
->index_page_str
) == -1)
1039 if (fcgi_gen_response(c
, "&path=") == -1)
1041 if (fcgi_gen_response(c
, qs
->path
) == -1)
1044 if (fcgi_gen_response(c
, "&action=blob") == -1)
1047 if (fcgi_gen_response(c
, "&commit=") == -1)
1049 if (fcgi_gen_response(c
, rc
->commit_id
) == -1)
1052 if (fcgi_gen_response(c
, "&folder=") == -1)
1054 if (fcgi_gen_response(c
, qs
->folder
) == -1)
1057 if (fcgi_gen_response(c
, "&file=") == -1)
1059 if (fcgi_gen_response(c
, name
) == -1)
1062 if (fcgi_gen_response(c
, "'>") == -1)
1064 if (fcgi_gen_response(c
, name
) == -1)
1066 if (fcgi_gen_response(c
, modestr
) == -1)
1069 if (fcgi_gen_response(c
, "</a>") == -1)
1072 if (fcgi_gen_response(c
, "</div>\n") == -1)
1075 if (fcgi_gen_response(c
, "<div id='tree_line_blank' "
1078 if (fcgi_gen_response(c
, class) == -1)
1080 if (fcgi_gen_response(c
, "'>") == -1)
1083 if (fcgi_gen_response(c
,
1084 "<a href='?index_page=") == -1)
1087 if (fcgi_gen_response(c
, qs
->index_page_str
) == -1)
1090 if (fcgi_gen_response(c
, "&path=") == -1)
1092 if (fcgi_gen_response(c
, qs
->path
) == -1)
1095 if (fcgi_gen_response(c
, "&action=commits") == -1)
1098 if (fcgi_gen_response(c
, "&commit=") == -1)
1100 if (fcgi_gen_response(c
, rc
->commit_id
) == -1)
1103 if (fcgi_gen_response(c
, "&folder=") == -1)
1105 if (fcgi_gen_response(c
, qs
->folder
) == -1)
1108 if (fcgi_gen_response(c
, "&file=") == -1)
1110 if (fcgi_gen_response(c
, name
) == -1)
1113 if (fcgi_gen_response(c
, "'>") == -1)
1116 if (fcgi_gen_response(c
, "commits") == -1)
1118 if (fcgi_gen_response(c
, "</a>\n") == -1)
1121 if (fcgi_gen_response(c
, " | \n") == -1)
1124 if (fcgi_gen_response(c
,
1125 "<a href='?index_page=") == -1)
1128 if (fcgi_gen_response(c
, qs
->index_page_str
) == -1)
1131 if (fcgi_gen_response(c
, "&path=") == -1)
1133 if (fcgi_gen_response(c
, qs
->path
) == -1)
1136 if (fcgi_gen_response(c
, "&action=blame") == -1)
1139 if (fcgi_gen_response(c
, "&commit=") == -1)
1141 if (fcgi_gen_response(c
, rc
->commit_id
) == -1)
1144 if (fcgi_gen_response(c
, "&folder=") == -1)
1146 if (fcgi_gen_response(c
, qs
->folder
) == -1)
1149 if (fcgi_gen_response(c
, "&file=") == -1)
1151 if (fcgi_gen_response(c
, name
) == -1)
1154 if (fcgi_gen_response(c
, "'>") == -1)
1157 if (fcgi_gen_response(c
, "blame") == -1)
1159 if (fcgi_gen_response(c
, "</a>\n") == -1)
1162 if (fcgi_gen_response(c
, "</div>\n") == -1)
1164 if (fcgi_gen_response(c
, "</div>\n") == -1)
1170 build_folder
= NULL
;
1185 got_ref_list_free(&refs
);
1187 got_object_commit_close(commit
);
1193 const struct got_error
*
1194 got_output_file_blob(struct request
*c
)
1196 const struct got_error
*error
= NULL
;
1197 struct transport
*t
= c
->t
;
1198 struct got_repository
*repo
= t
->repo
;
1199 struct querystring
*qs
= c
->t
->qs
;
1200 struct got_commit_object
*commit
= NULL
;
1201 struct got_object_id
*commit_id
= NULL
;
1202 struct got_reflist_head refs
;
1203 struct got_blob_object
*blob
= NULL
;
1204 char *path
= NULL
, *in_repo_path
= NULL
;
1205 int obj_type
, set_mime
= 0, type
= 0, fd
= -1;
1206 char *buf_output
= NULL
;
1212 if (asprintf(&path
, "%s%s%s", qs
->folder
? qs
->folder
: "",
1213 qs
->folder
? "/" : "", qs
->file
) == -1) {
1214 error
= got_error_from_errno("asprintf");
1218 error
= got_repo_map_path(&in_repo_path
, repo
, path
);
1222 error
= got_repo_match_object_id(&commit_id
, NULL
, qs
->commit
,
1223 GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
1227 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
1231 error
= got_object_id_by_path(&commit_id
, repo
, commit
, in_repo_path
);
1235 if (commit_id
== NULL
) {
1236 error
= got_error(GOT_ERR_NO_OBJ
);
1240 error
= got_object_get_type(&obj_type
, repo
, commit_id
);
1244 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
1245 error
= got_error(GOT_ERR_OBJ_TYPE
);
1249 error
= got_gotweb_dupfd(&c
->priv_fd
[BLOB_FD_1
], &fd
);
1253 error
= got_object_open_as_blob(&blob
, repo
, commit_id
, BUF
, fd
);
1256 hdrlen
= got_object_blob_get_hdrlen(blob
);
1258 error
= got_object_blob_read_block(&len
, blob
);
1261 buf
= got_object_blob_get_read_buf(blob
);
1264 * Skip blob object header first time around,
1265 * which also contains a zero byte.
1268 if (set_mime
== 0) {
1269 if (isbinary(buf
, len
- hdrlen
)) {
1270 error
= gotweb_render_content_type_file(c
,
1271 "application/octet-stream",
1274 log_warnx("%s: %s", __func__
,
1280 error
= gotweb_render_content_type(c
,
1283 log_warnx("%s: %s", __func__
,
1292 buf_output
= calloc(len
- hdrlen
+ 1,
1293 sizeof(*buf_output
));
1294 if (buf_output
== NULL
) {
1295 error
= got_error_from_errno("calloc");
1298 memcpy(buf_output
, buf
, len
- hdrlen
);
1299 fcgi_gen_response(c
, buf_output
);
1303 fcgi_gen_binary_response(c
, buf
, len
- hdrlen
);
1309 got_object_commit_close(commit
);
1310 if (fd
!= -1 && close(fd
) == -1 && error
== NULL
)
1311 error
= got_error_from_errno("close");
1313 got_object_blob_close(blob
);
1325 char datebuf
[11]; /* YYYY-MM-DD + NUL */
1328 struct blame_cb_args
{
1329 struct blame_line
*lines
;
1333 off_t
*line_offsets
;
1335 struct got_repository
*repo
;
1339 static const struct got_error
*
1340 got_gotweb_blame_cb(void *arg
, int nlines
, int lineno
,
1341 struct got_commit_object
*commit
, struct got_object_id
*id
)
1343 const struct got_error
*err
= NULL
;
1344 struct blame_cb_args
*a
= arg
;
1345 struct blame_line
*bline
;
1346 struct request
*c
= a
->c
;
1347 struct transport
*t
= c
->t
;
1348 struct querystring
*qs
= t
->qs
;
1349 struct repo_dir
*repo_dir
= t
->repo_dir
;
1350 char *line
= NULL
, *eline
= NULL
;
1351 size_t linesize
= 0;
1354 time_t committer_time
;
1356 if (nlines
!= a
->nlines
||
1357 (lineno
!= -1 && lineno
< 1) || lineno
> a
->nlines
)
1358 return got_error(GOT_ERR_RANGE
);
1361 return NULL
; /* no change in this commit */
1363 /* Annotate this line. */
1364 bline
= &a
->lines
[lineno
- 1];
1365 if (bline
->annotated
)
1367 err
= got_object_id_str(&bline
->id_str
, id
);
1371 bline
->committer
= strdup(got_object_commit_get_committer(commit
));
1372 if (bline
->committer
== NULL
) {
1373 err
= got_error_from_errno("strdup");
1377 committer_time
= got_object_commit_get_committer_time(commit
);
1378 if (gmtime_r(&committer_time
, &tm
) == NULL
)
1379 return got_error_from_errno("gmtime_r");
1380 if (strftime(bline
->datebuf
, sizeof(bline
->datebuf
), "%G-%m-%d",
1382 err
= got_error(GOT_ERR_NO_SPACE
);
1385 bline
->annotated
= 1;
1387 /* Print lines annotated so far. */
1388 bline
= &a
->lines
[a
->lineno_cur
- 1];
1389 if (!bline
->annotated
)
1392 offset
= a
->line_offsets
[a
->lineno_cur
- 1];
1393 if (fseeko(a
->f
, offset
, SEEK_SET
) == -1) {
1394 err
= got_error_from_errno("fseeko");
1398 while (bline
->annotated
) {
1399 int out_buff_size
= 100;
1400 char *smallerthan
, *at
, *nl
, *committer
;
1401 char out_buff
[out_buff_size
];
1404 if (getline(&line
, &linesize
, a
->f
) == -1) {
1406 err
= got_error_from_errno("getline");
1410 committer
= bline
->committer
;
1411 smallerthan
= strchr(committer
, '<');
1412 if (smallerthan
&& smallerthan
[1] != '\0')
1413 committer
= smallerthan
+ 1;
1414 at
= strchr(committer
, '@');
1417 len
= strlen(committer
);
1419 committer
[8] = '\0';
1421 nl
= strchr(line
, '\n');
1425 if (fcgi_gen_response(c
, "<div id='blame_wrapper'>") == -1)
1427 if (fcgi_gen_response(c
, "<div id='blame_number'>") == -1)
1429 if (snprintf(out_buff
, strlen(out_buff
), "%.*d", a
->nlines_prec
,
1432 if (fcgi_gen_response(c
, out_buff
) == -1)
1434 if (fcgi_gen_response(c
, "</div>") == -1)
1437 if (fcgi_gen_response(c
, "<div id='blame_hash'>") == -1)
1440 if (fcgi_gen_response(c
, "<a href='?index_page=") == -1)
1442 if (fcgi_gen_response(c
, qs
->index_page_str
) == -1)
1444 if (fcgi_gen_response(c
, "&path=") == -1)
1446 if (fcgi_gen_response(c
, repo_dir
->name
) == -1)
1448 if (fcgi_gen_response(c
, "&action=diff&commit=") == -1)
1450 if (fcgi_gen_response(c
, bline
->id_str
) == -1)
1452 if (fcgi_gen_response(c
, "'>") == -1)
1454 if (snprintf(out_buff
, 10, "%.8s", bline
->id_str
) < 0)
1456 if (fcgi_gen_response(c
, out_buff
) == -1)
1458 if (fcgi_gen_response(c
, "</a></div>") == -1)
1461 if (fcgi_gen_response(c
, "<div id='blame_date'>") == -1)
1463 if (fcgi_gen_response(c
, bline
->datebuf
) == -1)
1465 if (fcgi_gen_response(c
, "</div>") == -1)
1468 if (fcgi_gen_response(c
, "<div id='blame_author'>") == -1)
1470 if (fcgi_gen_response(c
, committer
) == -1)
1472 if (fcgi_gen_response(c
, "</div>") == -1)
1475 if (fcgi_gen_response(c
, "<div id='blame_code'>") == -1)
1477 err
= gotweb_escape_html(&eline
, line
);
1480 if (fcgi_gen_response(c
, eline
) == -1)
1482 if (fcgi_gen_response(c
, "</div>") == -1)
1485 if (fcgi_gen_response(c
, "</div>") == -1)
1488 bline
= &a
->lines
[a
->lineno_cur
- 1];
1496 const struct got_error
*
1497 got_output_file_blame(struct request
*c
)
1499 const struct got_error
*error
= NULL
;
1500 struct transport
*t
= c
->t
;
1501 struct got_repository
*repo
= t
->repo
;
1502 struct querystring
*qs
= c
->t
->qs
;
1503 struct got_object_id
*obj_id
= NULL
, *commit_id
= NULL
;
1504 struct got_commit_object
*commit
= NULL
;
1505 struct got_reflist_head refs
;
1506 struct got_blob_object
*blob
= NULL
;
1507 char *path
= NULL
, *in_repo_path
= NULL
;
1508 struct blame_cb_args bca
;
1509 int i
, obj_type
, fd1
= -1, fd2
= -1, fd3
= -1, fd4
= -1, fd5
= -1;
1512 FILE *f1
= NULL
, *f2
= NULL
;
1518 if (asprintf(&path
, "%s%s%s", qs
->folder
? qs
->folder
: "",
1519 qs
->folder
? "/" : "", qs
->file
) == -1) {
1520 error
= got_error_from_errno("asprintf");
1524 error
= got_repo_map_path(&in_repo_path
, repo
, path
);
1528 error
= got_repo_match_object_id(&commit_id
, NULL
, qs
->commit
,
1529 GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
1533 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
1537 error
= got_object_id_by_path(&obj_id
, repo
, commit
, in_repo_path
);
1541 if (commit_id
== NULL
) {
1542 error
= got_error(GOT_ERR_NO_OBJ
);
1546 error
= got_object_get_type(&obj_type
, repo
, obj_id
);
1550 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
1551 error
= got_error(GOT_ERR_OBJ_TYPE
);
1555 error
= got_gotweb_openfile(&bca
.f
, &c
->priv_fd
[BLAME_FD_1
], &fd1
);
1559 error
= got_gotweb_dupfd(&c
->priv_fd
[BLAME_FD_2
], &fd2
);
1563 error
= got_object_open_as_blob(&blob
, repo
, obj_id
, BUF
, fd2
);
1567 error
= got_object_blob_dump_to_file(&filesize
, &bca
.nlines
,
1568 &bca
.line_offsets
, bca
.f
, blob
);
1569 if (error
|| bca
.nlines
== 0)
1572 /* Don't include \n at EOF in the blame line count. */
1573 if (bca
.line_offsets
[bca
.nlines
- 1] == filesize
)
1576 bca
.lines
= calloc(bca
.nlines
, sizeof(*bca
.lines
));
1577 if (bca
.lines
== NULL
) {
1578 error
= got_error_from_errno("calloc");
1582 bca
.nlines_prec
= 0;
1591 error
= got_gotweb_dupfd(&c
->priv_fd
[BLAME_FD_3
], &fd3
);
1595 error
= got_gotweb_dupfd(&c
->priv_fd
[BLAME_FD_4
], &fd4
);
1599 error
= got_gotweb_openfile(&f1
, &c
->priv_fd
[BLAME_FD_5
], &fd5
);
1603 error
= got_gotweb_openfile(&f2
, &c
->priv_fd
[BLAME_FD_6
], &fd6
);
1607 error
= got_blame(in_repo_path
, commit_id
, repo
,
1608 GOT_DIFF_ALGORITHM_MYERS
, got_gotweb_blame_cb
, &bca
, NULL
, NULL
,
1612 free(bca
.line_offsets
);
1613 for (i
= 0; i
< bca
.nlines
; i
++) {
1614 struct blame_line
*bline
= &bca
.lines
[i
];
1615 free(bline
->id_str
);
1616 free(bline
->committer
);
1621 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
1622 error
= got_error_from_errno("close");
1623 if (fd3
!= -1 && close(fd3
) == -1 && error
== NULL
)
1624 error
= got_error_from_errno("close");
1625 if (fd4
!= -1 && close(fd4
) == -1 && error
== NULL
)
1626 error
= got_error_from_errno("close");
1628 const struct got_error
*bca_err
=
1629 got_gotweb_flushfile(bca
.f
, fd1
);
1634 const struct got_error
*f1_err
=
1635 got_gotweb_flushfile(f1
, fd5
);
1640 const struct got_error
*f2_err
=
1641 got_gotweb_flushfile(f2
, fd6
);
1646 got_object_commit_close(commit
);
1648 got_object_blob_close(blob
);
1655 const struct got_error
*
1656 got_output_repo_diff(struct request
*c
)
1658 const struct got_error
*error
= NULL
;
1659 struct transport
*t
= c
->t
;
1660 struct got_repository
*repo
= t
->repo
;
1661 struct repo_commit
*rc
= NULL
;
1662 struct got_object_id
*id1
= NULL
, *id2
= NULL
;
1663 struct got_reflist_head refs
;
1664 FILE *f1
= NULL
, *f2
= NULL
, *f3
= NULL
;
1665 char *label1
= NULL
, *label2
= NULL
, *line
= NULL
;
1666 char *newline
, *eline
= NULL
, *color
= NULL
;
1667 int obj_type
, fd1
, fd2
, fd3
, fd4
= -1, fd5
= -1;
1668 size_t linesize
= 0;
1674 error
= got_gotweb_openfile(&f1
, &c
->priv_fd
[DIFF_FD_1
], &fd1
);
1678 error
= got_gotweb_openfile(&f2
, &c
->priv_fd
[DIFF_FD_2
], &fd2
);
1682 error
= got_gotweb_openfile(&f3
, &c
->priv_fd
[DIFF_FD_3
], &fd3
);
1686 rc
= TAILQ_FIRST(&t
->repo_commits
);
1688 if (rc
->parent_id
!= NULL
&&
1689 strncmp(rc
->parent_id
, "/dev/null", 9) != 0) {
1690 error
= got_repo_match_object_id(&id1
, &label1
,
1691 rc
->parent_id
, GOT_OBJ_TYPE_ANY
,
1697 error
= got_repo_match_object_id(&id2
, &label2
, rc
->commit_id
,
1698 GOT_OBJ_TYPE_ANY
, &refs
, repo
);
1702 error
= got_object_get_type(&obj_type
, repo
, id2
);
1706 error
= got_gotweb_dupfd(&c
->priv_fd
[DIFF_FD_4
], &fd4
);
1710 error
= got_gotweb_dupfd(&c
->priv_fd
[DIFF_FD_5
], &fd5
);
1715 case GOT_OBJ_TYPE_BLOB
:
1716 error
= got_diff_objects_as_blobs(NULL
, NULL
, f1
, f2
, fd4
, fd5
,
1717 id1
, id2
, NULL
, NULL
, GOT_DIFF_ALGORITHM_MYERS
, 3, 0, 0,
1720 case GOT_OBJ_TYPE_TREE
:
1721 error
= got_diff_objects_as_trees(NULL
, NULL
, f1
, f2
, fd4
, fd5
,
1722 id1
, id2
, NULL
, "", "", GOT_DIFF_ALGORITHM_MYERS
, 3, 0, 0,
1725 case GOT_OBJ_TYPE_COMMIT
:
1726 error
= got_diff_objects_as_commits(NULL
, NULL
, f1
, f2
, fd4
,
1727 fd5
, id1
, id2
, NULL
, GOT_DIFF_ALGORITHM_MYERS
, 3, 0, 0,
1731 error
= got_error(GOT_ERR_OBJ_TYPE
);
1736 if (fseek(f1
, 0, SEEK_SET
) == -1) {
1737 error
= got_ferror(f1
, GOT_ERR_IO
);
1741 if (fseek(f2
, 0, SEEK_SET
) == -1) {
1742 error
= got_ferror(f2
, GOT_ERR_IO
);
1746 if (fseek(f3
, 0, SEEK_SET
) == -1) {
1747 error
= got_ferror(f3
, GOT_ERR_IO
);
1751 while ((linelen
= getline(&line
, &linesize
, f3
)) != -1) {
1752 if (strncmp(line
, "-", 1) == 0) {
1753 color
= strdup("diff_minus");
1754 if (color
== NULL
) {
1755 error
= got_error_from_errno("strdup");
1758 } else if (strncmp(line
, "+", 1) == 0) {
1759 color
= strdup("diff_plus");
1760 if (color
== NULL
) {
1761 error
= got_error_from_errno("strdup");
1764 } else if (strncmp(line
, "@@", 2) == 0) {
1765 color
= strdup("diff_chunk_header");
1766 if (color
== NULL
) {
1767 error
= got_error_from_errno("strdup");
1770 } else if (strncmp(line
, "@@", 2) == 0) {
1771 color
= strdup("diff_chunk_header");
1772 if (color
== NULL
) {
1773 error
= got_error_from_errno("strdup");
1776 } else if (strncmp(line
, "commit +", 8) == 0) {
1777 color
= strdup("diff_meta");
1778 if (color
== NULL
) {
1779 error
= got_error_from_errno("strdup");
1782 } else if (strncmp(line
, "commit -", 8) == 0) {
1783 color
= strdup("diff_meta");
1784 if (color
== NULL
) {
1785 error
= got_error_from_errno("strdup");
1788 } else if (strncmp(line
, "blob +", 6) == 0) {
1789 color
= strdup("diff_meta");
1790 if (color
== NULL
) {
1791 error
= got_error_from_errno("strdup");
1794 } else if (strncmp(line
, "blob -", 6) == 0) {
1795 color
= strdup("diff_meta");
1796 if (color
== NULL
) {
1797 error
= got_error_from_errno("strdup");
1800 } else if (strncmp(line
, "file +", 6) == 0) {
1801 color
= strdup("diff_meta");
1802 if (color
== NULL
) {
1803 error
= got_error_from_errno("strdup");
1806 } else if (strncmp(line
, "file -", 6) == 0) {
1807 color
= strdup("diff_meta");
1808 if (color
== NULL
) {
1809 error
= got_error_from_errno("strdup");
1812 } else if (strncmp(line
, "from:", 5) == 0) {
1813 color
= strdup("diff_author");
1814 if (color
== NULL
) {
1815 error
= got_error_from_errno("strdup");
1818 } else if (strncmp(line
, "via:", 4) == 0) {
1819 color
= strdup("diff_author");
1820 if (color
== NULL
) {
1821 error
= got_error_from_errno("strdup");
1824 } else if (strncmp(line
, "date:", 5) == 0) {
1825 color
= strdup("diff_date");
1826 if (color
== NULL
) {
1827 error
= got_error_from_errno("strdup");
1831 if (fcgi_gen_response(c
, "<div id='diff_line' class='") == -1)
1833 if (fcgi_gen_response(c
, color
? color
: "") == -1)
1835 if (fcgi_gen_response(c
, "'>") == -1)
1837 newline
= strchr(line
, '\n');
1841 error
= gotweb_escape_html(&eline
, line
);
1844 if (fcgi_gen_response(c
, eline
) == -1)
1849 if (fcgi_gen_response(c
, "</div>\n") == -1)
1852 wrlen
= wrlen
+ linelen
;
1856 if (linelen
== -1 && ferror(f3
))
1857 error
= got_error_from_errno("getline");
1860 if (fd4
!= -1 && close(fd4
) == -1 && error
== NULL
)
1861 error
= got_error_from_errno("close");
1862 if (fd5
!= -1 && close(fd5
) == -1 && error
== NULL
)
1863 error
= got_error_from_errno("close");
1865 const struct got_error
*f1_err
=
1866 got_gotweb_flushfile(f1
, fd1
);
1871 const struct got_error
*f2_err
=
1872 got_gotweb_flushfile(f2
, fd2
);
1877 const struct got_error
*f3_err
=
1878 got_gotweb_flushfile(f3
, fd3
);
1882 got_ref_list_free(&refs
);
1892 static const struct got_error
*
1893 got_init_repo_commit(struct repo_commit
**rc
)
1895 const struct got_error
*error
= NULL
;
1897 *rc
= calloc(1, sizeof(**rc
));
1899 return got_error_from_errno2("%s: calloc", __func__
);
1902 (*rc
)->refs_str
= NULL
;
1903 (*rc
)->commit_id
= NULL
;
1904 (*rc
)->committer
= NULL
;
1905 (*rc
)->author
= NULL
;
1906 (*rc
)->parent_id
= NULL
;
1907 (*rc
)->tree_id
= NULL
;
1908 (*rc
)->commit_msg
= NULL
;
1913 static const struct got_error
*
1914 got_init_repo_tag(struct repo_tag
**rt
)
1916 const struct got_error
*error
= NULL
;
1918 *rt
= calloc(1, sizeof(**rt
));
1920 return got_error_from_errno2("%s: calloc", __func__
);
1922 (*rt
)->commit_id
= NULL
;
1923 (*rt
)->tag_name
= NULL
;
1924 (*rt
)->tag_commit
= NULL
;
1925 (*rt
)->commit_msg
= NULL
;
1926 (*rt
)->tagger
= NULL
;