diff options
Diffstat (limited to 'lib/src')
| -rw-r--r-- | lib/src/rdline.c | 223 | 
1 files changed, 123 insertions, 100 deletions
diff --git a/lib/src/rdline.c b/lib/src/rdline.c index d6c4078..b108ef2 100644 --- a/lib/src/rdline.c +++ b/lib/src/rdline.c @@ -17,130 +17,153 @@   */  #include <stdlib.h>  #include <unistd.h> +#include <string.h>  #include <errno.h>  #include <ctype.h>  #include "util.h" -enum { -	STATE_INITIAL = 0, -	STATE_STRING = 1, -	STATE_STRING_ESC = 2, -	STATE_COMMENT = 3, -	STATE_ARG = 4, -}; +typedef struct { +	int fd;			/* input file descriptor */ +	const char *argstr;	/* if not NULL, read from this instead */ -char *rdline(int fd, int argc, const char *const *argv) +	size_t i;		/* buffer offset */ +	size_t bufsiz;		/* buffer size */ +	char *buffer; + +	bool string;		/* inside a string? */ +	bool escape;		/* reading an escape sequence? */ +	bool comment;		/* inside a comment */ +} rdline_t; + +static int rdline_getc(rdline_t *t)  { -	size_t i = 0, bufsiz = 0, newsz; -	int ret, state = STATE_INITIAL; -	char c, *new, *buffer = NULL; -	const char *argstr = NULL; - -	for (;;) { -		if (argstr == NULL) { -			switch (read(fd, &c, 1)) { -			case 0: -				if (i == 0) { -					errno = 0; -					return NULL; -				} -				c = '\0'; -				break; -			case 1: -				break; -			default: -				if (errno == EINTR) -					continue; -				goto fail; -			} -		} else { -			c = *(argstr++); +	int ret; +	char c; -			if (c == '\0') { -				argstr = NULL; -				continue; -			} +	if (t->argstr != NULL) { +		c = *(t->argstr++); +		if (c != '\0') +			goto out; + +		t->argstr = NULL; +	} + +	do { +		ret = read(t->fd, &c, 1); +	} while (ret < 0 && errno == EINTR); + +	if (ret < 0) +		return -1; + +	if (ret == 0) { +		if (t->i == 0) { +			errno = 0; +			return -1;  		} +		c = '\0'; +	} +out: +	return (c == '\n') ? '\0' : c; +} -		if (c == '\n') -			c = '\0'; +static int rdline_append(rdline_t *t, int c) +{ +	size_t newsz; +	char *new; -		switch (state) { -		case STATE_STRING: +	if (t->comment) { +		if (c != '\0') +			return 0; +	} else if (t->string) { +		if (t->escape) { +			t->escape = false; +		} else {  			if (c == '\\') -				state = STATE_STRING_ESC; -			if (c == '"') -				state = STATE_INITIAL; -			break; -		case STATE_STRING_ESC: -			state = STATE_STRING; -			break; -		case STATE_COMMENT: -			if (c != '\0') -				continue; -			break; -		case STATE_ARG: -			state = STATE_INITIAL; -			if (c == '%') -				break; -			if (!isdigit(c) || (c - '0') >= argc) { -				errno = EINVAL; -				goto fail; -			} -			if (argstr != NULL) { -				errno = ELOOP; -				goto fail; -			} -			argstr = argv[c - '0']; -			continue; -		default: -			if (isspace(c)) -				c = ' '; -			if (c == ' ' && (i == 0 || buffer[i - 1] == ' ')) -				continue; -			if (c == '#') { -				state = STATE_COMMENT; -				continue; -			} -			if (c == '%') { -				state = STATE_ARG; -				continue; -			} +				t->escape = true;  			if (c == '"') -				state = STATE_STRING; -			break; +				t->string = false;  		} - -		if (c == '\0') { -			while (i > 0 && buffer[i - 1] == ' ') -				--i; +	} else { +		if (isspace(c)) +			c = ' '; +		if (c == ' ' && (t->i == 0 || t->buffer[t->i - 1] == ' ')) +			return 0; +		if (c == '#') { +			t->comment = true; +			return 0;  		} +		if (c == '"') +			t->string = true; +	} -		if (i == bufsiz) { -			newsz = bufsiz ? bufsiz * 2 : 16; -			new = realloc(buffer, newsz); +	if (c == '\0') { +		while (t->i > 0 && t->buffer[t->i - 1] == ' ') +			t->i -= 1; +	} + +	if (t->i == t->bufsiz) { +		newsz = t->bufsiz ? t->bufsiz * 2 : 16; +		new = realloc(t->buffer, newsz); + +		if (new == NULL) +			return -1; + +		t->buffer = new; +		t->bufsiz = newsz; +	} + +	t->buffer[t->i++] = c; +	return 0; +} -			if (new == NULL) +char *rdline(int fd, int argc, const char *const *argv) +{ +	rdline_t rd; +	int ret; +	char c; + +	memset(&rd, 0, sizeof(rd)); +	rd.fd = fd; + +	do { +		c = rdline_getc(&rd); +		if (c < 0) +			goto fail; +		if (c == 0 && rd.string) { +			errno = EILSEQ; +			goto fail; +		} + +		if (c == '%') { +			c = rdline_getc(&rd); +			if (c == 0) +				errno = EILSEQ; +			if (c <= 0)  				goto fail; -			buffer = new; -			bufsiz = newsz; +			if (c != '%') { +				if (!isdigit(c) || (c - '0') >= argc) { +					errno = EINVAL; +					goto fail; +				} +				if (rd.argstr != NULL) { +					errno = ELOOP; +					goto fail; +				} +				rd.argstr = argv[c - '0']; +				continue; +			}  		} -		buffer[i++] = c; -		if (c == '\0') -			break; -	} +		if (rdline_append(&rd, c)) +			goto fail; +	} while (c != '\0'); -	if (state == STATE_STRING || state == STATE_STRING_ESC) { -		errno = EILSEQ; -		goto fail; -	} -	return buffer; +	return rd.buffer;  fail:  	ret = errno; -	free(buffer); +	free(rd.buffer);  	errno = ret;  	return NULL;  }  | 
