Unify view refresh checking
[tig.git] / src / io.c
blob0b1d3c5c0843c0a7285aef7bd626d6632cdb7b3b
1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/util.h"
16 #include "tig/io.h"
19 * Encoding conversion.
22 #define ENCODING_SEP ": encoding: "
23 #define ENCODING_ARG "--encoding=" ENCODING_UTF8
25 #define CHARSET_SEP "; charset="
27 struct encoding {
28 struct encoding *next;
29 iconv_t cd;
30 char fromcode[1];
33 char encoding_arg[] = ENCODING_ARG;
34 struct encoding *default_encoding;
35 static struct encoding *encodings;
37 struct encoding *
38 encoding_open(const char *fromcode)
40 struct encoding *encoding;
41 size_t len = strlen(fromcode);
43 if (!*fromcode)
44 return NULL;
46 for (encoding = encodings; encoding; encoding = encoding->next) {
47 if (!strcasecmp(encoding->fromcode, fromcode))
48 return encoding;
51 encoding = calloc(1, sizeof(*encoding) + len);
52 strncpy(encoding->fromcode, fromcode, len);
53 encoding->cd = iconv_open(ENCODING_UTF8, fromcode);
54 if (encoding->cd == ICONV_NONE) {
55 free(encoding);
56 return NULL;
59 encoding->next = encodings;
60 encodings = encoding;
62 return encoding;
65 static char *
66 encoding_convert_string(iconv_t iconv_cd, char *line, size_t linelen)
68 static char out_buffer[BUFSIZ * 2];
69 ICONV_CONST char *inbuf = line;
70 size_t inlen = linelen + 1;
72 char *outbuf = out_buffer;
73 size_t outlen = sizeof(out_buffer);
75 size_t ret = iconv(iconv_cd, &inbuf, &inlen, &outbuf, &outlen);
77 return (ret != (size_t) -1) ? out_buffer : line;
80 char *
81 encoding_convert(struct encoding *encoding, char *line)
83 return encoding_convert_string(encoding->cd, line, strlen(line));
86 const char *
87 encoding_iconv(iconv_t iconv_cd, const char *string, size_t length)
89 char *instr = strndup(string, length);
90 const char *ret = instr ? encoding_convert_string(iconv_cd, instr, length) : string;
92 free(instr);
93 return ret == instr ? string : ret;
96 struct encoding *
97 get_path_encoding(const char *path, struct encoding *default_encoding)
99 const char *check_attr_argv[] = {
100 "git", "check-attr", "encoding", "--", path, NULL
102 char buf[SIZEOF_STR];
103 char *encoding;
105 /* <path>: encoding: <encoding> */
107 if (!*path || !io_run_buf(check_attr_argv, buf, sizeof(buf))
108 || !(encoding = strstr(buf, ENCODING_SEP)))
109 return default_encoding;
111 encoding += STRING_SIZE(ENCODING_SEP);
112 if (!strcmp(encoding, ENCODING_UTF8)
113 || !strcmp(encoding, "unspecified")
114 || !strcmp(encoding, "set")) {
115 const char *file_argv[] = {
116 "file", "-I", "--", path, NULL
119 if (!*path || !io_run_buf(file_argv, buf, sizeof(buf))
120 || !(encoding = strstr(buf, CHARSET_SEP)))
121 return default_encoding;
123 encoding += STRING_SIZE(CHARSET_SEP);
126 return encoding_open(encoding);
130 * Executing external commands.
133 static void
134 io_init(struct io *io)
136 memset(io, 0, sizeof(*io));
137 io->pipe = -1;
140 bool
141 io_open(struct io *io, const char *fmt, ...)
143 char name[SIZEOF_STR] = "";
144 int retval;
146 io_init(io);
148 FORMAT_BUFFER(name, sizeof(name), fmt, retval, FALSE);
149 if (retval < 0) {
150 io->error = ENAMETOOLONG;
151 return FALSE;
154 io->pipe = *name ? open(name, O_RDONLY) : dup(STDIN_FILENO);
155 if (io->pipe == -1)
156 io->error = errno;
157 return io->pipe != -1;
160 bool
161 io_kill(struct io *io)
163 return io->pid == 0 || kill(io->pid, SIGKILL) != -1;
166 bool
167 io_done(struct io *io)
169 pid_t pid = io->pid;
171 if (io->pipe != -1)
172 close(io->pipe);
173 free(io->buf);
174 io_init(io);
176 while (pid > 0) {
177 int status;
178 pid_t waiting = waitpid(pid, &status, 0);
180 if (waiting < 0) {
181 if (errno == EINTR)
182 continue;
183 io->error = errno;
184 return FALSE;
187 if (WEXITSTATUS(status)) {
188 io->status = WEXITSTATUS(status);
191 return waiting == pid &&
192 !WIFSIGNALED(status) &&
193 !io->status;
196 return TRUE;
199 static int
200 open_trace(int devnull, const char *argv[])
202 static const char *trace_file;
204 if (!trace_file) {
205 trace_file = getenv("TIG_TRACE");
206 if (!trace_file)
207 trace_file = "";
210 if (*trace_file) {
211 int fd = open(trace_file, O_RDWR | O_CREAT | O_APPEND, 0666);
212 int i;
214 for (i = 0; argv[i]; i++) {
215 if (write(fd, argv[i], strlen(argv[i])) == -1
216 || write(fd, " ", 1) == -1)
217 break;
219 if (argv[i] || write(fd, "\n", 1) == -1) {
220 close(fd);
221 return devnull;
224 return fd;
227 return devnull;
230 bool
231 io_trace(const char *fmt, ...)
233 static FILE *trace_out; /* Intensionally leaked. */
234 va_list args;
235 int retval;
237 if (!trace_out) {
238 const char *trace_file = getenv("TIG_TRACE");
240 if (trace_file)
241 trace_out = fopen(trace_file, "a");
242 if (!trace_out)
243 return FALSE;
246 va_start(args, fmt);
247 retval = vfprintf(trace_out, fmt, args);
248 va_end(args);
249 fflush(trace_out);
251 return retval != -1;
254 bool
255 io_exec(struct io *io, enum io_type type, const char *dir, char * const env[], const char *argv[], int custom)
257 int pipefds[2] = { -1, -1 };
258 bool read_from_stdin = type == IO_RD && (custom & IO_RD_FORWARD_STDIN);
259 bool read_with_stderr = type == IO_RD && (custom & IO_RD_WITH_STDERR);
261 io_init(io);
263 if (dir && !strcmp(dir, argv[0]))
264 return io_open(io, "%s%s", dir, argv[1]);
266 if ((type == IO_RD || type == IO_WR) && pipe(pipefds) < 0) {
267 io->error = errno;
268 return FALSE;
269 } else if (type == IO_AP) {
270 pipefds[1] = custom;
273 if ((io->pid = fork())) {
274 if (io->pid == -1)
275 io->error = errno;
276 if (pipefds[!(type == IO_WR)] != -1)
277 close(pipefds[!(type == IO_WR)]);
278 if (io->pid != -1) {
279 io->pipe = pipefds[!!(type == IO_WR)];
280 return TRUE;
283 } else {
284 if (type != IO_FG) {
285 int devnull = open("/dev/null", O_RDWR);
286 int readfd = type == IO_WR ? pipefds[0] : devnull;
287 int writefd = (type == IO_RD || type == IO_AP)
288 ? pipefds[1] : devnull;
289 int errorfd = open_trace(devnull, argv);
291 /* Inject stdin given on the command line. */
292 if (read_from_stdin)
293 readfd = dup(STDIN_FILENO);
295 dup2(readfd, STDIN_FILENO);
296 dup2(writefd, STDOUT_FILENO);
297 if (read_with_stderr)
298 dup2(writefd, STDERR_FILENO);
299 else
300 dup2(errorfd, STDERR_FILENO);
302 if (devnull != errorfd)
303 close(errorfd);
304 close(devnull);
305 if (pipefds[0] != -1)
306 close(pipefds[0]);
307 if (pipefds[1] != -1)
308 close(pipefds[1]);
311 if (dir && *dir && chdir(dir) == -1)
312 exit(errno);
314 if (env) {
315 int i;
317 for (i = 0; env[i]; i++)
318 if (*env[i])
319 putenv(env[i]);
322 execvp(argv[0], (char *const*) argv);
323 exit(errno);
326 if (pipefds[!!(type == IO_WR)] != -1)
327 close(pipefds[!!(type == IO_WR)]);
328 return FALSE;
331 bool
332 io_run(struct io *io, enum io_type type, const char *dir, char * const env[], const char *argv[])
334 return io_exec(io, type, dir, env, argv, 0);
337 bool
338 io_complete(enum io_type type, const char **argv, const char *dir, int fd)
340 struct io io;
342 return io_exec(&io, type, dir, NULL, argv, fd) && io_done(&io);
345 bool
346 io_run_bg(const char **argv)
348 return io_complete(IO_BG, argv, NULL, -1);
351 bool
352 io_run_fg(const char **argv, const char *dir)
354 return io_complete(IO_FG, argv, dir, -1);
357 bool
358 io_run_append(const char **argv, int fd)
360 return io_complete(IO_AP, argv, NULL, fd);
363 bool
364 io_eof(struct io *io)
366 return io->eof;
370 io_error(struct io *io)
372 return io->error;
375 char *
376 io_strerror(struct io *io)
378 return strerror(io->error);
381 bool
382 io_can_read(struct io *io, bool can_block)
384 struct timeval tv = { 0, 500 };
385 fd_set fds;
387 FD_ZERO(&fds);
388 FD_SET(io->pipe, &fds);
390 return select(io->pipe + 1, &fds, NULL, NULL, can_block ? NULL : &tv) > 0;
393 ssize_t
394 io_read(struct io *io, void *buf, size_t bufsize)
396 do {
397 ssize_t readsize = read(io->pipe, buf, bufsize);
399 if (readsize < 0 && (errno == EAGAIN || errno == EINTR))
400 continue;
401 else if (readsize == -1)
402 io->error = errno;
403 else if (readsize == 0)
404 io->eof = 1;
405 return readsize;
406 } while (1);
409 char *
410 io_memchr(struct io *io, char *data, int c)
412 char *pos;
414 if (data < io->buf || io->bufpos <= data)
415 return NULL;
417 pos = memchr(data, c, io->bufpos - data - 1);
418 return pos ? pos + 1 : NULL;
421 DEFINE_ALLOCATOR(io_realloc_buf, char, BUFSIZ)
423 char *
424 io_get(struct io *io, int c, bool can_read)
426 char *eol;
427 ssize_t readsize;
429 while (TRUE) {
430 if (io->bufsize > 0) {
431 eol = memchr(io->bufpos, c, io->bufsize);
433 while (io->span && io->bufpos < eol && eol[-1] == '\\') {
434 eol[-1] = eol[0] = ' ';
435 eol = memchr(io->bufpos, c, io->bufsize);
437 if (eol) {
438 char *line = io->bufpos;
440 *eol = 0;
441 io->bufpos = eol + 1;
442 io->bufsize -= io->bufpos - line;
443 return line;
447 if (io_eof(io)) {
448 if (io->bufsize) {
449 char *line = io->bufpos;
451 io->bufpos[io->bufsize] = 0;
452 io->bufpos += io->bufsize;
453 io->bufsize = 0;
454 return line;
456 return NULL;
459 if (!can_read)
460 return NULL;
462 if (io->bufsize > 0 && io->bufpos > io->buf)
463 memmove(io->buf, io->bufpos, io->bufsize);
465 if (io->bufalloc == io->bufsize) {
466 if (!io_realloc_buf(&io->buf, io->bufalloc, BUFSIZ))
467 return NULL;
468 io->bufalloc += BUFSIZ;
471 io->bufpos = io->buf;
472 readsize = io_read(io, io->buf + io->bufsize, io->bufalloc - io->bufsize);
473 if (io_error(io))
474 return NULL;
475 io->bufsize += readsize;
479 bool
480 io_write(struct io *io, const void *buf, size_t bufsize)
482 size_t written = 0;
484 while (!io_error(io) && written < bufsize) {
485 ssize_t size;
487 size = write(io->pipe, buf + written, bufsize - written);
488 if (size < 0 && (errno == EAGAIN || errno == EINTR))
489 continue;
490 else if (size == -1)
491 io->error = errno;
492 else
493 written += size;
496 return written == bufsize;
499 bool
500 io_printf(struct io *io, const char *fmt, ...)
502 char buf[SIZEOF_STR] = "";
503 int retval;
505 FORMAT_BUFFER(buf, sizeof(buf), fmt, retval, FALSE);
506 if (retval < 0) {
507 io->error = ENAMETOOLONG;
508 return FALSE;
511 return io_write(io, buf, retval);
514 bool
515 io_read_buf(struct io *io, char buf[], size_t bufsize)
517 char *result = io_get(io, '\n', TRUE);
519 if (result) {
520 result = chomp_string(result);
521 string_ncopy_do(buf, bufsize, result, strlen(result));
524 return io_done(io) && result;
527 bool
528 io_run_buf(const char **argv, char buf[], size_t bufsize)
530 struct io io;
532 return io_run(&io, IO_RD, NULL, NULL, argv) && io_read_buf(&io, buf, bufsize);
535 bool
536 io_from_string(struct io *io, const char *str)
538 size_t len = strlen(str);
540 io_init(io);
542 if (!io_realloc_buf(&io->buf, io->bufalloc, len))
543 return FALSE;
545 io->bufsize = io->bufalloc = len;
546 io->bufpos = io->buf;
547 io->eof = TRUE;
548 strncpy(io->buf, str, len);
550 return TRUE;
554 io_load(struct io *io, const char *separators,
555 io_read_fn read_property, void *data)
557 char *name;
558 int state = OK;
560 while (state == OK && (name = io_get(io, '\n', TRUE))) {
561 char *value;
562 size_t namelen;
563 size_t valuelen;
565 name = chomp_string(name);
566 namelen = strcspn(name, separators);
568 if (name[namelen]) {
569 name[namelen] = 0;
570 value = chomp_string(name + namelen + 1);
571 valuelen = strlen(value);
573 } else {
574 value = "";
575 valuelen = 0;
578 state = read_property(name, namelen, value, valuelen, data);
581 if (state != ERR && io_error(io))
582 state = ERR;
583 io_done(io);
585 return state;
589 io_run_load(const char **argv, const char *separators,
590 io_read_fn read_property, void *data)
592 struct io io;
594 if (!io_run(&io, IO_RD, NULL, NULL, argv))
595 return ERR;
596 return io_load(&io, separators, read_property, data);
599 const char *
600 get_temp_dir(void)
602 static const char *tmp;
604 if (tmp)
605 return tmp;
607 if (!tmp)
608 tmp = getenv("TMPDIR");
609 if (!tmp)
610 tmp = getenv("TEMP");
611 if (!tmp)
612 tmp = getenv("TMP");
613 if (!tmp)
614 tmp = "/tmp";
616 return tmp;
619 /* vim: set ts=8 sw=8 noexpandtab: */