treewide: be explicit about dependence on gettext.h
[git/debian.git] / builtin / bugreport.c
blobb61cfa9464f8ea4f7009866fab763b84f0228b5a
1 #include "builtin.h"
2 #include "gettext.h"
3 #include "parse-options.h"
4 #include "strbuf.h"
5 #include "help.h"
6 #include "compat/compiler.h"
7 #include "hook.h"
8 #include "hook-list.h"
9 #include "diagnose.h"
12 static void get_system_info(struct strbuf *sys_info)
14 struct utsname uname_info;
15 char *shell = NULL;
17 /* get git version from native cmd */
18 strbuf_addstr(sys_info, _("git version:\n"));
19 get_version_info(sys_info, 1);
21 /* system call for other version info */
22 strbuf_addstr(sys_info, "uname: ");
23 if (uname(&uname_info))
24 strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
25 strerror(errno),
26 errno);
27 else
28 strbuf_addf(sys_info, "%s %s %s %s\n",
29 uname_info.sysname,
30 uname_info.release,
31 uname_info.version,
32 uname_info.machine);
34 strbuf_addstr(sys_info, _("compiler info: "));
35 get_compiler_info(sys_info);
37 strbuf_addstr(sys_info, _("libc info: "));
38 get_libc_info(sys_info);
40 shell = getenv("SHELL");
41 strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n",
42 shell ? shell : "<unset>");
45 static void get_populated_hooks(struct strbuf *hook_info, int nongit)
47 const char **p;
49 if (nongit) {
50 strbuf_addstr(hook_info,
51 _("not run from a git repository - no hooks to show\n"));
52 return;
55 for (p = hook_name_list; *p; p++) {
56 const char *hook = *p;
58 if (hook_exists(hook))
59 strbuf_addf(hook_info, "%s\n", hook);
63 static const char * const bugreport_usage[] = {
64 N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
65 " [--diagnose[=<mode>]]"),
66 NULL
69 static int get_bug_template(struct strbuf *template)
71 const char template_text[] = N_(
72 "Thank you for filling out a Git bug report!\n"
73 "Please answer the following questions to help us understand your issue.\n"
74 "\n"
75 "What did you do before the bug happened? (Steps to reproduce your issue)\n"
76 "\n"
77 "What did you expect to happen? (Expected behavior)\n"
78 "\n"
79 "What happened instead? (Actual behavior)\n"
80 "\n"
81 "What's different between what you expected and what actually happened?\n"
82 "\n"
83 "Anything else you want to add:\n"
84 "\n"
85 "Please review the rest of the bug report below.\n"
86 "You can delete any lines you don't wish to share.\n");
88 strbuf_addstr(template, _(template_text));
89 return 0;
92 static void get_header(struct strbuf *buf, const char *title)
94 strbuf_addf(buf, "\n\n[%s]\n", title);
97 int cmd_bugreport(int argc, const char **argv, const char *prefix)
99 struct strbuf buffer = STRBUF_INIT;
100 struct strbuf report_path = STRBUF_INIT;
101 int report = -1;
102 time_t now = time(NULL);
103 struct tm tm;
104 enum diagnose_mode diagnose = DIAGNOSE_NONE;
105 char *option_output = NULL;
106 char *option_suffix = "%Y-%m-%d-%H%M";
107 const char *user_relative_path = NULL;
108 char *prefixed_filename;
109 size_t output_path_len;
110 int ret;
112 const struct option bugreport_options[] = {
113 OPT_CALLBACK_F(0, "diagnose", &diagnose, N_("mode"),
114 N_("create an additional zip archive of detailed diagnostics (default 'stats')"),
115 PARSE_OPT_OPTARG, option_parse_diagnose),
116 OPT_STRING('o', "output-directory", &option_output, N_("path"),
117 N_("specify a destination for the bugreport file(s)")),
118 OPT_STRING('s', "suffix", &option_suffix, N_("format"),
119 N_("specify a strftime format suffix for the filename(s)")),
120 OPT_END()
123 argc = parse_options(argc, argv, prefix, bugreport_options,
124 bugreport_usage, 0);
126 /* Prepare the path to put the result */
127 prefixed_filename = prefix_filename(prefix,
128 option_output ? option_output : "");
129 strbuf_addstr(&report_path, prefixed_filename);
130 strbuf_complete(&report_path, '/');
131 output_path_len = report_path.len;
133 strbuf_addstr(&report_path, "git-bugreport-");
134 strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
135 strbuf_addstr(&report_path, ".txt");
137 switch (safe_create_leading_directories(report_path.buf)) {
138 case SCLD_OK:
139 case SCLD_EXISTS:
140 break;
141 default:
142 die(_("could not create leading directories for '%s'"),
143 report_path.buf);
146 /* Prepare diagnostics, if requested */
147 if (diagnose != DIAGNOSE_NONE) {
148 struct strbuf zip_path = STRBUF_INIT;
149 strbuf_add(&zip_path, report_path.buf, output_path_len);
150 strbuf_addstr(&zip_path, "git-diagnostics-");
151 strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0);
152 strbuf_addstr(&zip_path, ".zip");
154 if (create_diagnostics_archive(&zip_path, diagnose))
155 die_errno(_("unable to create diagnostics archive %s"), zip_path.buf);
157 strbuf_release(&zip_path);
160 /* Prepare the report contents */
161 get_bug_template(&buffer);
163 get_header(&buffer, _("System Info"));
164 get_system_info(&buffer);
166 get_header(&buffer, _("Enabled Hooks"));
167 get_populated_hooks(&buffer, !startup_info->have_repository);
169 /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
170 report = xopen(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
172 if (write_in_full(report, buffer.buf, buffer.len) < 0)
173 die_errno(_("unable to write to %s"), report_path.buf);
175 close(report);
178 * We want to print the path relative to the user, but we still need the
179 * path relative to us to give to the editor.
181 if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
182 user_relative_path = report_path.buf;
183 fprintf(stderr, _("Created new report at '%s'.\n"),
184 user_relative_path);
186 free(prefixed_filename);
187 strbuf_release(&buffer);
189 ret = !!launch_editor(report_path.buf, NULL, NULL);
190 strbuf_release(&report_path);
191 return ret;