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 }