1 /*
2  * libgit2 "cat-file" example - shows how to print data from the ODB
3  *
4  * Written by the libgit2 contributors
5  *
6  * To the extent possible under law, the author(s) have dedicated all copyright
7  * and related and neighboring rights to this software to the public domain
8  * worldwide. This software is distributed without any warranty.
9  *
10  * You should have received a copy of the CC0 Public Domain Dedication along
11  * with this software. If not, see
12  * <http://creativecommons.org/publicdomain/zero/1.0/>.
13  */
14 module libgit2_d.example.cat_file;
15 
16 
17 private static import core.stdc.config;
18 private static import core.stdc.stdio;
19 private static import core.stdc.stdlib;
20 private static import core.stdc.string;
21 private static import libgit2_d.blob;
22 private static import libgit2_d.commit;
23 private static import libgit2_d.example.args;
24 private static import libgit2_d.example.common;
25 private static import libgit2_d.object;
26 private static import libgit2_d.odb;
27 private static import libgit2_d.oid;
28 private static import libgit2_d.repository;
29 private static import libgit2_d.revparse;
30 private static import libgit2_d.tag;
31 private static import libgit2_d.tree;
32 private static import libgit2_d.types;
33 
34 package:
35 
36 nothrow @nogc
37 private void print_signature(const (char)* header, const (libgit2_d.types.git_signature)* sig)
38 
39 	in
40 	{
41 	}
42 
43 	do
44 	{
45 		if (sig == null) {
46 			return;
47 		}
48 
49 		int offset = sig.when.offset;
50 		char sign;
51 
52 		if (offset < 0) {
53 			sign = '-';
54 			offset = -offset;
55 		} else {
56 			sign = '+';
57 		}
58 
59 		int hours = offset / 60;
60 		int minutes = offset % 60;
61 
62 		core.stdc.stdio.printf("%s %s <%s> %ld %c%02d%02d\n", header, sig.name, sig.email, cast(core.stdc.config.c_long)(sig.when.time), sign, hours, minutes);
63 	}
64 
65 /**
66  * Printing out a blob is simple, get the contents and print
67  */
68 nothrow @nogc
69 private void show_blob(const (libgit2_d.types.git_blob)* blob)
70 
71 	in
72 	{
73 	}
74 
75 	do
76 	{
77 		/* ? Does this need crlf filtering? */
78 		core.stdc.stdio.fwrite(libgit2_d.blob.git_blob_rawcontent(blob), cast(size_t)(libgit2_d.blob.git_blob_rawsize(blob)), 1, core.stdc.stdio.stdout);
79 	}
80 
81 /**
82  * Show each entry with its type, id and attributes
83  */
84 nothrow @nogc
85 private void show_tree(const (libgit2_d.types.git_tree)* tree)
86 
87 	in
88 	{
89 	}
90 
91 	do
92 	{
93 		size_t max_i = cast(int)(libgit2_d.tree.git_tree_entrycount(tree));
94 		char[libgit2_d.oid.GIT_OID_HEXSZ + 1] oidstr;
95 
96 		for (size_t i = 0; i < max_i; ++i) {
97 			const (libgit2_d.types.git_tree_entry)* te = libgit2_d.tree.git_tree_entry_byindex(tree, i);
98 
99 			libgit2_d.oid.git_oid_tostr(&(oidstr[0]), oidstr.length, libgit2_d.tree.git_tree_entry_id(te));
100 
101 			core.stdc.stdio.printf("%06o %s %s\t%s\n", libgit2_d.tree.git_tree_entry_filemode(te), libgit2_d.object.git_object_type2string(libgit2_d.tree.git_tree_entry_type(te)), &(oidstr[0]), libgit2_d.tree.git_tree_entry_name(te));
102 		}
103 	}
104 
105 /**
106  * Commits and tags have a few interesting fields in their header.
107  */
108 nothrow @nogc
109 private void show_commit(const (libgit2_d.types.git_commit)* commit)
110 
111 	in
112 	{
113 	}
114 
115 	do
116 	{
117 		char[libgit2_d.oid.GIT_OID_HEXSZ + 1] oidstr;
118 		libgit2_d.oid.git_oid_tostr(&(oidstr[0]), oidstr.length, libgit2_d.commit.git_commit_tree_id(commit));
119 		core.stdc.stdio.printf("tree %s\n", &(oidstr[0]));
120 
121 		uint max_i = cast(uint)(libgit2_d.commit.git_commit_parentcount(commit));
122 
123 		for (uint i = 0; i < max_i; ++i) {
124 			libgit2_d.oid.git_oid_tostr(&(oidstr[0]), oidstr.length, libgit2_d.commit.git_commit_parent_id(commit, i));
125 			core.stdc.stdio.printf("parent %s\n", &(oidstr[0]));
126 		}
127 
128 		.print_signature("author", libgit2_d.commit.git_commit_author(commit));
129 		.print_signature("committer", libgit2_d.commit.git_commit_committer(commit));
130 
131 		if (libgit2_d.commit.git_commit_message(commit)) {
132 			core.stdc.stdio.printf("\n%s\n", libgit2_d.commit.git_commit_message(commit));
133 		}
134 	}
135 
136 nothrow @nogc
137 private void show_tag(const (libgit2_d.types.git_tag)* tag)
138 
139 	in
140 	{
141 	}
142 
143 	do
144 	{
145 		char[libgit2_d.oid.GIT_OID_HEXSZ + 1] oidstr;
146 		libgit2_d.oid.git_oid_tostr(&(oidstr[0]), oidstr.length, libgit2_d.tag.git_tag_target_id(tag));
147 
148 		core.stdc.stdio.printf("object %s\n", &(oidstr[0]));
149 		core.stdc.stdio.printf("type %s\n", libgit2_d.object.git_object_type2string(libgit2_d.tag.git_tag_target_type(tag)));
150 		core.stdc.stdio.printf("tag %s\n", libgit2_d.tag.git_tag_name(tag));
151 		.print_signature("tagger", libgit2_d.tag.git_tag_tagger(tag));
152 
153 		if (libgit2_d.tag.git_tag_message(tag)) {
154 			core.stdc.stdio.printf("\n%s\n", libgit2_d.tag.git_tag_message(tag));
155 		}
156 	}
157 
158 public enum catfile_mode
159 {
160 	SHOW_TYPE = 1,
161 	SHOW_SIZE = 2,
162 	SHOW_NONE = 3,
163 	SHOW_PRETTY = 4,
164 }
165 
166 /**
167  * Forward declarations for option-parsing helper
168  */
169 public struct catfile_options
170 {
171 	const (char)* dir;
172 	const (char)* rev;
173 
174 	version (none) {
175 		.catfile_mode action;
176 	} else {
177 		int action;
178 	}
179 
180 	int verbose;
181 }
182 
183 /**
184  * Entry point for this command
185  */
186 extern (C)
187 nothrow @nogc
188 //int lg2_cat_file(libgit2_d.types.git_repository* repo, int argc, char*[] argv)
189 public int lg2_cat_file(libgit2_d.types.git_repository* repo, int argc, char** argv)
190 
191 	in
192 	{
193 	}
194 
195 	do
196 	{
197 		.catfile_options o = {".", null, 0, 0};
198 
199 		.parse_opts(&o, argc, argv);
200 
201 		libgit2_d.types.git_object* obj = null;
202 		libgit2_d.example.common.check_lg2(libgit2_d.revparse.git_revparse_single(&obj, repo, o.rev), "Could not resolve", o.rev);
203 
204 		char[libgit2_d.oid.GIT_OID_HEXSZ + 1] oidstr;
205 
206 		if (o.verbose) {
207 			libgit2_d.oid.git_oid_tostr(&(oidstr[0]), oidstr.length, libgit2_d.object.git_object_id(obj));
208 
209 			core.stdc.stdio.printf("%s %s\n--\n", libgit2_d.object.git_object_type2string(libgit2_d.object.git_object_type(obj)), &(oidstr[0]));
210 		}
211 
212 		switch (o.action) {
213 			case .catfile_mode.SHOW_TYPE:
214 				core.stdc.stdio.printf("%s\n", libgit2_d.object.git_object_type2string(libgit2_d.object.git_object_type(obj)));
215 
216 				break;
217 
218 			case .catfile_mode.SHOW_SIZE:
219 				libgit2_d.types.git_odb* odb;
220 				libgit2_d.types.git_odb_object* odbobj;
221 
222 				libgit2_d.example.common.check_lg2(libgit2_d.repository.git_repository_odb(&odb, repo), "Could not open ODB", null);
223 				libgit2_d.example.common.check_lg2(libgit2_d.odb.git_odb_read(&odbobj, odb, libgit2_d.object.git_object_id(obj)), "Could not find obj", null);
224 
225 				core.stdc.stdio.printf("%ld\n", cast(core.stdc.config.c_long)(libgit2_d.odb.git_odb_object_size(odbobj)));
226 
227 				libgit2_d.odb.git_odb_object_free(odbobj);
228 				libgit2_d.odb.git_odb_free(odb);
229 
230 				break;
231 
232 			case .catfile_mode.SHOW_NONE:
233 				/* just want return result */
234 				break;
235 
236 			case .catfile_mode.SHOW_PRETTY:
237 				switch (libgit2_d.object.git_object_type(obj)) {
238 					case libgit2_d.types.git_object_t.GIT_OBJECT_BLOB:
239 						.show_blob(cast(const (libgit2_d.types.git_blob)*)(obj));
240 
241 						break;
242 
243 					case libgit2_d.types.git_object_t.GIT_OBJECT_COMMIT:
244 						.show_commit(cast(const (libgit2_d.types.git_commit)*)(obj));
245 
246 						break;
247 
248 					case libgit2_d.types.git_object_t.GIT_OBJECT_TREE:
249 						.show_tree(cast(const (libgit2_d.types.git_tree)*)(obj));
250 
251 						break;
252 
253 					case libgit2_d.types.git_object_t.GIT_OBJECT_TAG:
254 						.show_tag(cast(const (libgit2_d.types.git_tag)*)(obj));
255 
256 						break;
257 
258 					default:
259 						core.stdc.stdio.printf("unknown %s\n", &(oidstr[0]));
260 
261 						break;
262 				}
263 
264 				break;
265 
266 			default:
267 				break;
268 		}
269 
270 		libgit2_d.object.git_object_free(obj);
271 
272 		return 0;
273 	}
274 
275 /**
276  * Print out usage information
277  */
278 nothrow @nogc
279 private void usage(const (char)* message, const (char)* arg)
280 
281 	in
282 	{
283 	}
284 
285 	do
286 	{
287 		if ((message != null) && (arg != null)) {
288 			core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "%s: %s\n", message, arg);
289 		} else if (message != null) {
290 			core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "%s\n", message);
291 		}
292 
293 		core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "usage: cat-file (-t | -s | -e | -p) [-v] [-q] [-h|--help] [--git-dir=<dir>] <object>\n");
294 		core.stdc.stdlib.exit(1);
295 	}
296 
297 /**
298  * Parse the command-line options taken from git
299  */
300 nothrow @nogc
301 private void parse_opts(.catfile_options* o, int argc, char** argv)
302 
303 	in
304 	{
305 	}
306 
307 	do
308 	{
309 		libgit2_d.example.args.args_info args = libgit2_d.example.args.ARGS_INFO_INIT(argc, argv);
310 
311 		for (args.pos = 1; args.pos < argc; ++args.pos) {
312 			char* a = argv[args.pos];
313 
314 			if (a[0] != '-') {
315 				if (o.rev != null) {
316 					.usage("Only one rev should be provided", null);
317 				} else {
318 					o.rev = a;
319 				}
320 			} else if (!core.stdc..string.strcmp(a, "-t")) {
321 				o.action = .catfile_mode.SHOW_TYPE;
322 			} else if (!core.stdc..string.strcmp(a, "-s")) {
323 				o.action = .catfile_mode.SHOW_SIZE;
324 			} else if (!core.stdc..string.strcmp(a, "-e")) {
325 				o.action = .catfile_mode.SHOW_NONE;
326 			} else if (!core.stdc..string.strcmp(a, "-p")) {
327 				o.action = .catfile_mode.SHOW_PRETTY;
328 			} else if (!core.stdc..string.strcmp(a, "-q")) {
329 				o.verbose = 0;
330 			} else if (!core.stdc..string.strcmp(a, "-v")) {
331 				o.verbose = 1;
332 			} else if ((!core.stdc..string.strcmp(a, "--help")) || (!core.stdc..string.strcmp(a, "-h"))) {
333 				.usage(null, null);
334 			} else if (!libgit2_d.example.args.match_str_arg(&o.dir, &args, "--git-dir")) {
335 				.usage("Unknown option", a);
336 			}
337 		}
338 
339 		if ((!o.action) || (!o.rev)) {
340 			.usage(null, null);
341 		}
342 	}