blob: b67b802ddf8493ea25ccbb1fc5c046240bfe917d [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"
Stephan Beyerd82f33e2008-07-25 18:28:41 +020014
Jonathan Nieder8f4b5762009-10-30 20:44:41 -050015#ifndef DEFAULT_EDITOR
16#define DEFAULT_EDITOR "vi"
17#endif
18
Lars Schneidera64f2132017-11-29 15:37:51 +010019int is_terminal_dumb(void)
20{
21 const char *terminal = getenv("TERM");
22 return !terminal || !strcmp(terminal, "dumb");
23}
24
Jonathan Nieder44fcb492009-11-11 18:01:27 -060025const char *git_editor(void)
Stephan Beyerd82f33e2008-07-25 18:28:41 +020026{
Jonathan Niederd33738d2009-11-11 17:56:07 -060027 const char *editor = getenv("GIT_EDITOR");
Lars Schneidera64f2132017-11-29 15:37:51 +010028 int terminal_is_dumb = is_terminal_dumb();
Stephan Beyerd82f33e2008-07-25 18:28:41 +020029
Stephan Beyerd82f33e2008-07-25 18:28:41 +020030 if (!editor && editor_program)
31 editor = editor_program;
Jonathan Niederd33738d2009-11-11 17:56:07 -060032 if (!editor && !terminal_is_dumb)
Stephan Beyerd82f33e2008-07-25 18:28:41 +020033 editor = getenv("VISUAL");
34 if (!editor)
35 editor = getenv("EDITOR");
36
Jonathan Niederd33738d2009-11-11 17:56:07 -060037 if (!editor && terminal_is_dumb)
Jonathan Nieder44fcb492009-11-11 18:01:27 -060038 return NULL;
Stephan Beyerd82f33e2008-07-25 18:28:41 +020039
40 if (!editor)
Jonathan Nieder8f4b5762009-10-30 20:44:41 -050041 editor = DEFAULT_EDITOR;
Stephan Beyerd82f33e2008-07-25 18:28:41 +020042
Jonathan Nieder44fcb492009-11-11 18:01:27 -060043 return editor;
44}
45
Alban Gruin2aed0182018-08-10 18:51:30 +020046const char *git_sequence_editor(void)
Jonathan Nieder44fcb492009-11-11 18:01:27 -060047{
Alban Gruin2aed0182018-08-10 18:51:30 +020048 const char *editor = getenv("GIT_SEQUENCE_EDITOR");
Jonathan Nieder44fcb492009-11-11 18:01:27 -060049
50 if (!editor)
Jeff Kingf1de9812020-08-14 12:17:36 -040051 git_config_get_string_tmp("sequence.editor", &editor);
Alban Gruin2aed0182018-08-10 18:51:30 +020052 if (!editor)
53 editor = git_editor();
54
55 return editor;
56}
57
58static int launch_specified_editor(const char *editor, const char *path,
59 struct strbuf *buffer, const char *const *env)
60{
61 if (!editor)
Jonathan Nieder44fcb492009-11-11 18:01:27 -060062 return error("Terminal is dumb, but EDITOR unset");
63
Stephan Beyerd82f33e2008-07-25 18:28:41 +020064 if (strcmp(editor, ":")) {
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000065 struct strbuf realpath = STRBUF_INIT;
René Scharfed3180272014-08-19 21:09:35 +020066 struct child_process p = CHILD_PROCESS_INIT;
Jeff King12508572012-11-30 17:41:50 -050067 int ret, sig;
Ben Boeckeled9bff02021-08-23 12:44:00 +020068 int print_waiting_for_editor = advice_enabled(ADVICE_WAITING_FOR_EDITOR) && isatty(2);
Lars Schneiderabfb04d2017-12-07 16:16:41 +010069
70 if (print_waiting_for_editor) {
71 /*
72 * A dumb terminal cannot erase the line later on. Add a
73 * newline to separate the hint from subsequent output.
74 *
75 * Make sure that our message is separated with a whitespace
76 * from further cruft that may be written by the editor.
77 */
78 const char term = is_terminal_dumb() ? '\n' : ' ';
79
80 fprintf(stderr,
81 _("hint: Waiting for your editor to close the file...%c"),
82 term);
83 fflush(stderr);
84 }
Stephan Beyerd82f33e2008-07-25 18:28:41 +020085
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000086 strbuf_realpath(&realpath, path, 1);
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000087
Ævar Arnfjörð Bjarmason2b709892021-11-25 23:52:20 +010088 strvec_pushl(&p.args, editor, realpath.buf, NULL);
Ævar Arnfjörð Bjarmasonc7c4bde2021-11-25 23:52:24 +010089 if (env)
Ævar Arnfjörð Bjarmason29fda242022-06-02 11:09:50 +020090 strvec_pushv(&p.env, (const char **)env);
Jeff Kingf42ca312012-11-30 17:41:04 -050091 p.use_shell = 1;
Jeff Hostetlereee73d12019-02-22 14:25:04 -080092 p.trace2_child_class = "editor";
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000093 if (start_command(&p) < 0) {
94 strbuf_release(&realpath);
Jeff Kingf42ca312012-11-30 17:41:04 -050095 return error("unable to start editor '%s'", editor);
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +000096 }
Jeff Kingf42ca312012-11-30 17:41:04 -050097
Paul Fox913ef362012-11-30 17:41:26 -050098 sigchain_push(SIGINT, SIG_IGN);
99 sigchain_push(SIGQUIT, SIG_IGN);
100 ret = finish_command(&p);
Alexandr Miloslavskiy3d7747e2020-03-10 13:11:22 +0000101 strbuf_release(&realpath);
Jeff King709ca732013-01-05 09:49:49 -0500102 sig = ret - 128;
Paul Fox913ef362012-11-30 17:41:26 -0500103 sigchain_pop(SIGINT);
104 sigchain_pop(SIGQUIT);
Jeff King12508572012-11-30 17:41:50 -0500105 if (sig == SIGINT || sig == SIGQUIT)
106 raise(sig);
Paul Fox913ef362012-11-30 17:41:26 -0500107 if (ret)
Stephan Beyer71982032008-07-25 18:28:42 +0200108 return error("There was a problem with the editor '%s'.",
109 editor);
Lars Schneiderabfb04d2017-12-07 16:16:41 +0100110
111 if (print_waiting_for_editor && !is_terminal_dumb())
112 /*
SZEDER Gáborcd1096b2019-06-24 20:13:16 +0200113 * Erase the entire line to avoid wasting the
114 * vertical space.
Lars Schneiderabfb04d2017-12-07 16:16:41 +0100115 */
SZEDER Gáborcd1096b2019-06-24 20:13:16 +0200116 term_clear_line();
Stephan Beyerd82f33e2008-07-25 18:28:41 +0200117 }
118
119 if (!buffer)
Stephan Beyer71982032008-07-25 18:28:42 +0200120 return 0;
Stephan Beyerd82f33e2008-07-25 18:28:41 +0200121 if (strbuf_read_file(buffer, path, 0) < 0)
Nguyễn Thái Ngọc Duy9f9a5222016-05-08 16:47:43 +0700122 return error_errno("could not read file '%s'", path);
Stephan Beyer71982032008-07-25 18:28:42 +0200123 return 0;
Stephan Beyerd82f33e2008-07-25 18:28:41 +0200124}
Alban Gruin2aed0182018-08-10 18:51:30 +0200125
126int launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
127{
128 return launch_specified_editor(git_editor(), path, buffer, env);
129}
130
131int launch_sequence_editor(const char *path, struct strbuf *buffer,
132 const char *const *env)
133{
134 return launch_specified_editor(git_sequence_editor(), path, buffer, env);
135}
Elijah Newren4e120822023-04-11 00:41:57 -0700136
137int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
138 const char *const *env)
139{
140 char *path2 = NULL;
141 int fd, res = 0;
142
143 if (!is_absolute_path(path))
144 path = path2 = xstrdup(git_path("%s", path));
145
146 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
147 if (fd < 0)
148 res = error_errno(_("could not open '%s' for writing"), path);
149 else if (write_in_full(fd, buffer->buf, buffer->len) < 0) {
150 res = error_errno(_("could not write to '%s'"), path);
151 close(fd);
152 } else if (close(fd) < 0)
153 res = error_errno(_("could not close '%s'"), path);
154 else {
155 strbuf_reset(buffer);
156 if (launch_editor(path, buffer, env) < 0)
157 res = error_errno(_("could not edit '%s'"), path);
158 unlink(path);
159 }
160
161 free(path2);
162 return res;
163}