1 /*
2  * libgit2 "ls-files" example - shows how to view all files currently in the index
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.ls_files;
15 
16 
17 private static import core.stdc.stdio;
18 private static import core.stdc.stdlib;
19 private static import core.stdc.string;
20 private static import libgit2_d.index;
21 private static import libgit2_d.repository;
22 private static import libgit2_d.types;
23 
24 package:
25 
26 /**
27  * This example demonstrates the libgit2 index APIs to roughly
28  * simulate the output of `git ls-files`.
29  * `git ls-files` has many options and this currently does not show them.
30  *
31  * `git ls-files` base command shows all paths in the index at that time.
32  * This includes staged and committed files, but unstaged files will not display.
33  *
34  * This currently supports the default behavior and the `--error-unmatch` option.
35  */
36 
37 public struct ls_options
38 {
39 	int error_unmatch;
40 	char*[1024] files;
41 	size_t file_count;
42 }
43 
44 nothrow @nogc
45 private void usage(const (char)* message, const (char)* arg)
46 
47 	in
48 	{
49 	}
50 
51 	do
52 	{
53 		if ((message != null) && (arg != null)) {
54 			core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "%s: %s\n", message, arg);
55 		} else if (message != null) {
56 			core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "%s\n", message);
57 		}
58 
59 		core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "usage: ls-files [--error-unmatch] [--] [<file>...]\n");
60 		core.stdc.stdlib.exit(1);
61 	}
62 
63 nothrow @nogc
64 private int parse_options(.ls_options* opts, int argc, char** argv)
65 
66 	in
67 	{
68 	}
69 
70 	do
71 	{
72 		core.stdc..string.memset(opts, 0, .ls_options.sizeof);
73 
74 		if (argc < 2) {
75 			return 0;
76 		}
77 
78 		int parsing_files = 0;
79 
80 		for (int i = 1; i < argc; ++i) {
81 			char* a = argv[i];
82 
83 			/* if it doesn't start with a '-' or is after the '--' then it is a file */
84 			if ((a[0] != '-') || (parsing_files)) {
85 				parsing_files = 1;
86 
87 				/* watch for overflows (just in case) */
88 				if (opts.file_count == 1024) {
89 					core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "ls-files can only support 1024 files at this time.\n");
90 
91 					return -1;
92 				}
93 
94 				opts.files[opts.file_count++] = a;
95 			} else if (!core.stdc..string.strcmp(a, "--")) {
96 				parsing_files = 1;
97 			} else if (!core.stdc..string.strcmp(a, "--error-unmatch")) {
98 				opts.error_unmatch = 1;
99 			} else {
100 				.usage("Unsupported argument", a);
101 
102 				return -1;
103 			}
104 		}
105 
106 		return 0;
107 	}
108 
109 nothrow @nogc
110 private int print_paths(.ls_options* opts, libgit2_d.types.git_index* index)
111 
112 	in
113 	{
114 	}
115 
116 	do
117 	{
118 		const (libgit2_d.index.git_index_entry)* entry;
119 
120 		/* if there are no files explicitly listed by the user print all entries in the index */
121 		if (opts.file_count == 0) {
122 			size_t entry_count = libgit2_d.index.git_index_entrycount(index);
123 
124 			for (size_t i = 0; i < entry_count; i++) {
125 				entry = libgit2_d.index.git_index_get_byindex(index, i);
126 				core.stdc.stdio.puts(entry.path);
127 			}
128 
129 			return 0;
130 		}
131 
132 		/* loop through the files found in the args and print them if they exist */
133 		for (size_t i = 0; i < opts.file_count; ++i) {
134 			const (char)* path = opts.files[i];
135 			entry = libgit2_d.index.git_index_get_bypath(index, path, libgit2_d.index.git_index_stage_t.GIT_INDEX_STAGE_NORMAL);
136 
137 			if (entry != null) {
138 				core.stdc.stdio.puts(path);
139 			} else if (opts.error_unmatch) {
140 				core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "error: pathspec '%s' did not match any file(s) known to git.\n", path);
141 				core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "Did you forget to 'git add'?\n");
142 
143 				return -1;
144 			}
145 		}
146 
147 		return 0;
148 	}
149 
150 extern (C)
151 nothrow @nogc
152 //int lg2_ls_files(libgit2_d.types.git_repository* repo, int argc, char*[] argv)
153 public int lg2_ls_files(libgit2_d.types.git_repository* repo, int argc, char** argv)
154 
155 	in
156 	{
157 	}
158 
159 	do
160 	{
161 		.ls_options opts;
162 		int error = .parse_options(&opts, argc, argv);
163 
164 		if (error < 0) {
165 			return error;
166 		}
167 
168 		libgit2_d.types.git_index* index = null;
169 
170 		scope (exit) {
171 			libgit2_d.index.git_index_free(index);
172 		}
173 
174 		error = libgit2_d.repository.git_repository_index(&index, repo);
175 
176 		if (error < 0) {
177 			return error;
178 		}
179 
180 		error = .print_paths(&opts, index);
181 
182 		return error;
183 	}