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 }