summaryrefslogtreecommitdiffstatshomepage
path: root/ui-patch.c
blob: fbb92cc4eca31de7cf9d998c3cec46a0e1937ccb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* ui-patch.c: generate patch view
 *
 * Copyright (C) 2007 Lars Hjemli
 *
 * Licensed under GNU General Public License v2
 *   (see COPYING for full license text)
 */

#include "cgit.h"
#include "ui-patch.h"
#include "html.h"
#include "ui-shared.h"

static void print_line(char *line, int len)
{
	char c = line[len-1];

	line[len-1] = '\0';
	htmlf("%s\n", line);
	line[len-1] = c;
}

static void header(unsigned char *sha1, char *path1, int mode1,
		   unsigned char *sha2, char *path2, int mode2)
{
	char *abbrev1, *abbrev2;
	int subproject;

	subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
	htmlf("diff --git a/%s b/%s\n", path1, path2);

	if (mode1 == 0)
		htmlf("new file mode %.6o\n", mode2);

	if (mode2 == 0)
		htmlf("deleted file mode %.6o\n", mode1);

	if (!subproject) {
		abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
		abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
		htmlf("index %s..%s", abbrev1, abbrev2);
		free(abbrev1);
		free(abbrev2);
		if (mode1 != 0 && mode2 != 0) {
			htmlf(" %.6o", mode1);
			if (mode2 != mode1)
				htmlf("..%.6o", mode2);
		}

		if (is_null_sha1(sha1)) {
			path1 = "dev/null";
			htmlf("\n--- /%s\n", path1);
		} else
			htmlf("\n--- a/%s\n", path1);

		if (is_null_sha1(sha2)) {
			path2 = "dev/null";
			htmlf("+++ /%s\n", path2);
		} else
			htmlf("+++ b/%s\n", path2);
	}
}

static void filepair_cb(struct diff_filepair *pair)
{
	unsigned long old_size = 0;
	unsigned long new_size = 0;
	int binary = 0;

	header(pair->one->sha1, pair->one->path, pair->one->mode,
	       pair->two->sha1, pair->two->path, pair->two->mode);
	if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
		if (S_ISGITLINK(pair->one->mode))
			print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
		if (S_ISGITLINK(pair->two->mode))
			print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
		return;
	}
	if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
			    &new_size, &binary, 0, 0, print_line))
		html("Error running diff");
	if (binary)
		html("Binary files differ\n");
}

void cgit_print_patch(char *hex, const char *prefix)
{
	struct commit *commit;
	struct commitinfo *info;
	unsigned char sha1[20], old_sha1[20];
	char *patchname;

	if (!hex)
		hex = ctx.qry.head;

	if (get_sha1(hex, sha1)) {
		cgit_print_error("Bad object id: %s", hex);
		return;
	}
	commit = lookup_commit_reference(sha1);
	if (!commit) {
		cgit_print_error("Bad commit reference: %s", hex);
		return;
	}
	info = cgit_parse_commit(commit);

	if (commit->parents && commit->parents->item)
		hashcpy(old_sha1, commit->parents->item->object.sha1);
	else
		hashclr(old_sha1);

	patchname = fmt("%s.patch", sha1_to_hex(sha1));
	ctx.page.mimetype = "text/plain";
	ctx.page.filename = patchname;
	cgit_print_http_headers(&ctx);
	htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1));
	htmlf("From: %s", info->author);
	if (!ctx.cfg.noplainemail) {
		htmlf(" %s", info->author_email);
	}
	html("\n");
	html("Date: ");
	cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n", ctx.cfg.local_time);
	htmlf("Subject: %s\n\n", info->subject);
	if (info->msg && *info->msg) {
		htmlf("%s", info->msg);
		if (info->msg[strlen(info->msg) - 1] != '\n')
			html("\n");
	}
	html("---\n");
	if (prefix)
		htmlf("(limited to '%s')\n\n", prefix);
	cgit_diff_tree(old_sha1, sha1, filepair_cb, prefix, 0);
	html("--\n");
	htmlf("cgit %s\n", cgit_version);
	cgit_free_commitinfo(info);
}
class="w"> '/'); if (slash) *(slash + 1) = 0; else fullpath = NULL; html("<li>"); cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.sha1, fullpath); html("</li>\n"); } free(fullpath); } static void print_dir_entry(const unsigned char *sha1, const char *base, int baselen, const char *path, unsigned mode) { char *fullpath; fullpath = buildpath(base, baselen, path); if (!S_ISDIR(mode) && !S_ISGITLINK(mode)) fullpath[strlen(fullpath) - 1] = 0; html(" <li>"); if (S_ISGITLINK(mode)) { cgit_submodule_link(NULL, fullpath, sha1_to_hex(sha1)); } else cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, fullpath); html("</li>\n"); free(fullpath); } static void print_dir_tail(void) { html(" </ul>\n</body></html>\n"); } static int walk_tree(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; if (baselen == walk_tree_ctx->match_baselen) { if (S_ISREG(mode)) { if (print_object(sha1, pathname)) walk_tree_ctx->match = 1; } else if (S_ISDIR(mode)) { print_dir(sha1, base, baselen, pathname); walk_tree_ctx->match = 2; return READ_TREE_RECURSIVE; } } else if (baselen > walk_tree_ctx->match_baselen) { print_dir_entry(sha1, base, baselen, pathname, mode); walk_tree_ctx->match = 2; } else if (S_ISDIR(mode)) { return READ_TREE_RECURSIVE; } return 0; } static int basedir_len(const char *path) { char *p = strrchr(path, '/'); if (p) return p - path + 1; return 0; } void cgit_print_plain(struct cgit_context *ctx) { const char *rev = ctx->qry.sha1; unsigned char sha1[20]; struct commit *commit; struct pathspec_item path_items = { .match = ctx->qry.path, .len = ctx->qry.path ? strlen(ctx->qry.path) : 0 }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match = 0 }; if (!rev) rev = ctx->qry.head; if (get_sha1(rev, sha1)) { html_status(404, "Not found", 0); return; } commit = lookup_commit_reference(sha1); if (!commit || parse_commit(commit)) { html_status(404, "Not found", 0); return; } if (!path_items.match) { path_items.match = ""; walk_tree_ctx.match_baselen = -1; print_dir(commit->tree->object.sha1, "", 0, ""); walk_tree_ctx.match = 2; } else walk_tree_ctx.match_baselen = basedir_len(path_items.match); read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); if (!walk_tree_ctx.match) html_status(404, "Not found", 0); else if (walk_tree_ctx.match == 2) print_dir_tail(); }