diff tok.c @ 27:722a45b4028b

Add define command for defining macros Macros are parsed when they are defined with the D command and can subsequently be used as arguments for other commands. Handle out of memory errors directly in tok.c.
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Mon, 25 Sep 2017 21:21:25 +0200
parents a7e41e1a79c8
children
line wrap: on
line diff
--- a/tok.c	Thu Sep 21 09:45:59 2017 +0200
+++ b/tok.c	Mon Sep 25 21:21:25 2017 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
+ * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
@@ -23,22 +23,24 @@
 
 #include "compat.h"
 
-#include <errno.h>
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "tok.h"
+#include "util.h"
 
 enum tok_states {
 	STATE_INITIAL,
 	STATE_IN_WORD,
 	STATE_IN_QUOTE,
 	STATE_IN_WORD_ESCAPE,
-	STATE_IN_QUOTE_ESCAPE
+	STATE_IN_QUOTE_ESCAPE,
+	STATE_IN_MACRO
 };
 
-static inline int
+static inline void
 strbuf_appendc(char **bufp, size_t *buf_sizep, int c)
 {
 	char	*buf = *bufp;
@@ -50,10 +52,7 @@
 	/* allocate buffer if *bufp is NULL and *buf_sizep is 0 */
 	if (buf_size < len + (c >= 0) + 1) {
 		buf_size = (buf_size * 2 > BUFSIZ) ? buf_size * 2 : BUFSIZ;
-		buf = realloc(buf, buf_size);
-		if (buf == NULL) {
-			return (-1);
-		}
+		buf = xrealloc(buf, buf_size);
 	}
 
 	/* append character to string buffer or reset buffer if c is -1 */
@@ -64,35 +63,48 @@
 
 	*bufp = buf;
 	*buf_sizep = buf_size;
+}
 
-	return (0);
+void
+tok_free(union tok **tokenv)
+{
+	size_t	i;
+
+	if (tokenv == NULL) {
+		return;
+	}
+
+	for (i = 0; tokenv[i] != NULL; i++) {
+		switch (tokenv[i]->any.type) {
+		case TOK_MACRO:
+			free(tokenv[i]->macro.name);
+			break;
+		case TOK_ARG:
+			free(tokenv[i]->arg.value);
+			break;
+		}
+		free(tokenv[i]);
+	}
+	free(tokenv);
 }
 
 enum tok_err
-tok_tokenize(const char *s, int *tokencp, char ***tokenvp)
+tok_tokenize(const char *s, size_t *tokencp, union tok ***tokenvp)
 {
-	int		retval = TOK_ERR_SYSTEM_ERROR;
-	int		saved_errno = 0;
-	char		**tokenv;
+	int		retval = TOK_ERR_OK;
+	union tok	**tokenv;
 	size_t		tokenc = 0;
 	const char	*p = s;
 	enum tok_states	state = STATE_INITIAL;
 	char		quote;
 	char		*buf = NULL;
 	size_t		buf_size = 0;
-	char		*token;
-	size_t		i;
+	char		*value;
+	char		*name;
 
-	/*
-	 * allocate maximum number of tokens including the terminating NULL
-	 * pointer: ceil(length / 2) + 1
-	 */
-	tokenv = malloc(((strlen(s) + 2 - 1) / 2 + 1) * sizeof (char *));
-	if (tokenv == NULL) {
-		saved_errno = errno;
-		goto out;
-	}
-	tokenv[0] = NULL;
+	/* allocate maximum number of tokens: ceil(length / 2) */
+	tokenv = xmalloc((((strlen(s) + 2 - 1) / 2) + 1) *
+	    sizeof (union tok *));
 
 	for (;;) {
 		switch (state) {
@@ -108,34 +120,27 @@
 				/* start quoted part of token */
 				state = STATE_IN_QUOTE;
 				quote = *p;
-				if (strbuf_appendc(&buf, &buf_size, -1) != 0) {
-					saved_errno = errno;
-					goto out;
-				}
+				strbuf_appendc(&buf, &buf_size, -1);
 				break;
 			case '\\':
 				/* start token with a backslash escape */
 				state = STATE_IN_WORD_ESCAPE;
-				if (strbuf_appendc(&buf, &buf_size, -1) != 0) {
-					saved_errno = errno;
-					goto out;
-				}
+				strbuf_appendc(&buf, &buf_size, -1);
+				break;
+			case '$':
+				/* start macro token */
+				state = STATE_IN_MACRO;
+				strbuf_appendc(&buf, &buf_size, -1);
 				break;
 			case '\0':
 				/* end of input */
-				retval = 0;
+				retval = TOK_ERR_OK;
 				goto out;
 			default:
 				/* start token with a word */
 				state = STATE_IN_WORD;
-				if (strbuf_appendc(&buf, &buf_size, -1) != 0) {
-					saved_errno = errno;
-					goto out;
-				}
-				if (strbuf_appendc(&buf, &buf_size, *p) != 0) {
-					saved_errno = errno;
-					goto out;
-				}
+				strbuf_appendc(&buf, &buf_size, -1);
+				strbuf_appendc(&buf, &buf_size, *p);
 			}
 			break;
 		case STATE_IN_WORD:
@@ -145,15 +150,14 @@
 			case '\n':	/* FALLTHROUGH */
 			case '\0':
 				/* end of token */
-				token = strdup(buf);
-				if (token == NULL) {
-					saved_errno = errno;
-					goto out;
-				}
-				tokenv[tokenc++] = token;
-				tokenv[tokenc] = NULL;
+				value = xstrdup(buf);
+				tokenv[tokenc] = xmalloc(sizeof (union tok));
+				tokenv[tokenc]->arg.type = TOK_ARG;
+				tokenv[tokenc]->arg.value = value;
+				tokenc++;
+
 				if (*p == '\0') {
-					retval = 0;
+					retval = TOK_ERR_OK;
 					goto out;
 				}
 				state = STATE_INITIAL;
@@ -170,10 +174,7 @@
 				break;
 			default:
 				/* regular character */
-				if (strbuf_appendc(&buf, &buf_size, *p) != 0) {
-					saved_errno = errno;
-					goto out;
-				}
+				strbuf_appendc(&buf, &buf_size, *p);
 			}
 			break;
 		case STATE_IN_QUOTE:
@@ -185,11 +186,7 @@
 					state = STATE_IN_WORD;
 				} else {
 					/* quote quote character */
-					if (strbuf_appendc(&buf, &buf_size,
-					    *p) != 0) {
-						saved_errno = errno;
-						goto out;
-					}
+					strbuf_appendc(&buf, &buf_size, *p);
 				}
 				break;
 			case '\\':
@@ -202,10 +199,7 @@
 				goto out;
 			default:
 				/* regular character */
-				if (strbuf_appendc(&buf, &buf_size, *p) != 0) {
-					saved_errno = errno;
-					goto out;
-				}
+				strbuf_appendc(&buf, &buf_size, *p);
 			}
 			break;
 		case STATE_IN_WORD_ESCAPE:	/* FALLTHROUGH */
@@ -218,9 +212,38 @@
 			/* escaped character */
 			state = (state == STATE_IN_WORD_ESCAPE) ?
 			    STATE_IN_WORD : STATE_IN_QUOTE;
-			if (strbuf_appendc(&buf, &buf_size, *p) != 0) {
-				saved_errno = errno;
-				goto out;
+			strbuf_appendc(&buf, &buf_size, *p);
+			break;
+		case STATE_IN_MACRO:
+			switch (*p) {
+			case ' ':	/* FALLTHROUGH */
+			case '\t':	/* FALLTHROUGH */
+			case '\n':	/* FALLTHROUGH */
+			case '\0':
+				/* end of token */
+				name = xstrdup(buf);
+				tokenv[tokenc] = xmalloc(sizeof (union tok));
+				tokenv[tokenc]->macro.type = TOK_MACRO;
+				tokenv[tokenc]->macro.name = name;
+				tokenc++;
+
+				if (*p == '\0') {
+					retval = TOK_ERR_OK;
+					goto out;
+				}
+				state = STATE_INITIAL;
+				break;
+			default:
+				/*
+				 * macro names must only contain alphanumeric
+				 * characters and underscores
+				 */
+				if (!isascii(*p) || (!isalnum(*p) &&
+				    (*p != '_'))) {
+					retval = TOK_ERR_INVALID_MACRO_NAME;
+					goto out;
+				}
+				strbuf_appendc(&buf, &buf_size, *p);
 			}
 			break;
 		}
@@ -229,18 +252,14 @@
 
 out:
 	if (retval < 0) {
-		for (i = 0; i < tokenc; i++) {
-			free(tokenv[i]);
-		}
-		free(tokenv);
+		tok_free(tokenv);
 	} else {
+		tokenv[tokenc] = NULL;
 		*tokencp = tokenc;
-		*tokenvp = realloc(tokenv, (tokenc + 1) * sizeof (char *));
+		*tokenvp = xrealloc(tokenv, (tokenc + 1) *
+		    sizeof (union tok *));
 	}
 	free(buf);
-	if (retval < 0) {
-		errno = saved_errno;
-	}
 
 	return (retval);
 }