Line | Count | Source (jump to first uncovered line) |
1 | | #define USE_THE_REPOSITORY_VARIABLE |
2 | | |
3 | | #include "git-compat-util.h" |
4 | | #include "abspath.h" |
5 | | #include "advice.h" |
6 | | #include "config.h" |
7 | | #include "editor.h" |
8 | | #include "environment.h" |
9 | | #include "gettext.h" |
10 | | #include "pager.h" |
11 | | #include "path.h" |
12 | | #include "strbuf.h" |
13 | | #include "strvec.h" |
14 | | #include "run-command.h" |
15 | | #include "sigchain.h" |
16 | | |
17 | | #ifndef DEFAULT_EDITOR |
18 | 0 | #define DEFAULT_EDITOR "vi" |
19 | | #endif |
20 | | |
21 | | int is_terminal_dumb(void) |
22 | 0 | { |
23 | 0 | const char *terminal = getenv("TERM"); |
24 | 0 | return !terminal || !strcmp(terminal, "dumb"); |
25 | 0 | } |
26 | | |
27 | | const char *git_editor(void) |
28 | 0 | { |
29 | 0 | const char *editor = getenv("GIT_EDITOR"); |
30 | 0 | int terminal_is_dumb = is_terminal_dumb(); |
31 | |
|
32 | 0 | if (!editor && editor_program) |
33 | 0 | editor = editor_program; |
34 | 0 | if (!editor && !terminal_is_dumb) |
35 | 0 | editor = getenv("VISUAL"); |
36 | 0 | if (!editor) |
37 | 0 | editor = getenv("EDITOR"); |
38 | |
|
39 | 0 | if (!editor && terminal_is_dumb) |
40 | 0 | return NULL; |
41 | | |
42 | 0 | if (!editor) |
43 | 0 | editor = DEFAULT_EDITOR; |
44 | |
|
45 | 0 | return editor; |
46 | 0 | } |
47 | | |
48 | | const char *git_sequence_editor(void) |
49 | 0 | { |
50 | 0 | const char *editor = getenv("GIT_SEQUENCE_EDITOR"); |
51 | |
|
52 | 0 | if (!editor) |
53 | 0 | git_config_get_string_tmp("sequence.editor", &editor); |
54 | 0 | if (!editor) |
55 | 0 | editor = git_editor(); |
56 | |
|
57 | 0 | return editor; |
58 | 0 | } |
59 | | |
60 | | static int launch_specified_editor(const char *editor, const char *path, |
61 | | struct strbuf *buffer, const char *const *env) |
62 | 0 | { |
63 | 0 | if (!editor) |
64 | 0 | return error("Terminal is dumb, but EDITOR unset"); |
65 | | |
66 | 0 | if (strcmp(editor, ":")) { |
67 | 0 | struct strbuf realpath = STRBUF_INIT; |
68 | 0 | struct child_process p = CHILD_PROCESS_INIT; |
69 | 0 | int ret, sig; |
70 | 0 | int print_waiting_for_editor = advice_enabled(ADVICE_WAITING_FOR_EDITOR) && isatty(2); |
71 | |
|
72 | 0 | if (print_waiting_for_editor) { |
73 | | /* |
74 | | * A dumb terminal cannot erase the line later on. Add a |
75 | | * newline to separate the hint from subsequent output. |
76 | | * |
77 | | * Make sure that our message is separated with a whitespace |
78 | | * from further cruft that may be written by the editor. |
79 | | */ |
80 | 0 | const char term = is_terminal_dumb() ? '\n' : ' '; |
81 | |
|
82 | 0 | fprintf(stderr, |
83 | 0 | _("hint: Waiting for your editor to close the file...%c"), |
84 | 0 | term); |
85 | 0 | fflush(stderr); |
86 | 0 | } |
87 | |
|
88 | 0 | strbuf_realpath(&realpath, path, 1); |
89 | |
|
90 | 0 | strvec_pushl(&p.args, editor, realpath.buf, NULL); |
91 | 0 | if (env) |
92 | 0 | strvec_pushv(&p.env, (const char **)env); |
93 | 0 | p.use_shell = 1; |
94 | 0 | p.trace2_child_class = "editor"; |
95 | 0 | if (start_command(&p) < 0) { |
96 | 0 | strbuf_release(&realpath); |
97 | 0 | return error("unable to start editor '%s'", editor); |
98 | 0 | } |
99 | | |
100 | 0 | sigchain_push(SIGINT, SIG_IGN); |
101 | 0 | sigchain_push(SIGQUIT, SIG_IGN); |
102 | 0 | ret = finish_command(&p); |
103 | 0 | strbuf_release(&realpath); |
104 | 0 | sig = ret - 128; |
105 | 0 | sigchain_pop(SIGINT); |
106 | 0 | sigchain_pop(SIGQUIT); |
107 | 0 | if (sig == SIGINT || sig == SIGQUIT) |
108 | 0 | raise(sig); |
109 | 0 | if (print_waiting_for_editor && !is_terminal_dumb()) |
110 | | /* |
111 | | * Erase the entire line to avoid wasting the |
112 | | * vertical space. |
113 | | */ |
114 | 0 | term_clear_line(); |
115 | 0 | if (ret) |
116 | 0 | return error("there was a problem with the editor '%s'", |
117 | 0 | editor); |
118 | 0 | } |
119 | | |
120 | 0 | if (!buffer) |
121 | 0 | return 0; |
122 | 0 | if (strbuf_read_file(buffer, path, 0) < 0) |
123 | 0 | return error_errno("could not read file '%s'", path); |
124 | 0 | return 0; |
125 | 0 | } |
126 | | |
127 | | int launch_editor(const char *path, struct strbuf *buffer, const char *const *env) |
128 | 0 | { |
129 | 0 | return launch_specified_editor(git_editor(), path, buffer, env); |
130 | 0 | } |
131 | | |
132 | | int launch_sequence_editor(const char *path, struct strbuf *buffer, |
133 | | const char *const *env) |
134 | 0 | { |
135 | 0 | return launch_specified_editor(git_sequence_editor(), path, buffer, env); |
136 | 0 | } |
137 | | |
138 | | int strbuf_edit_interactively(struct repository *r, |
139 | | struct strbuf *buffer, const char *path, |
140 | | const char *const *env) |
141 | 0 | { |
142 | 0 | struct strbuf sb = STRBUF_INIT; |
143 | 0 | int fd, res = 0; |
144 | |
|
145 | 0 | if (!is_absolute_path(path)) { |
146 | 0 | strbuf_repo_git_path(&sb, r, "%s", path); |
147 | 0 | path = sb.buf; |
148 | 0 | } |
149 | |
|
150 | 0 | fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666); |
151 | 0 | if (fd < 0) |
152 | 0 | res = error_errno(_("could not open '%s' for writing"), path); |
153 | 0 | else if (write_in_full(fd, buffer->buf, buffer->len) < 0) { |
154 | 0 | res = error_errno(_("could not write to '%s'"), path); |
155 | 0 | close(fd); |
156 | 0 | } else if (close(fd) < 0) |
157 | 0 | res = error_errno(_("could not close '%s'"), path); |
158 | 0 | else { |
159 | 0 | strbuf_reset(buffer); |
160 | 0 | if (launch_editor(path, buffer, env) < 0) |
161 | 0 | res = error_errno(_("could not edit '%s'"), path); |
162 | 0 | unlink(path); |
163 | 0 | } |
164 | |
|
165 | 0 | strbuf_release(&sb); |
166 | 0 | return res; |
167 | 0 | } |