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 }