1/*	$NetBSD: split_qnameval.c,v 1.2 2020/03/18 19:05:22 christos Exp $	*/
2
3/*++
4/* NAME
5/*	split_qnameval 3
6/* SUMMARY
7/*	name-value splitter
8/* SYNOPSIS
9/*	#include <stringops.h>
10/*
11/*	const char *split_qnameval(buf, name, value)
12/*	char    *buf;
13/*	char    **name;
14/*	char    **value;
15/* DESCRIPTION
16/*	split_qnameval() expects text of the form "key = value"
17/*	or "key =", where the key may be quoted with backslash or
18/*      double quotes. The buffer argument is broken up into the key
19/*      and value substrings.
20/*
21/*	Arguments:
22/* .IP buf
23/*	Result from readlline() or equivalent. The buffer is modified.
24/* .IP key
25/*	Upon successful completion, this is set to the key
26/*	substring.
27/* .IP value
28/*	Upon successful completion, this is set to the value
29/*	substring.
30/* SEE ALSO
31/*	split_nameval(3) name-value splitter
32/* BUGS
33/* DIAGNOSTICS
34/*	The result is a null pointer in case of success, a string
35/*	describing the error otherwise: missing '=' after attribute
36/*	name; missing attribute name.
37/* LICENSE
38/* .ad
39/* .fi
40/*	The Secure Mailer license must be distributed with this software.
41/* AUTHOR(S)
42/*	Wietse Venema
43/*	Google, Inc.
44/*	111 8th Avenue
45/*	New York, NY 10011, USA
46/*--*/
47
48/* System libraries. */
49
50#include <sys_defs.h>
51#include <ctype.h>
52#include <string.h>
53
54/* Utility library. */
55
56#include <msg.h>
57#include <stringops.h>
58
59/* split_qnameval - split "key = value", support quoted key */
60
61const char *split_qnameval(char *buf, char **pkey, char **pvalue)
62{
63    int     in_quotes = 0;
64    char   *key;
65    char   *key_end;
66    char   *value;
67
68    for (key = buf; *key && ISSPACE(*key); key++)
69	 /* void */ ;
70    if (*key == 0)
71	return ("no key found; expected format: key = value");
72
73    for (key_end = key; *key_end; key_end++) {
74	if (*key_end == '\\') {
75	    if (*++key_end == 0)
76		break;
77	} else if (ISSPACE(*key_end) || *key_end == '=') {
78	    if (!in_quotes)
79		break;
80	} else if (*key_end == '"') {
81	    in_quotes = !in_quotes;
82	}
83    }
84    if (in_quotes) {
85	return ("unbalanced '\"\'");
86    }
87    value = key_end;
88    while (ISSPACE(*value))
89	value++;
90    if (*value != '=')
91	return ("missing '=' after attribute name");
92    *key_end = 0;
93    do {
94	value++;
95    } while (ISSPACE(*value));
96    trimblanks(value, 0)[0] = 0;
97    *pkey = key;
98    *pvalue = value;
99    return (0);
100}
101
102#ifdef TEST
103
104#include <stdlib.h>
105#include <unistd.h>
106#include <string.h>
107
108#include <mymalloc.h>
109
110static int compare(int test_number, const char *what,
111		           const char *expect, const char *real)
112{
113    if ((expect == 0 && real == 0)
114	|| (expect != 0 && real != 0 && strcmp(expect, real) == 0)) {
115	return (0);
116    } else {
117	msg_warn("test %d: %s mis-match: expect='%s', real='%s'",
118		 test_number, what, expect ? expect : "(null)",
119		 real ? real : "(null)");
120	return (1);
121    }
122}
123
124int     main(int argc, char **argv)
125{
126    struct test_info {
127	const char *input;
128	const char *expect_result;
129	const char *expect_key;
130	const char *expect_value;
131    };
132    static const struct test_info test_info[] = {
133	/* Unquoted keys. */
134	{"xx = yy", 0, "xx", "yy"},
135	{"xx=yy", 0, "xx", "yy"},
136	{"xx =", 0, "xx", ""},
137	{"xx=", 0, "xx", ""},
138	{"xx", "missing '=' after attribute name", 0, 0},
139	/* Quoted keys. */
140	{"\"xx \" = yy", 0, "\"xx \"", "yy"},
141	{"\"xx \"= yy", 0, "\"xx \"", "yy"},
142	{"\"xx \" =", 0, "\"xx \"", ""},
143	{"\"xx \"=", 0, "\"xx \"", ""},
144	{"\"xx \"", "missing '=' after attribute name", 0, 0},
145	{"\"xx ", "unbalanced '\"'", 0, 0},
146	/* Backslash escapes. */
147	{"\"\\\"xx \" = yy", 0, "\"\\\"xx \"", "yy"},
148	{0,},
149    };
150
151    int     errs = 0;
152    const struct test_info *tp;
153
154    for (tp = test_info; tp->input != 0; tp++) {
155	const char *result;
156	char   *key = 0;
157	char   *value = 0;
158	char   *buf = mystrdup(tp->input);
159	int     test_number = (int) (tp - test_info);
160
161	result = split_qnameval(buf, &key, &value);
162	errs += compare(test_number, "result", tp->expect_result, result);
163	errs += compare(test_number, "key", tp->expect_key, key);
164	errs += compare(test_number, "value", tp->expect_value, value);
165	myfree(buf);
166    }
167    exit(errs);
168}
169
170#endif
171