static int quoted_term;
#define quoted_term_char ((unsigned char)quoted_term)
#define WHEN_QUOTED_TERM(x) ((quoted_term >= 0) && (x))
#define QUOTED_TERM_P(c) WHEN_QUOTED_TERM((c) == quoted_term_char)


#define STR_FUNC_ESCAPE 0x01
#define STR_FUNC_EXPAND 0x02
#define STR_FUNC_REGEXP 0x04
#define STR_FUNC_QWORDS 0x08
#define STR_FUNC_INDENT 0x20

enum string_type {
    str_squote = (0),
    str_dquote = (STR_FUNC_EXPAND),
    str_xquote = (STR_FUNC_ESCAPE|STR_FUNC_EXPAND),
    str_regexp = (STR_FUNC_REGEXP|STR_FUNC_ESCAPE|STR_FUNC_EXPAND),
    str_sword  = (STR_FUNC_QWORDS),
    str_dword  = (STR_FUNC_QWORDS|STR_FUNC_EXPAND),
};


#define NEW_STRTERM(func, term, paren) \
	rb_node_newnode(NODE_STRTERM, (func), (term), (paren))


static int
parse_string(quote)
    NODE *quote;
{
    int func = quote->nd_func;
    int term = quote->nd_term;
    int paren = quote->nd_paren;
    int c, space = 0;

    if (func == -1) return tSTRING_END;
    c = nextc();
    if (ISSPACE(c)) {
	do {c = nextc();} while (ISSPACE(c));
	space = 1;
    }
    if (c == term) {
	if (!lex_strnest) {
	  eos:
            quote->nd_func = -1;
            return ' ';
	}
    }
    if (c == '\\' && WHEN_QUOTED_TERM(peek(quoted_term_char))) {
	if ((c = nextc()) == term) goto eos;
    }
    if (space) {
	pushback(c);
	return ' ';
    }
    newtok();
    pushback(c);
    if (tokadd_string(func, term, paren) == -1) {
	ruby_sourceline = nd_line(quote);
	rb_compile_error("unterminated string meets end of file");
	return tSTRING_END;
    }

    tokfix();
    yylval.node = NEW_STR(rb_str_new(tok(), toklen()));
    return tSTRING_CONTENT;
}

static int
tokadd_string(func, term, paren)
    int func, term, paren;
{
    int c;

    while ((c = nextc()) != -1) {
	if (paren && c == paren) {
	    lex_strnest++;
	}
	else if (c == term) {
	    if (!lex_strnest) {
		pushback(c);
		break;
	    }
	    --lex_strnest;
	}
	else if (c == '\\') {
	    c = nextc();
	    if (QUOTED_TERM_P(c)) {
		pushback(c);
		return c;
	    }
	    switch (c) {
	      case '\n':
		continue;

	      case '\\':
		break;

	      default:
		if (ISSPACE(c)) {
		    /* ignore backslashed spaces in %w */
		}
		else if (c != term && !(paren && c == paren)) {
		    tokadd('\\');
		}
	    }
	}
	else if (ismbchar(c)) {
	    int i, len = mbclen(c)-1;

	    for (i = 0; i < len; i++) {
		tokadd(c);
		c = nextc();
	    }
	}
	else if (ISSPACE(c)) {
	    pushback(c);
	    break;
	}
	tokadd(c);
    }
    return c;
}
