소스 검색

Qdevice: Improve simple lex and add unit test

Simple lex now support backslashes and quotes. Behavior is
similar to shell.

Signed-off-by: Jan Friesse <jfriesse@redhat.com>
Jan Friesse 9 년 전
부모
커밋
c53d901143
6개의 변경된 파일342개의 추가작업 그리고 11개의 파일을 삭제
  1. 3 2
      qdevices/Makefile.am
  2. 115 6
      qdevices/dynar-simple-lex.c
  3. 9 1
      qdevices/dynar-simple-lex.h
  4. 1 1
      qdevices/qdevice-ipc.c
  5. 209 0
      qdevices/test-dynar-simple-lex.c
  6. 5 1
      qdevices/test-qnetd-cluster-list.c

+ 3 - 2
qdevices/Makefile.am

@@ -77,8 +77,8 @@ corosync-qnetd-certutil: corosync-qnetd-certutil.sh
 	    -e 's#@''COROSYSCONFDIR@#${COROSYSCONFDIR}#g' \
 	    -e 's#@''COROSYSCONFDIR@#${COROSYSCONFDIR}#g' \
 	    $< > $@
 	    $< > $@
 
 
-TESTS				= qnetd-cluster-list.test dynar.test
-check_PROGRAMS			= qnetd-cluster-list.test dynar.test
+TESTS				= qnetd-cluster-list.test dynar.test dynar-simple-lex.test
+check_PROGRAMS			= qnetd-cluster-list.test dynar.test dynar-simple-lex.test
 
 
 qnetd_cluster_list_test_SOURCES	= qnetd-cluster-list.c test-qnetd-cluster-list.c \
 qnetd_cluster_list_test_SOURCES	= qnetd-cluster-list.c test-qnetd-cluster-list.c \
 				    qnetd-client-list.c qnetd-client.c dynar.c node-list.c \
 				    qnetd-client-list.c qnetd-client.c dynar.c node-list.c \
@@ -87,5 +87,6 @@ qnetd_cluster_list_test_CFLAGS  = $(nss_CFLAGS)
 qnetd_cluster_list_test_LDADD	= $(nss_LIBS)
 qnetd_cluster_list_test_LDADD	= $(nss_LIBS)
 
 
 dynar_test_SOURCES		= test-dynar.c dynar.c dynar-str.c
 dynar_test_SOURCES		= test-dynar.c dynar.c dynar-str.c
+dynar_simple_lex_test_SOURCES	= test-dynar-simple-lex.c dynar.c dynar-str.c dynar-simple-lex.c
 
 
 endif
 endif

+ 115 - 6
qdevices/dynar-simple-lex.c

@@ -46,11 +46,13 @@ dynar_simple_lex_is_space(char ch)
 }
 }
 
 
 void
 void
-dynar_simple_lex_init(struct dynar_simple_lex *lex, struct dynar *input)
+dynar_simple_lex_init(struct dynar_simple_lex *lex, struct dynar *input,
+    enum dynar_simple_lex_type lex_type)
 {
 {
 
 
 	memset(lex, 0, sizeof(*lex));
 	memset(lex, 0, sizeof(*lex));
 	lex->input = input;
 	lex->input = input;
+	lex->lex_type = lex_type;
 	dynar_init(&lex->token, dynar_max_size(input));
 	dynar_init(&lex->token, dynar_max_size(input));
 }
 }
 
 
@@ -68,18 +70,125 @@ dynar_simple_lex_token_next(struct dynar_simple_lex *lex)
 	size_t pos;
 	size_t pos;
 	size_t size;
 	size_t size;
 	char *str;
 	char *str;
-	char ch;
+	char ch, ch2;
+	int add_char;
+	int state;
 
 
 	dynar_clean(&lex->token);
 	dynar_clean(&lex->token);
 
 
 	size = dynar_size(lex->input);
 	size = dynar_size(lex->input);
 	str = dynar_data(lex->input);
 	str = dynar_data(lex->input);
 
 
-	for (pos = lex->pos; pos < size && dynar_simple_lex_is_space(str[pos]) && str[pos] != '\n'; pos++) ;
+	state = 1;
+	pos = lex->pos;
 
 
-	for (; pos < size && !dynar_simple_lex_is_space(str[pos]); pos++) {
-		if (dynar_cat(&lex->token, &str[pos], sizeof(*str)) != 0) {
-			return (NULL);
+	while (state != 0) {
+		if (pos < size) {
+			ch = str[pos];
+		} else {
+			ch = '\0';
+		}
+
+		add_char = 0;
+
+		switch (state) {
+		case 1:
+			/*
+			 * Skip spaces. Newline is special and means end of processing
+			 */
+			if (pos >= size || ch == '\n' || ch == '\r') {
+				state = 0;
+			} else if (dynar_simple_lex_is_space(ch)) {
+				pos++;
+			} else {
+				state = 2;
+			}
+			break;
+		case 2:
+			/*
+			 * Read word
+			 */
+			if (pos >= size) {
+				state = 0;
+			} else if ((lex->lex_type == DYNAR_SIMPLE_LEX_TYPE_BACKSLASH ||
+			    lex->lex_type == DYNAR_SIMPLE_LEX_TYPE_QUOTE) && ch == '\\') {
+				pos++;
+				state = 3;
+			} else if (lex->lex_type == DYNAR_SIMPLE_LEX_TYPE_QUOTE &&
+			    ch == '"') {
+				pos++;
+				state = 4;
+			} else if (dynar_simple_lex_is_space(ch)) {
+				state = 0;
+			} else {
+				pos++;
+				add_char = 1;
+			}
+			break;
+		case 3:
+			/*
+			 * Process backslash
+			 */
+			if (pos >= size || ch == '\n' || ch == '\r') {
+				/*
+				 * End of string. Do not include backslash (it's just ignored)
+				 */
+				state = 0;
+			} else {
+				add_char = 1;
+				state = 2;
+				pos++;
+			}
+			break;
+		case 4:
+			/*
+			 * Quote word
+			 */
+			if (pos >= size) {
+				state = 0;
+			} else if (ch == '\\') {
+				state = 5;
+				pos++;
+			} else if (ch == '"') {
+				state = 2;
+				pos++;
+			} else if (ch == '\n' || ch == '\r') {
+				state = 0;
+			} else {
+				pos++;
+				add_char = 1;
+			}
+			break;
+		case 5:
+			/*
+			 * Quote word backslash
+			 */
+			if (pos >= size || ch == '\n' || ch == '\r') {
+				/*
+				 * End of string. Do not include backslash (it's just ignored)
+				 */
+				state = 0;
+			} else if (ch == '\\' || ch == '"') {
+				add_char = 1;
+				state = 4;
+				pos++;
+			} else {
+				ch2 = '\\';
+				if (dynar_cat(&lex->token, &ch2, sizeof(ch2)) != 0) {
+					return (NULL);
+				}
+
+				add_char = 1;
+				state = 4;
+				pos++;
+			}
+			break;
+		}
+
+		if (add_char) {
+			if (dynar_cat(&lex->token, &ch, sizeof(ch)) != 0) {
+				return (NULL);
+			}
 		}
 		}
 	}
 	}
 
 

+ 9 - 1
qdevices/dynar-simple-lex.h

@@ -41,13 +41,21 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
+enum dynar_simple_lex_type {
+	DYNAR_SIMPLE_LEX_TYPE_PLAIN,
+	DYNAR_SIMPLE_LEX_TYPE_BACKSLASH,
+	DYNAR_SIMPLE_LEX_TYPE_QUOTE,
+};
+
 struct dynar_simple_lex {
 struct dynar_simple_lex {
 	struct dynar token;
 	struct dynar token;
 	struct dynar *input;
 	struct dynar *input;
+	enum dynar_simple_lex_type lex_type;
 	size_t pos;
 	size_t pos;
 };
 };
 
 
-extern void	 	 dynar_simple_lex_init(struct dynar_simple_lex *lex, struct dynar *input);
+extern void	 	 dynar_simple_lex_init(struct dynar_simple_lex *lex, struct dynar *input,
+    enum dynar_simple_lex_type lex_type);
 
 
 extern void	 	 dynar_simple_lex_destroy(struct dynar_simple_lex *lex);
 extern void	 	 dynar_simple_lex_destroy(struct dynar_simple_lex *lex);
 
 

+ 1 - 1
qdevices/qdevice-ipc.c

@@ -191,7 +191,7 @@ qdevice_ipc_parse_line(struct qdevice_instance *instance, struct unix_socket_cli
 
 
 	ipc_user_data = (struct qdevice_ipc_user_data *)client->user_data;
 	ipc_user_data = (struct qdevice_ipc_user_data *)client->user_data;
 
 
-	dynar_simple_lex_init(&lex, &client->receive_buffer);
+	dynar_simple_lex_init(&lex, &client->receive_buffer, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
 	token = dynar_simple_lex_token_next(&lex);
 	token = dynar_simple_lex_token_next(&lex);
 
 
 	verbose = 0;
 	verbose = 0;

+ 209 - 0
qdevices/test-dynar-simple-lex.c

@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "dynar-simple-lex.h"
+
+int
+main(void)
+{
+	struct dynar input_str;
+	struct dynar_simple_lex lex;
+	struct dynar *output_str;
+
+	dynar_init(&input_str, 128);
+
+	dynar_str_catf(&input_str, "token1 token2");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "token1") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "token2") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "	 token1			   token2		");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "token1") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "token2") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "      token1		 	token2	 	\ntoken3");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "token1") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "token2") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "\\ab\\cd e\\fg\\ h i\\");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "\\ab\\cd") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "e\\fg\\") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "h") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "i\\") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, " a b\rc");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "a") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "b") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "\\ab\\\\cd e\\fg\\ h i\\");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_BACKSLASH);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "ab\\cd") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "efg h") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "i") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "ab\\\\cd e\\fg\\ h ij\\\na");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_BACKSLASH);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "ab\\cd") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "efg h") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "ij") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, " a b\\\rc");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_BACKSLASH);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "a") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "b") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "abc def \"ghi\" jkl \"m n	o\"");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "abc") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "def") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "ghi") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "jkl") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "m n	o") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "a\\bc \"d\\e \\\"f\\\\  \\\"  \"g hij");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "abc") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "d\\e \"f\\  \"  g") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "hij") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "abc \"d e  \r\n");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "abc") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "d e  ") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+	dynar_str_cpy(&input_str, "");
+	dynar_str_catf(&input_str, "abc \"d e  \\\"\\\r\n");
+	dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "abc") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "d e  \"") == 0);
+	assert((output_str = dynar_simple_lex_token_next(&lex)) != NULL);
+	assert(strcmp(dynar_data(output_str), "") == 0);
+	dynar_simple_lex_destroy(&lex);
+
+
+	dynar_destroy(&input_str);
+
+	return (0);
+}

+ 5 - 1
qdevices/test-qnetd-cluster-list.c

@@ -50,10 +50,14 @@ add_client(const char *cluster_name, size_t cluster_name_len,
 	PRNetAddr addr;
 	PRNetAddr addr;
 	struct qnetd_client *tmp_client;
 	struct qnetd_client *tmp_client;
 	struct qnetd_cluster *tmp_cluster;
 	struct qnetd_cluster *tmp_cluster;
+	char *client_addr_str;
 
 
 	memset(&addr, 0, sizeof(addr));
 	memset(&addr, 0, sizeof(addr));
 
 
-	tmp_client = qnetd_client_list_add(&clients, NULL, &addr, 1000, 2, 1000, NULL);
+	client_addr_str = strdup("addrstr");
+	assert(client_addr_str != NULL);
+
+	tmp_client = qnetd_client_list_add(&clients, NULL, &addr, client_addr_str, 1000, 2, 1000, NULL);
 	assert(tmp_client != NULL);
 	assert(tmp_client != NULL);
 	tmp_client->cluster_name = malloc(cluster_name_len + 1);
 	tmp_client->cluster_name = malloc(cluster_name_len + 1);
 	assert(tmp_client->cluster_name != NULL);
 	assert(tmp_client->cluster_name != NULL);