blob: 38c5dbbb79b96ca15431141bf70b84799149eb40 [file] [log] [blame]
Elijah Newren77f091e2023-04-11 00:42:00 -07001#include "git-compat-util.h"
Elijah Newren0b027f62023-03-21 06:25:58 +00002#include "abspath.h"
Elijah Newren6c6ddf92023-04-11 03:00:39 +00003#include "advice.h"
Alban Gruin2aed0182018-08-10 18:51:30 +02004#include "config.h"
Elijah Newren4e120822023-04-11 00:41:57 -07005#include "editor.h"
Elijah Newren32a8f512023-03-21 06:26:03 +00006#include "environment.h"
Elijah Newrenf394e092023-03-21 06:25:54 +00007#include "gettext.h"
Elijah Newrenca4eed72023-04-11 00:41:59 -07008#include "pager.h"
Elijah Newrend1cbe1e2023-04-22 20:17:20 +00009#include "path.h"
Stephan Beyerd82f33e2008-07-25 18:28:41 +020010#include "strbuf.h"
Ævar Arnfjörð Bjarmasonc7c4bde2021-11-25 23:52:24 +010011#include "strvec.h"
Stephan Beyerd82f33e2008-07-25 18:28:41 +020012#include "run-command.h"
Paul Fox913ef362012-11-30 17:41:26 -050013#include "sigchain.h"
Elijah Newren4e120822023-04-11 00:41:57 -070014#include "wrapper.h"
Stephan Beyerd82f33e2008-07-25 18:28:41 +020015
Jonathan Nieder8f4b5762009-10-30 20:44:41 -050016#ifndef DEFAULT_EDITOR
17#define DEFAULT_EDITOR "vi"
18#endif
19
Lars Schneidera64f2132017-11-29 15:37:51 +010020int is_terminal_dumb(void)
21{
22 const char *terminal = getenv("TERM");
23 return !terminal || !strcmp(terminal, "dumb");
24}
25
Jonathan Nieder44fcb492009-11-11 18:01:27 -060026const char *git_editor(void)
Stephan Beyerd82f33e2008-07-25 18:28:41 +020027{
Jonathan Niederd33738d2009-11-11 17:56:07 -060028 const char *editor = getenv("GIT_EDITOR");
Lars Schneidera64f2132017-11-29 15:37:51 +010029 int terminal_is_dumb = is_terminal_dumb();
Stephan Beyerd82f33e2008-07-25 18:28:41 +020030
Stephan Beyerd82f33e2008-07-25 18:28:41 +020031 if (!editor && editor_program)
32 editor = editor_program;
Jonathan Niederd33738d2009-11-11 17:56:07 -060033 if (!editor && !terminal_is_dumb)
Stephan Beyerd82f33e2008-07-25 18:28:41 +020034 editor = getenv("VISUAL");
35 if (!editor)
36 editor = getenv("EDITOR");
37
Jonathan Niederd33738d2009-11-11 17:56:07 -060038 if (!editor && terminal_is_dumb)
Jonathan Nieder44fcb492009-11-11 18:01:27 -060039 return NULL;
Stephan Beyerd82f33e2008-07-25 18:28:41 +020040
41 if (!editor)
Jonathan Nieder8f4b5762009-10-30 20:44:41 -050042 editor = DEFAULT_EDITOR;
Stephan Beyerd82f33e2008-07-25 18:28:41 +020043
Jonathan Nieder44fcb492009-11-11 18:01:27 -060044 return editor;
45}
46
Alban Gruin2aed0182018-08-10 18:51:30 +020047const char *git_sequence_editor(void)
Jonathan Nieder44fcb492009-11-11 18:01:27 -060048{
Alban Gruin2aed0182018-08-10 18:51:30 +020049 const char *editor = getenv("GIT_SEQUENCE_EDITOR");
Jonathan Nieder44fcb492009-11-11 18:01:27 -060050
51 if (!editor)
Jeff Kingf1de9812020-08-14 12:17:36 -040052 git_config_get_string_tmp("sequence.editor", &editor);
Alban Gruin2aed0182018-08-10 18:51:30 +020053 if (!editor)
54 editor = git_editor();
55
56 return editor;
57}
58
59static int launch_specified_editor(const char *editor, const char *path,
60 struct strbuf *buffer, const char *const *env)
61{
62 if (!editor)
Jonathan Nieder44fcb492009-11-11 18:01:27 -060063 return error("Terminal is dumb, but EDITOR unset");
64
Stephan Beyerd82f33e2008-07-25 18:28:41 +020065 if (strcmp(editor, ":")) {
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000066 struct strbuf realpath = STRBUF_INIT;
René Scharfed3180272014-08-19 21:09:35 +020067 struct child_process p = CHILD_PROCESS_INIT;
Jeff King12508572012-11-30 17:41:50 -050068 int ret, sig;
Ben Boeckeled9bff02021-08-23 12:44:00 +020069 int print_waiting_for_editor = advice_enabled(ADVICE_WAITING_FOR_EDITOR) && isatty(2);
Lars Schneiderabfb04d2017-12-07 16:16:41 +010070
71 if (print_waiting_for_editor) {
72 /*
73 * A dumb terminal cannot erase the line later on. Add a
74 * newline to separate the hint from subsequent output.
75 *
76 * Make sure that our message is separated with a whitespace
77 * from further cruft that may be written by the editor.
78 */
79 const char term = is_terminal_dumb() ? '\n' : ' ';
80
81 fprintf(stderr,
82 _("hint: Waiting for your editor to close the file...%c"),
83 term);
84 fflush(stderr);
85 }
Stephan Beyerd82f33e2008-07-25 18:28:41 +020086
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000087 strbuf_realpath(&realpath, path, 1);
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000088
Ævar Arnfjörð Bjarmason2b709892021-11-25 23:52:20 +010089 strvec_pushl(&p.args, editor, realpath.buf, NULL);
Ævar Arnfjörð Bjarmasonc7c4bde2021-11-25 23:52:24 +010090 if (env)
Ævar Arnfjörð Bjarmason29fda242022-06-02 11:09:50 +020091 strvec_pushv(&p.env, (const char **)env);
Jeff Kingf42ca312012-11-30 17:41:04 -050092 p.use_shell = 1;
Jeff Hostetlereee73d12019-02-22 14:25:04 -080093 p.trace2_child_class = "editor";
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000094 if (start_command(&p) < 0) {
95 strbuf_release(&realpath);
Jeff Kingf42ca312012-11-30 17:41:04 -050096 return error("unable to start editor '%s'", editor);
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000097 }
Jeff Kingf42ca312012-11-30 17:41:04 -050098
Paul Fox913ef362012-11-30 17:41:26 -050099 sigchain_push(SIGINT, SIG_IGN);
100 sigchain_push(SIGQUIT, SIG_IGN);
101 ret = finish_command(&p);
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +0000102 strbuf_release(&realpath);
Jeff King709ca732013-01-05 09:49:49 -0500103 sig = ret - 128;
Paul Fox913ef362012-11-30 17:41:26 -0500104 sigchain_pop(SIGINT);
105 sigchain_pop(SIGQUIT);
Jeff King12508572012-11-30 17:41:50 -0500106 if (sig == SIGINT || sig == SIGQUIT)
107 raise(sig);
Paul Fox913ef362012-11-30 17:41:26 -0500108 if (ret)
Stephan Beyer71982032008-07-25 18:28:42 +0200109 return error("There was a problem with the editor '%s'.",
110 editor);
Lars Schneiderabfb04d2017-12-07 16:16:41 +0100111
112 if (print_waiting_for_editor && !is_terminal_dumb())
113 /*
SZEDER Gáborcd1096b2019-06-24 20:13:16 +0200114 * Erase the entire line to avoid wasting the
115 * vertical space.
Lars Schneiderabfb04d2017-12-07 16:16:41 +0100116 */
SZEDER Gáborcd1096b2019-06-24 20:13:16 +0200117 term_clear_line();
Stephan Beyerd82f33e2008-07-25 18:28:41 +0200118 }
119
120 if (!buffer)
Stephan Beyer71982032008-07-25 18:28:42 +0200121 return 0;
Stephan Beyerd82f33e2008-07-25 18:28:41 +0200122 if (strbuf_read_file(buffer, path, 0) < 0)
Nguyễn Thái Ngọc Duy9f9a5222016-05-08 16:47:43 +0700123 return error_errno("could not read file '%s'", path);
Stephan Beyer71982032008-07-25 18:28:42 +0200124 return 0;
Stephan Beyerd82f33e2008-07-25 18:28:41 +0200125}
Alban Gruin2aed0182018-08-10 18:51:30 +0200126
127int launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
128{
129 return launch_specified_editor(git_editor(), path, buffer, env);
130}
131
132int launch_sequence_editor(const char *path, struct strbuf *buffer,
133 const char *const *env)
134{
135 return launch_specified_editor(git_sequence_editor(), path, buffer, env);
136}
Elijah Newren4e120822023-04-11 00:41:57 -0700137
138int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
139 const char *const *env)
140{
141 char *path2 = NULL;
142 int fd, res = 0;
143
144 if (!is_absolute_path(path))
145 path = path2 = xstrdup(git_path("%s", path));
146
147 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
148 if (fd < 0)
149 res = error_errno(_("could not open '%s' for writing"), path);
150 else if (write_in_full(fd, buffer->buf, buffer->len) < 0) {
151 res = error_errno(_("could not write to '%s'"), path);
152 close(fd);
153 } else if (close(fd) < 0)
154 res = error_errno(_("could not close '%s'"), path);
155 else {
156 strbuf_reset(buffer);
157 if (launch_editor(path, buffer, env) < 0)
158 res = error_errno(_("could not edit '%s'"), path);
159 unlink(path);
160 }
161
162 free(path2);
163 return res;
164}