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 //Declaration name in C language
167 public enum
168 {
169 	SHOW_TYPE = .catfile_mode.SHOW_TYPE,
170 	SHOW_SIZE = .catfile_mode.SHOW_SIZE,
171 	SHOW_NONE = .catfile_mode.SHOW_NONE,
172 	SHOW_PRETTY = .catfile_mode.SHOW_PRETTY,
173 }
174 
175 /**
176  * Forward declarations for option-parsing helper
177  */
178 public struct catfile_options
179 {
180 	const (char)* dir;
181 	const (char)* rev;
182 	.catfile_mode action = cast(.catfile_mode)(0);
183 	int verbose;
184 }
185 
186 /**
187  * Entry point for this command
188  */
189 extern (C)
190 nothrow @nogc
191 //int lg2_cat_file(libgit2_d.types.git_repository* repo, int argc, char*[] argv)
192 public int lg2_cat_file(libgit2_d.types.git_repository* repo, int argc, char** argv)
193 
194 	in
195 	{
196 	}
197 
198 	do
199 	{
200 		.catfile_options o = {".", null, 0, 0};
201 
202 		.parse_opts(&o, argc, argv);
203 
204 		libgit2_d.types.git_object* obj = null;
205 		libgit2_d.example.common.check_lg2(libgit2_d.revparse.git_revparse_single(&obj, repo, o.rev), "Could not resolve", o.rev);
206 
207 		char[libgit2_d.oid.GIT_OID_HEXSZ + 1] oidstr;
208 
209 		if (o.verbose) {
210 			libgit2_d.oid.git_oid_tostr(&(oidstr[0]), oidstr.length, libgit2_d.object.git_object_id(obj));
211 
212 			core.stdc.stdio.printf("%s %s\n--\n", libgit2_d.object.git_object_type2string(libgit2_d.object.git_object_type(obj)), &(oidstr[0]));
213 		}
214 
215 		switch (o.action) {
216 			case .catfile_mode.SHOW_TYPE:
217 				core.stdc.stdio.printf("%s\n", libgit2_d.object.git_object_type2string(libgit2_d.object.git_object_type(obj)));
218 
219 				break;
220 
221 			case .catfile_mode.SHOW_SIZE:
222 				libgit2_d.types.git_odb* odb;
223 				libgit2_d.types.git_odb_object* odbobj;
224 
225 				libgit2_d.example.common.check_lg2(libgit2_d.repository.git_repository_odb(&odb, repo), "Could not open ODB", null);
226 				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);
227 
228 				core.stdc.stdio.printf("%ld\n", cast(core.stdc.config.c_long)(libgit2_d.odb.git_odb_object_size(odbobj)));
229 
230 				libgit2_d.odb.git_odb_object_free(odbobj);
231 				libgit2_d.odb.git_odb_free(odb);
232 
233 				break;
234 
235 			case .catfile_mode.SHOW_NONE:
236 				/* just want return result */
237 				break;
238 
239 			case .catfile_mode.SHOW_PRETTY:
240 				switch (libgit2_d.object.git_object_type(obj)) {
241 					case libgit2_d.types.git_object_t.GIT_OBJECT_BLOB:
242 						.show_blob(cast(const (libgit2_d.types.git_blob)*)(obj));
243 
244 						break;
245 
246 					case libgit2_d.types.git_object_t.GIT_OBJECT_COMMIT:
247 						.show_commit(cast(const (libgit2_d.types.git_commit)*)(obj));
248 
249 						break;
250 
251 					case libgit2_d.types.git_object_t.GIT_OBJECT_TREE:
252 						.show_tree(cast(const (libgit2_d.types.git_tree)*)(obj));
253 
254 						break;
255 
256 					case libgit2_d.types.git_object_t.GIT_OBJECT_TAG:
257 						.show_tag(cast(const (libgit2_d.types.git_tag)*)(obj));
258 
259 						break;
260 
261 					default:
262 						core.stdc.stdio.printf("unknown %s\n", &(oidstr[0]));
263 
264 						break;
265 				}
266 
267 				break;
268 
269 			default:
270 				break;
271 		}
272 
273 		libgit2_d.object.git_object_free(obj);
274 
275 		return 0;
276 	}
277 
278 /**
279  * Print out usage information
280  */
281 nothrow @nogc
282 private void usage(const (char)* message, const (char)* arg)
283 
284 	in
285 	{
286 	}
287 
288 	do
289 	{
290 		if ((message != null) && (arg != null)) {
291 			core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "%s: %s\n", message, arg);
292 		} else if (message != null) {
293 			core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "%s\n", message);
294 		}
295 
296 		core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "usage: cat-file (-t | -s | -e | -p) [-v] [-q] [-h|--help] [--git-dir=<dir>] <object>\n");
297 		core.stdc.stdlib.exit(1);
298 	}
299 
300 /**
301  * Parse the command-line options taken from git
302  */
303 nothrow @nogc
304 private void parse_opts(.catfile_options* o, int argc, char** argv)
305 
306 	in
307 	{
308 	}
309 
310 	do
311 	{
312 		libgit2_d.example.args.args_info args = libgit2_d.example.args.ARGS_INFO_INIT(argc, argv);
313 
314 		for (args.pos = 1; args.pos < argc; ++args.pos) {
315 			char* a = argv[args.pos];
316 
317 			if (a[0] != '-') {
318 				if (o.rev != null) {
319 					.usage("Only one rev should be provided", null);
320 				} else {
321 					o.rev = a;
322 				}
323 			} else if (!core.stdc..string.strcmp(a, "-t")) {
324 				o.action = .catfile_mode.SHOW_TYPE;
325 			} else if (!core.stdc..string.strcmp(a, "-s")) {
326 				o.action = .catfile_mode.SHOW_SIZE;
327 			} else if (!core.stdc..string.strcmp(a, "-e")) {
328 				o.action = .catfile_mode.SHOW_NONE;
329 			} else if (!core.stdc..string.strcmp(a, "-p")) {
330 				o.action = .catfile_mode.SHOW_PRETTY;
331 			} else if (!core.stdc..string.strcmp(a, "-q")) {
332 				o.verbose = 0;
333 			} else if (!core.stdc..string.strcmp(a, "-v")) {
334 				o.verbose = 1;
335 			} else if ((!core.stdc..string.strcmp(a, "--help")) || (!core.stdc..string.strcmp(a, "-h"))) {
336 				.usage(null, null);
337 			} else if (!libgit2_d.example.args.match_str_arg(&o.dir, &args, "--git-dir")) {
338 				.usage("Unknown option", a);
339 			}
340 		}
341 
342 		if ((!o.action) || (!o.rev)) {
343 			.usage(null, null);
344 		}
345 	}