blob: 64195b5a11f752b62cc4c5b4a40318e18d5dcbb0 [file] [log] [blame]
/* Handle quoted segments of a string.
Copyright (C) 2014-2016 Free Software Foundation, Inc.
Written by Daiki Ueno <ueno@gnu.org>, 2015.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
#ifndef _QUOTE_H
#define _QUOTE_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
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)
{
const char *p, *start, *end;
bool seen_opening;
/* START shall point to the beginning of a quoted segment, END
points to the end of the entire input string. */
start = input;
end = &input[length - 1];
/* True if we have seen a character which could be an opening
quotation mark. Note that we can't determine if it is really an
opening quotation mark until we see a closing quotation mark. */
seen_opening = false;
for (p = start; p <= end; p++)
{
switch (*p)
{
case '"':
if (seen_opening)
{
if (*start == '"')
{
if (p == start + 1)
/* Consider "" as "". */
callback ('\0', "\"\"", 2, data);
else
/* "..." */
callback ('"', start + 1, p - (start + 1), data);
start = p + 1;
seen_opening = false;
}
}
else
{
callback ('\0', start, p - start, data);
start = p;
seen_opening = true;
}
break;
case '`':
if (seen_opening)
{
if (*start == '`')
{
callback ('\0', start, p - start, data);
start = p;
}
}
else
{
callback ('\0', start, p - start, data);
start = p;
seen_opening = true;
}
break;
case '\'':
if (seen_opening)
{
if (/* `...' */
*start == '`'
/* '...', where
- The left quote is preceded by a space, and the
right quote is followed by a space.
- The left quote is preceded by a space, and the
right quote is at the end of line.
- The left quote is at the beginning of the line, and
the right quote is followed by a space. */
|| (*start == '\''
&& (((start > input && *(start - 1) == ' ')
&& (p == end || *(p + 1) == '\n' || *(p + 1) == ' '))
|| ((start == input || *(start - 1) == '\n')
&& p < end && *(p + 1) == ' '))))
{
callback ('\'', start + 1, p - (start + 1), data);
start = p + 1;
}
else
{
callback ('\0', start, p - start, data);
start = p;
}
seen_opening = false;
}
else if (p == input || *(p - 1) == '\n' || *(p - 1) == ' ')
{
callback ('\0', start, p - start, data);
start = p;
seen_opening = true;
}
break;
}
}
/* Copy the rest. */
if (p > start)
callback ('\0', start, p - start, data);
}
#ifdef __cplusplus
}
#endif
#endif /* _QUOTE_H */