PageRenderTime 40ms CodeModel.GetById 13ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/tig-1.0/io.c

#
C | 579 lines | 449 code | 112 blank | 18 comment | 146 complexity | b4e1d157e10b41b7b756239b763d44a9 MD5 | raw file
Possible License(s): GPL-2.0
  1/* Copyright (c) 2006-2012 Jonas Fonseca <fonseca@diku.dk>
  2 *
  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.
  7 *
  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.
 12 */
 13
 14#include "tig.h"
 15#include "io.h"
 16
 17static inline int
 18get_arg_valuelen(const char *arg, bool *quoted)
 19{
 20	if (*arg == '"' || *arg == '\'') {
 21		const char *end = *arg == '"' ? "\"" : "'";
 22		int valuelen = strcspn(arg + 1, end);
 23
 24		if (quoted)
 25			*quoted = TRUE;
 26		return valuelen > 0 ? valuelen + 2 : strlen(arg);
 27	} else {
 28		return strcspn(arg, " \t");
 29	}
 30}
 31
 32static bool
 33split_argv_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd, bool remove_quotes)
 34{
 35	while (*cmd && *argc < SIZEOF_ARG) {
 36		bool quoted = FALSE;
 37		int valuelen = get_arg_valuelen(cmd, &quoted);
 38		bool advance = cmd[valuelen] != 0;
 39		int quote_offset = !!(quoted && remove_quotes);
 40
 41		cmd[valuelen - quote_offset] = 0;
 42		argv[(*argc)++] = chomp_string(cmd + quote_offset);
 43		cmd = chomp_string(cmd + valuelen + advance);
 44	}
 45
 46	if (*argc < SIZEOF_ARG)
 47		argv[*argc] = NULL;
 48	return *argc < SIZEOF_ARG;
 49}
 50
 51bool
 52argv_from_string_no_quotes(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
 53{
 54	return split_argv_string(argv, argc, cmd, TRUE);
 55}
 56
 57bool
 58argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
 59{
 60	return split_argv_string(argv, argc, cmd, FALSE);
 61}
 62
 63bool
 64argv_from_env(const char **argv, const char *name)
 65{
 66	char *env = argv ? getenv(name) : NULL;
 67	int argc = 0;
 68
 69	if (env && *env)
 70		env = strdup(env);
 71	return !env || argv_from_string(argv, &argc, env);
 72}
 73
 74void
 75argv_free(const char *argv[])
 76{
 77	int argc;
 78
 79	if (!argv)
 80		return;
 81	for (argc = 0; argv[argc]; argc++)
 82		free((void *) argv[argc]);
 83	argv[0] = NULL;
 84}
 85
 86size_t
 87argv_size(const char **argv)
 88{
 89	int argc = 0;
 90
 91	while (argv && argv[argc])
 92		argc++;
 93
 94	return argc;
 95}
 96
 97DEFINE_ALLOCATOR(argv_realloc, const char *, SIZEOF_ARG)
 98
 99bool
100argv_append(const char ***argv, const char *arg)
101{
102	size_t argc = argv_size(*argv);
103
104	if (!*arg && argc > 0)
105		return TRUE;
106
107	if (!argv_realloc(argv, argc, 2))
108		return FALSE;
109
110	(*argv)[argc++] = strdup(arg);
111	(*argv)[argc] = NULL;
112	return TRUE;
113}
114
115bool
116argv_append_array(const char ***dst_argv, const char *src_argv[])
117{
118	int i;
119
120	for (i = 0; src_argv && src_argv[i]; i++)
121		if (!argv_append(dst_argv, src_argv[i]))
122			return FALSE;
123	return TRUE;
124}
125
126bool
127argv_copy(const char ***dst, const char *src[])
128{
129	int argc;
130
131	argv_free(*dst);
132	for (argc = 0; src[argc]; argc++)
133		if (!argv_append(dst, src[argc]))
134			return FALSE;
135	return TRUE;
136}
137
138/*
139 * Encoding conversion.
140 */
141
142struct encoding {
143	struct encoding *next;
144	iconv_t cd;
145	char fromcode[1];
146};
147
148static struct encoding *encodings;
149
150struct encoding *
151encoding_open(const char *fromcode)
152{
153	struct encoding *encoding;
154	size_t len = strlen(fromcode);
155
156	if (!*fromcode)
157		return NULL;
158
159	for (encoding = encodings; encoding; encoding = encoding->next) {
160		if (!strcasecmp(encoding->fromcode, fromcode))
161			return encoding;
162	}
163
164	encoding = calloc(1, sizeof(*encoding) + len);
165	strncpy(encoding->fromcode, fromcode, len);
166	encoding->cd = iconv_open(ENCODING_UTF8, fromcode);
167	if (encoding->cd == ICONV_NONE) {
168		free(encoding);
169		return NULL;
170	}
171
172	encoding->next = encodings;
173	encodings = encoding;
174
175	return encoding;
176}
177
178char *
179encoding_convert(struct encoding *encoding, char *line)
180{
181	static char out_buffer[BUFSIZ * 2];
182	ICONV_CONST char *inbuf = line;
183	size_t inlen = strlen(line) + 1;
184
185	char *outbuf = out_buffer;
186	size_t outlen = sizeof(out_buffer);
187
188	size_t ret = iconv(encoding->cd, &inbuf, &inlen, &outbuf, &outlen);
189
190	return (ret != (size_t) -1) ? out_buffer : line;
191}
192
193/*
194 * Executing external commands.
195 */
196
197static void
198io_init(struct io *io)
199{
200	memset(io, 0, sizeof(*io));
201	io->pipe = -1;
202}
203
204bool
205io_open(struct io *io, const char *fmt, ...)
206{
207	char name[SIZEOF_STR] = "";
208	int retval;
209
210	io_init(io);
211
212	FORMAT_BUFFER(name, sizeof(name), fmt, retval, FALSE);
213	if (retval < 0) {
214		io->error = ENAMETOOLONG;
215		return FALSE;
216	}
217
218	io->pipe = *name ? open(name, O_RDONLY) : dup(STDIN_FILENO);
219	if (io->pipe == -1)
220		io->error = errno;
221	return io->pipe != -1;
222}
223
224bool
225io_kill(struct io *io)
226{
227	return io->pid == 0 || kill(io->pid, SIGKILL) != -1;
228}
229
230bool
231io_done(struct io *io)
232{
233	pid_t pid = io->pid;
234
235	if (io->pipe != -1)
236		close(io->pipe);
237	free(io->buf);
238	io_init(io);
239
240	while (pid > 0) {
241		int status;
242		pid_t waiting = waitpid(pid, &status, 0);
243
244		if (waiting < 0) {
245			if (errno == EINTR)
246				continue;
247			io->error = errno;
248			return FALSE;
249		}
250
251		if (WEXITSTATUS(status)) {
252			io->status = WEXITSTATUS(status);
253		}
254
255		return waiting == pid &&
256		       !WIFSIGNALED(status) &&
257		       !io->status;
258	}
259
260	return TRUE;
261}
262
263static int
264open_trace(int devnull, const char *argv[])
265{
266	static const char *trace_file;
267
268	if (!trace_file) {
269		trace_file = getenv("TIG_TRACE");
270		if (!trace_file)
271			trace_file = "";
272	}
273
274	if (*trace_file) {
275		int fd = open(trace_file, O_RDWR | O_CREAT | O_APPEND, 0666);
276		int i;
277
278		for (i = 0; argv[i]; i++) {
279			if (write(fd, argv[i], strlen(argv[i])) == -1
280			    || write(fd, " ", 1) == -1)
281				break;
282		}
283		if (argv[i] || write(fd, "\n", 1) == -1) {
284			close(fd);
285			return devnull;
286		}
287
288		return fd;
289	}
290
291	return devnull;
292}
293
294bool
295io_run(struct io *io, enum io_type type, const char *dir, const char *argv[], ...)
296{
297	int pipefds[2] = { -1, -1 };
298	va_list args;
299
300	io_init(io);
301
302	if (dir && !strcmp(dir, argv[0]))
303		return io_open(io, "%s%s", dir, argv[1]);
304
305	if ((type == IO_RD || type == IO_WR) && pipe(pipefds) < 0) {
306		io->error = errno;
307		return FALSE;
308	} else if (type == IO_AP) {
309		va_start(args, argv);
310		pipefds[1] = va_arg(args, int);
311		va_end(args);
312	}
313
314	if ((io->pid = fork())) {
315		if (io->pid == -1)
316			io->error = errno;
317		if (pipefds[!(type == IO_WR)] != -1)
318			close(pipefds[!(type == IO_WR)]);
319		if (io->pid != -1) {
320			io->pipe = pipefds[!!(type == IO_WR)];
321			return TRUE;
322		}
323
324	} else {
325		if (type != IO_FG) {
326			int devnull = open("/dev/null", O_RDWR);
327			int readfd  = type == IO_WR ? pipefds[0] : devnull;
328			int writefd = (type == IO_RD || type == IO_AP)
329							? pipefds[1] : devnull;
330			int errorfd = open_trace(devnull, argv);
331
332			dup2(readfd,  STDIN_FILENO);
333			dup2(writefd, STDOUT_FILENO);
334			dup2(errorfd, STDERR_FILENO);
335
336			if (devnull != errorfd)
337				close(errorfd);
338			close(devnull);
339			if (pipefds[0] != -1)
340				close(pipefds[0]);
341			if (pipefds[1] != -1)
342				close(pipefds[1]);
343		}
344
345		if (dir && *dir && chdir(dir) == -1)
346			exit(errno);
347
348		execvp(argv[0], (char *const*) argv);
349		exit(errno);
350	}
351
352	if (pipefds[!!(type == IO_WR)] != -1)
353		close(pipefds[!!(type == IO_WR)]);
354	return FALSE;
355}
356
357bool
358io_complete(enum io_type type, const char **argv, const char *dir, int fd)
359{
360	struct io io;
361
362	return io_run(&io, type, dir, argv, fd) && io_done(&io);
363}
364
365bool
366io_run_bg(const char **argv)
367{
368	return io_complete(IO_BG, argv, NULL, -1);
369}
370
371bool
372io_run_fg(const char **argv, const char *dir)
373{
374	return io_complete(IO_FG, argv, dir, -1);
375}
376
377bool
378io_run_append(const char **argv, int fd)
379{
380	return io_complete(IO_AP, argv, NULL, fd);
381}
382
383bool
384io_eof(struct io *io)
385{
386	return io->eof;
387}
388
389int
390io_error(struct io *io)
391{
392	return io->error;
393}
394
395char *
396io_strerror(struct io *io)
397{
398	return strerror(io->error);
399}
400
401bool
402io_can_read(struct io *io, bool can_block)
403{
404	struct timeval tv = { 0, 500 };
405	fd_set fds;
406
407	FD_ZERO(&fds);
408	FD_SET(io->pipe, &fds);
409
410	return select(io->pipe + 1, &fds, NULL, NULL, can_block ? NULL : &tv) > 0;
411}
412
413ssize_t
414io_read(struct io *io, void *buf, size_t bufsize)
415{
416	do {
417		ssize_t readsize = read(io->pipe, buf, bufsize);
418
419		if (readsize < 0 && (errno == EAGAIN || errno == EINTR))
420			continue;
421		else if (readsize == -1)
422			io->error = errno;
423		else if (readsize == 0)
424			io->eof = 1;
425		return readsize;
426	} while (1);
427}
428
429DEFINE_ALLOCATOR(io_realloc_buf, char, BUFSIZ)
430
431char *
432io_get(struct io *io, int c, bool can_read)
433{
434	char *eol;
435	ssize_t readsize;
436
437	while (TRUE) {
438		if (io->bufsize > 0) {
439			eol = memchr(io->bufpos, c, io->bufsize);
440			if (eol) {
441				char *line = io->bufpos;
442
443				*eol = 0;
444				io->bufpos = eol + 1;
445				io->bufsize -= io->bufpos - line;
446				return line;
447			}
448		}
449
450		if (io_eof(io)) {
451			if (io->bufsize) {
452				io->bufpos[io->bufsize] = 0;
453				io->bufsize = 0;
454				return io->bufpos;
455			}
456			return NULL;
457		}
458
459		if (!can_read)
460			return NULL;
461
462		if (io->bufsize > 0 && io->bufpos > io->buf)
463			memmove(io->buf, io->bufpos, io->bufsize);
464
465		if (io->bufalloc == io->bufsize) {
466			if (!io_realloc_buf(&io->buf, io->bufalloc, BUFSIZ))
467				return NULL;
468			io->bufalloc += BUFSIZ;
469		}
470
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;
476	}
477}
478
479bool
480io_write(struct io *io, const void *buf, size_t bufsize)
481{
482	size_t written = 0;
483
484	while (!io_error(io) && written < bufsize) {
485		ssize_t size;
486
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;
494	}
495
496	return written == bufsize;
497}
498
499bool
500io_printf(struct io *io, const char *fmt, ...)
501{
502	char buf[SIZEOF_STR] = "";
503	int retval;
504
505	FORMAT_BUFFER(buf, sizeof(buf), fmt, retval, FALSE);
506	if (retval < 0) {
507		io->error = ENAMETOOLONG;
508		return FALSE;
509	}
510
511	return io_write(io, buf, retval);
512}
513
514bool
515io_read_buf(struct io *io, char buf[], size_t bufsize)
516{
517	char *result = io_get(io, '\n', TRUE);
518
519	if (result) {
520		result = chomp_string(result);
521		string_ncopy_do(buf, bufsize, result, strlen(result));
522	}
523
524	return io_done(io) && result;
525}
526
527bool
528io_run_buf(const char **argv, char buf[], size_t bufsize)
529{
530	struct io io;
531
532	return io_run(&io, IO_RD, NULL, argv) && io_read_buf(&io, buf, bufsize);
533}
534
535int
536io_load(struct io *io, const char *separators,
537	io_read_fn read_property, void *data)
538{
539	char *name;
540	int state = OK;
541
542	while (state == OK && (name = io_get(io, '\n', TRUE))) {
543		char *value;
544		size_t namelen;
545		size_t valuelen;
546
547		name = chomp_string(name);
548		namelen = strcspn(name, separators);
549
550		if (name[namelen]) {
551			name[namelen] = 0;
552			value = chomp_string(name + namelen + 1);
553			valuelen = strlen(value);
554
555		} else {
556			value = "";
557			valuelen = 0;
558		}
559
560		state = read_property(name, namelen, value, valuelen, data);
561	}
562
563	if (state != ERR && io_error(io))
564		state = ERR;
565	io_done(io);
566
567	return state;
568}
569
570int
571io_run_load(const char **argv, const char *separators,
572	    io_read_fn read_property, void *data)
573{
574	struct io io;
575
576	if (!io_run(&io, IO_RD, NULL, argv))
577		return ERR;
578	return io_load(&io, separators, read_property, data);
579}