Create a commit view history API based on the tree view stack
[tig.git] / io.c
blob2c699786b74905903e5064b1946d17e8144b0f93
1 /* Copyright (c) 2006-2013 Jonas Fonseca <fonseca@diku.dk>
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.h"
15 #include "io.h"
17 bool
18 argv_to_string(const char *argv[SIZEOF_ARG], char *buf, size_t buflen, const char *sep)
20 size_t bufpos, argc;
22 for (bufpos = 0, argc = 0; argv[argc]; argc++)
23 if (!string_nformat(buf, buflen, &bufpos, "%s%s",
24 argc ? sep : "", argv[argc]))
25 return FALSE;
27 return TRUE;
30 static inline int
31 get_arg_valuelen(const char *arg, char *quoted)
33 if (*arg == '"' || *arg == '\'') {
34 const char *end = *arg == '"' ? "\"" : "'";
35 int valuelen = strcspn(arg + 1, end);
37 if (quoted)
38 *quoted = *arg;
39 return valuelen > 0 ? valuelen + 2 : strlen(arg);
40 } else {
41 if (quoted)
42 *quoted = 0;
43 return strcspn(arg, " \t");
47 static bool
48 split_argv_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd, bool remove_quotes)
50 while (*cmd && *argc < SIZEOF_ARG) {
51 char quoted = 0;
52 int valuelen = get_arg_valuelen(cmd, &quoted);
53 bool advance = cmd[valuelen] != 0;
54 int quote_offset = !!(quoted && remove_quotes);
56 cmd[valuelen - quote_offset] = 0;
57 argv[(*argc)++] = chomp_string(cmd + quote_offset);
58 cmd = chomp_string(cmd + valuelen + advance);
61 if (*argc < SIZEOF_ARG)
62 argv[*argc] = NULL;
63 return *argc < SIZEOF_ARG;
66 bool
67 argv_from_string_no_quotes(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
69 return split_argv_string(argv, argc, cmd, TRUE);
72 bool
73 argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
75 return split_argv_string(argv, argc, cmd, FALSE);
78 bool
79 argv_from_env(const char **argv, const char *name)
81 char *env = argv ? getenv(name) : NULL;
82 int argc = 0;
84 if (env && *env)
85 env = strdup(env);
86 return !env || argv_from_string(argv, &argc, env);
89 void
90 argv_free(const char *argv[])
92 int argc;
94 if (!argv)
95 return;
96 for (argc = 0; argv[argc]; argc++)
97 free((void *) argv[argc]);
98 argv[0] = NULL;
101 size_t
102 argv_size(const char **argv)
104 int argc = 0;
106 while (argv && argv[argc])
107 argc++;
109 return argc;
112 bool
113 argv_contains(const char **argv, const char *arg)
115 int i;
117 for (i = 0; argv && argv[i]; i++)
118 if (!strcmp(argv[i], arg))
119 return TRUE;
120 return FALSE;
123 DEFINE_ALLOCATOR(argv_realloc, const char *, SIZEOF_ARG)
125 bool
126 argv_append(const char ***argv, const char *arg)
128 size_t argc = argv_size(*argv);
129 char *alloc;
131 if (!*arg && argc > 0)
132 return TRUE;
134 if (!argv_realloc(argv, argc, 2))
135 return FALSE;
137 alloc = strdup(arg);
139 (*argv)[argc++] = alloc;
140 (*argv)[argc] = NULL;
142 return alloc != NULL;
145 bool
146 argv_append_array(const char ***dst_argv, const char *src_argv[])
148 int i;
150 for (i = 0; src_argv && src_argv[i]; i++)
151 if (!argv_append(dst_argv, src_argv[i]))
152 return FALSE;
153 return TRUE;
156 bool
157 argv_remove_quotes(const char *argv[])
159 int argc;
161 for (argc = 0; argv[argc]; argc++) {
162 char quoted = 0;
163 const char *arg = argv[argc];
164 int arglen = get_arg_valuelen(arg, &quoted);
165 int unquotedlen = arglen - 1 - (arg[arglen - 1] == quoted);
166 char *unquoted;
168 if (!quoted)
169 continue;
171 unquoted = malloc(unquotedlen + 1);
172 if (!unquoted)
173 return FALSE;
174 strncpy(unquoted, arg + 1, unquotedlen);
175 unquoted[unquotedlen] = 0;
176 free((void *) arg);
177 argv[argc] = unquoted;
180 return TRUE;
183 bool
184 argv_copy(const char ***dst, const char *src[])
186 int argc;
188 argv_free(*dst);
189 for (argc = 0; src[argc]; argc++)
190 if (!argv_append(dst, src[argc]))
191 return FALSE;
192 return TRUE;
196 * Encoding conversion.
199 #define ENCODING_SEP ": encoding: "
200 #define ENCODING_ARG "--encoding=" ENCODING_UTF8
202 struct encoding {
203 struct encoding *next;
204 iconv_t cd;
205 char fromcode[1];
208 char encoding_arg[] = ENCODING_ARG;
209 struct encoding *default_encoding;
210 static struct encoding *encodings;
212 struct encoding *
213 encoding_open(const char *fromcode)
215 struct encoding *encoding;
216 size_t len = strlen(fromcode);
218 if (!*fromcode)
219 return NULL;
221 for (encoding = encodings; encoding; encoding = encoding->next) {
222 if (!strcasecmp(encoding->fromcode, fromcode))
223 return encoding;
226 encoding = calloc(1, sizeof(*encoding) + len);
227 strncpy(encoding->fromcode, fromcode, len);
228 encoding->cd = iconv_open(ENCODING_UTF8, fromcode);
229 if (encoding->cd == ICONV_NONE) {
230 free(encoding);
231 return NULL;
234 encoding->next = encodings;
235 encodings = encoding;
237 return encoding;
240 static char *
241 encoding_convert_string(iconv_t iconv_cd, char *line)
243 static char out_buffer[BUFSIZ * 2];
244 ICONV_CONST char *inbuf = line;
245 size_t inlen = strlen(line) + 1;
247 char *outbuf = out_buffer;
248 size_t outlen = sizeof(out_buffer);
250 size_t ret = iconv(iconv_cd, &inbuf, &inlen, &outbuf, &outlen);
252 return (ret != (size_t) -1) ? out_buffer : line;
255 char *
256 encoding_convert(struct encoding *encoding, char *line)
258 return encoding_convert_string(encoding->cd, line);
261 const char *
262 encoding_iconv(iconv_t iconv_cd, const char *string)
264 char *instr = strdup(string);
265 const char *ret = encoding_convert_string(iconv_cd, instr);
267 free(instr);
268 return ret == instr ? string : ret;
271 struct encoding *
272 get_path_encoding(const char *path, struct encoding *default_encoding)
274 const char *check_attr_argv[] = {
275 "git", "check-attr", "encoding", "--", path, NULL
277 char buf[SIZEOF_STR];
278 char *encoding;
280 /* <path>: encoding: <encoding> */
282 if (!*path || !io_run_buf(check_attr_argv, buf, sizeof(buf))
283 || !(encoding = strstr(buf, ENCODING_SEP)))
284 return default_encoding;
286 encoding += STRING_SIZE(ENCODING_SEP);
287 if (!strcmp(encoding, ENCODING_UTF8)
288 || !strcmp(encoding, "unspecified")
289 || !strcmp(encoding, "set"))
290 return default_encoding;
292 return encoding_open(encoding);
296 * Executing external commands.
299 static void
300 io_init(struct io *io)
302 memset(io, 0, sizeof(*io));
303 io->pipe = -1;
306 bool
307 io_open(struct io *io, const char *fmt, ...)
309 char name[SIZEOF_STR] = "";
310 int retval;
312 io_init(io);
314 FORMAT_BUFFER(name, sizeof(name), fmt, retval, FALSE);
315 if (retval < 0) {
316 io->error = ENAMETOOLONG;
317 return FALSE;
320 io->pipe = *name ? open(name, O_RDONLY) : dup(STDIN_FILENO);
321 if (io->pipe == -1)
322 io->error = errno;
323 return io->pipe != -1;
326 bool
327 io_kill(struct io *io)
329 return io->pid == 0 || kill(io->pid, SIGKILL) != -1;
332 bool
333 io_done(struct io *io)
335 pid_t pid = io->pid;
337 if (io->pipe != -1)
338 close(io->pipe);
339 free(io->buf);
340 io_init(io);
342 while (pid > 0) {
343 int status;
344 pid_t waiting = waitpid(pid, &status, 0);
346 if (waiting < 0) {
347 if (errno == EINTR)
348 continue;
349 io->error = errno;
350 return FALSE;
353 if (WEXITSTATUS(status)) {
354 io->status = WEXITSTATUS(status);
357 return waiting == pid &&
358 !WIFSIGNALED(status) &&
359 !io->status;
362 return TRUE;
365 static int
366 open_trace(int devnull, const char *argv[])
368 static const char *trace_file;
370 if (!trace_file) {
371 trace_file = getenv("TIG_TRACE");
372 if (!trace_file)
373 trace_file = "";
376 if (*trace_file) {
377 int fd = open(trace_file, O_RDWR | O_CREAT | O_APPEND, 0666);
378 int i;
380 for (i = 0; argv[i]; i++) {
381 if (write(fd, argv[i], strlen(argv[i])) == -1
382 || write(fd, " ", 1) == -1)
383 break;
385 if (argv[i] || write(fd, "\n", 1) == -1) {
386 close(fd);
387 return devnull;
390 return fd;
393 return devnull;
396 bool
397 io_run(struct io *io, enum io_type type, const char *dir, char * const env[], const char *argv[], ...)
399 int pipefds[2] = { -1, -1 };
400 va_list args;
401 bool read_from_stdin = type == IO_RD_STDIN;
403 io_init(io);
405 if (read_from_stdin)
406 type = IO_RD;
408 if (dir && !strcmp(dir, argv[0]))
409 return io_open(io, "%s%s", dir, argv[1]);
411 if ((type == IO_RD || type == IO_WR) && pipe(pipefds) < 0) {
412 io->error = errno;
413 return FALSE;
414 } else if (type == IO_AP) {
415 va_start(args, argv);
416 pipefds[1] = va_arg(args, int);
417 va_end(args);
420 if ((io->pid = fork())) {
421 if (io->pid == -1)
422 io->error = errno;
423 if (pipefds[!(type == IO_WR)] != -1)
424 close(pipefds[!(type == IO_WR)]);
425 if (io->pid != -1) {
426 io->pipe = pipefds[!!(type == IO_WR)];
427 return TRUE;
430 } else {
431 if (type != IO_FG) {
432 int devnull = open("/dev/null", O_RDWR);
433 int readfd = type == IO_WR ? pipefds[0] : devnull;
434 int writefd = (type == IO_RD || type == IO_AP)
435 ? pipefds[1] : devnull;
436 int errorfd = open_trace(devnull, argv);
438 /* Inject stdin given on the command line. */
439 if (read_from_stdin)
440 readfd = dup(STDIN_FILENO);
442 dup2(readfd, STDIN_FILENO);
443 dup2(writefd, STDOUT_FILENO);
444 dup2(errorfd, STDERR_FILENO);
446 if (devnull != errorfd)
447 close(errorfd);
448 close(devnull);
449 if (pipefds[0] != -1)
450 close(pipefds[0]);
451 if (pipefds[1] != -1)
452 close(pipefds[1]);
455 if (dir && *dir && chdir(dir) == -1)
456 exit(errno);
458 if (env) {
459 int i;
461 for (i = 0; env[i]; i++)
462 if (*env[i])
463 putenv(env[i]);
466 execvp(argv[0], (char *const*) argv);
467 exit(errno);
470 if (pipefds[!!(type == IO_WR)] != -1)
471 close(pipefds[!!(type == IO_WR)]);
472 return FALSE;
475 bool
476 io_complete(enum io_type type, const char **argv, const char *dir, int fd)
478 struct io io;
480 return io_run(&io, type, dir, NULL, argv, fd) && io_done(&io);
483 bool
484 io_run_bg(const char **argv)
486 return io_complete(IO_BG, argv, NULL, -1);
489 bool
490 io_run_fg(const char **argv, const char *dir)
492 return io_complete(IO_FG, argv, dir, -1);
495 bool
496 io_run_append(const char **argv, int fd)
498 return io_complete(IO_AP, argv, NULL, fd);
501 bool
502 io_eof(struct io *io)
504 return io->eof;
508 io_error(struct io *io)
510 return io->error;
513 char *
514 io_strerror(struct io *io)
516 return strerror(io->error);
519 bool
520 io_can_read(struct io *io, bool can_block)
522 struct timeval tv = { 0, 500 };
523 fd_set fds;
525 FD_ZERO(&fds);
526 FD_SET(io->pipe, &fds);
528 return select(io->pipe + 1, &fds, NULL, NULL, can_block ? NULL : &tv) > 0;
531 ssize_t
532 io_read(struct io *io, void *buf, size_t bufsize)
534 do {
535 ssize_t readsize = read(io->pipe, buf, bufsize);
537 if (readsize < 0 && (errno == EAGAIN || errno == EINTR))
538 continue;
539 else if (readsize == -1)
540 io->error = errno;
541 else if (readsize == 0)
542 io->eof = 1;
543 return readsize;
544 } while (1);
547 DEFINE_ALLOCATOR(io_realloc_buf, char, BUFSIZ)
549 char *
550 io_get(struct io *io, int c, bool can_read)
552 char *eol;
553 ssize_t readsize;
555 while (TRUE) {
556 if (io->bufsize > 0) {
557 eol = memchr(io->bufpos, c, io->bufsize);
558 if (eol) {
559 char *line = io->bufpos;
561 *eol = 0;
562 io->bufpos = eol + 1;
563 io->bufsize -= io->bufpos - line;
564 return line;
568 if (io_eof(io)) {
569 if (io->bufsize) {
570 io->bufpos[io->bufsize] = 0;
571 io->bufsize = 0;
572 return io->bufpos;
574 return NULL;
577 if (!can_read)
578 return NULL;
580 if (io->bufsize > 0 && io->bufpos > io->buf)
581 memmove(io->buf, io->bufpos, io->bufsize);
583 if (io->bufalloc == io->bufsize) {
584 if (!io_realloc_buf(&io->buf, io->bufalloc, BUFSIZ))
585 return NULL;
586 io->bufalloc += BUFSIZ;
589 io->bufpos = io->buf;
590 readsize = io_read(io, io->buf + io->bufsize, io->bufalloc - io->bufsize);
591 if (io_error(io))
592 return NULL;
593 io->bufsize += readsize;
597 bool
598 io_write(struct io *io, const void *buf, size_t bufsize)
600 size_t written = 0;
602 while (!io_error(io) && written < bufsize) {
603 ssize_t size;
605 size = write(io->pipe, buf + written, bufsize - written);
606 if (size < 0 && (errno == EAGAIN || errno == EINTR))
607 continue;
608 else if (size == -1)
609 io->error = errno;
610 else
611 written += size;
614 return written == bufsize;
617 bool
618 io_printf(struct io *io, const char *fmt, ...)
620 char buf[SIZEOF_STR] = "";
621 int retval;
623 FORMAT_BUFFER(buf, sizeof(buf), fmt, retval, FALSE);
624 if (retval < 0) {
625 io->error = ENAMETOOLONG;
626 return FALSE;
629 return io_write(io, buf, retval);
632 bool
633 io_read_buf(struct io *io, char buf[], size_t bufsize)
635 char *result = io_get(io, '\n', TRUE);
637 if (result) {
638 result = chomp_string(result);
639 string_ncopy_do(buf, bufsize, result, strlen(result));
642 return io_done(io) && result;
645 bool
646 io_run_buf(const char **argv, char buf[], size_t bufsize)
648 struct io io;
650 return io_run(&io, IO_RD, NULL, NULL, argv) && io_read_buf(&io, buf, bufsize);
654 io_load(struct io *io, const char *separators,
655 io_read_fn read_property, void *data)
657 char *name;
658 int state = OK;
660 while (state == OK && (name = io_get(io, '\n', TRUE))) {
661 char *value;
662 size_t namelen;
663 size_t valuelen;
665 name = chomp_string(name);
666 namelen = strcspn(name, separators);
668 if (name[namelen]) {
669 name[namelen] = 0;
670 value = chomp_string(name + namelen + 1);
671 valuelen = strlen(value);
673 } else {
674 value = "";
675 valuelen = 0;
678 state = read_property(name, namelen, value, valuelen, data);
681 if (state != ERR && io_error(io))
682 state = ERR;
683 io_done(io);
685 return state;
689 io_run_load(const char **argv, const char *separators,
690 io_read_fn read_property, void *data)
692 struct io io;
694 if (!io_run(&io, IO_RD, NULL, NULL, argv))
695 return ERR;
696 return io_load(&io, separators, read_property, data);
699 const char *
700 get_temp_dir(void)
702 static const char *tmp;
704 if (tmp)
705 return tmp;
707 if (!tmp)
708 tmp = getenv("TMPDIR");
709 if (!tmp)
710 tmp = getenv("TEMP");
711 if (!tmp)
712 tmp = getenv("TMP");
713 if (!tmp)
714 tmp = "/tmp";
716 return tmp;
719 /* vim: set ts=8 sw=8 noexpandtab: */