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