1 /* Handle quoted segments of a string.
2 Copyright (C) 2014-2016 Free Software Foundation, Inc.
3 Written by Daiki Ueno <ueno@gnu.org>, 2015.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18 #ifndef _QUOTE_H
19 #define _QUOTE_H
20
21 #include <stdbool.h>
22
23
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27
28 static void
scan_quoted(const char * input,size_t length,void (* callback)(char quote,const char * quoted,size_t quoted_length,void * data),void * data)29 scan_quoted (const char *input, size_t length,
30 void (* callback) (char quote, const char *quoted,
31 size_t quoted_length,
32 void *data),
33 void *data)
34 {
35 const char *p, *start, *end;
36 bool seen_opening;
37
38 /* START shall point to the beginning of a quoted segment, END
39 points to the end of the entire input string. */
40 start = input;
41 end = &input[length - 1];
42
43 /* True if we have seen a character which could be an opening
44 quotation mark. Note that we can't determine if it is really an
45 opening quotation mark until we see a closing quotation mark. */
46 seen_opening = false;
47
48 for (p = start; p <= end; p++)
49 {
50 switch (*p)
51 {
52 case '"':
53 if (seen_opening)
54 {
55 if (*start == '"')
56 {
57 if (p == start + 1)
58 /* Consider "" as "". */
59 callback ('\0', "\"\"", 2, data);
60 else
61 /* "..." */
62 callback ('"', start + 1, p - (start + 1), data);
63
64 start = p + 1;
65 seen_opening = false;
66 }
67 }
68 else
69 {
70 callback ('\0', start, p - start, data);
71 start = p;
72 seen_opening = true;
73 }
74 break;
75
76 case '`':
77 if (seen_opening)
78 {
79 if (*start == '`')
80 {
81 callback ('\0', start, p - start, data);
82 start = p;
83 }
84 }
85 else
86 {
87 callback ('\0', start, p - start, data);
88 start = p;
89 seen_opening = true;
90 }
91 break;
92
93 case '\'':
94 if (seen_opening)
95 {
96 if (/* `...' */
97 *start == '`'
98 /* '...', where
99 - The left quote is preceded by a space, and the
100 right quote is followed by a space.
101 - The left quote is preceded by a space, and the
102 right quote is at the end of line.
103 - The left quote is at the beginning of the line, and
104 the right quote is followed by a space. */
105 || (*start == '\''
106 && (((start > input && *(start - 1) == ' ')
107 && (p == end || *(p + 1) == '\n' || *(p + 1) == ' '))
108 || ((start == input || *(start - 1) == '\n')
109 && p < end && *(p + 1) == ' '))))
110 {
111 callback ('\'', start + 1, p - (start + 1), data);
112 start = p + 1;
113 }
114 else
115 {
116 callback ('\0', start, p - start, data);
117 start = p;
118 }
119 seen_opening = false;
120 }
121 else if (p == input || *(p - 1) == '\n' || *(p - 1) == ' ')
122 {
123 callback ('\0', start, p - start, data);
124 start = p;
125 seen_opening = true;
126 }
127 break;
128 }
129 }
130
131 /* Copy the rest. */
132 if (p > start)
133 callback ('\0', start, p - start, data);
134 }
135
136
137 #ifdef __cplusplus
138 }
139 #endif
140
141
142 #endif /* _QUOTE_H */
143