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