xref: /freebsd/usr.bin/tr/str.c (revision bdcbfde31e8e9b343f113a1956384bdf30d1ed62)
19b50d902SRodney W. Grimes /*-
2*8a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3*8a16b7a1SPedro F. Giffuni  *
49b50d902SRodney W. Grimes  * Copyright (c) 1991, 1993
59b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
69b50d902SRodney W. Grimes  *
79b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
89b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
99b50d902SRodney W. Grimes  * are met:
109b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
119b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
129b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
139b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
149b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
169b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
179b50d902SRodney W. Grimes  *    without specific prior written permission.
189b50d902SRodney W. Grimes  *
199b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299b50d902SRodney W. Grimes  * SUCH DAMAGE.
309b50d902SRodney W. Grimes  */
319b50d902SRodney W. Grimes 
3278732475SMark Murray 
339b50d902SRodney W. Grimes 
349b50d902SRodney W. Grimes #include <sys/types.h>
359b50d902SRodney W. Grimes 
36af647767SPhilippe Charnier #include <ctype.h>
37af647767SPhilippe Charnier #include <err.h>
38ca99cfddSTim J. Robbins #include <errno.h>
39821df508SXin LI #include <stddef.h>
40821df508SXin LI #include <stdio.h>
419b50d902SRodney W. Grimes #include <stdlib.h>
429b50d902SRodney W. Grimes #include <string.h>
43ca99cfddSTim J. Robbins #include <wchar.h>
44ca99cfddSTim J. Robbins #include <wctype.h>
459b50d902SRodney W. Grimes 
469b50d902SRodney W. Grimes #include "extern.h"
479b50d902SRodney W. Grimes 
4821f53e91SAndrey A. Chernov static int      backslash(STR *, int *);
493f330d7dSWarner Losh static int	bracket(STR *);
503f330d7dSWarner Losh static void	genclass(STR *);
513f330d7dSWarner Losh static void	genequiv(STR *);
525b4fa425SAndrey A. Chernov static int      genrange(STR *, int);
533f330d7dSWarner Losh static void	genseq(STR *);
549b50d902SRodney W. Grimes 
55ca99cfddSTim J. Robbins wint_t
next(STR * s)56b173dd44SEd Schouten next(STR *s)
579b50d902SRodney W. Grimes {
58ca99cfddSTim J. Robbins 	int is_octal;
59ca99cfddSTim J. Robbins 	wint_t ch;
60ca99cfddSTim J. Robbins 	wchar_t wch;
61ca99cfddSTim J. Robbins 	size_t clen;
629b50d902SRodney W. Grimes 
639b50d902SRodney W. Grimes 	switch (s->state) {
649b50d902SRodney W. Grimes 	case EOS:
659b50d902SRodney W. Grimes 		return (0);
669b50d902SRodney W. Grimes 	case INFINITE:
679b50d902SRodney W. Grimes 		return (1);
689b50d902SRodney W. Grimes 	case NORMAL:
69ca99cfddSTim J. Robbins 		switch (*s->str) {
709b50d902SRodney W. Grimes 		case '\0':
719b50d902SRodney W. Grimes 			s->state = EOS;
729b50d902SRodney W. Grimes 			return (0);
739b50d902SRodney W. Grimes 		case '\\':
7421f53e91SAndrey A. Chernov 			s->lastch = backslash(s, &is_octal);
759b50d902SRodney W. Grimes 			break;
769b50d902SRodney W. Grimes 		case '[':
779b50d902SRodney W. Grimes 			if (bracket(s))
789b50d902SRodney W. Grimes 				return (next(s));
799b50d902SRodney W. Grimes 			/* FALLTHROUGH */
809b50d902SRodney W. Grimes 		default:
81ca99cfddSTim J. Robbins 			clen = mbrtowc(&wch, s->str, MB_LEN_MAX, NULL);
82ca99cfddSTim J. Robbins 			if (clen == (size_t)-1 || clen == (size_t)-2 ||
83ca99cfddSTim J. Robbins 			    clen == 0)
84ca99cfddSTim J. Robbins 				errc(1, EILSEQ, NULL);
8521f53e91SAndrey A. Chernov 			is_octal = 0;
86ca99cfddSTim J. Robbins 			s->lastch = wch;
87ca99cfddSTim J. Robbins 			s->str += clen;
889b50d902SRodney W. Grimes 			break;
899b50d902SRodney W. Grimes 		}
909b50d902SRodney W. Grimes 
919b50d902SRodney W. Grimes 		/* We can start a range at any time. */
925b4fa425SAndrey A. Chernov 		if (s->str[0] == '-' && genrange(s, is_octal))
939b50d902SRodney W. Grimes 			return (next(s));
949b50d902SRodney W. Grimes 		return (1);
9530c11564SAndrey A. Chernov 	case RANGE:
9630c11564SAndrey A. Chernov 		if (s->cnt-- == 0) {
9730c11564SAndrey A. Chernov 			s->state = NORMAL;
9830c11564SAndrey A. Chernov 			return (next(s));
9930c11564SAndrey A. Chernov 		}
10030c11564SAndrey A. Chernov 		++s->lastch;
10130c11564SAndrey A. Chernov 		return (1);
1029b50d902SRodney W. Grimes 	case SEQUENCE:
1039b50d902SRodney W. Grimes 		if (s->cnt-- == 0) {
1049b50d902SRodney W. Grimes 			s->state = NORMAL;
1059b50d902SRodney W. Grimes 			return (next(s));
1069b50d902SRodney W. Grimes 		}
1079b50d902SRodney W. Grimes 		return (1);
108ca99cfddSTim J. Robbins 	case CCLASS:
109ca99cfddSTim J. Robbins 	case CCLASS_UPPER:
110ca99cfddSTim J. Robbins 	case CCLASS_LOWER:
111ca99cfddSTim J. Robbins 		s->cnt++;
112ca99cfddSTim J. Robbins 		ch = nextwctype(s->lastch, s->cclass);
113ca99cfddSTim J. Robbins 		if (ch == -1) {
114ca99cfddSTim J. Robbins 			s->state = NORMAL;
115ca99cfddSTim J. Robbins 			return (next(s));
116ca99cfddSTim J. Robbins 		}
117ca99cfddSTim J. Robbins 		s->lastch = ch;
118ca99cfddSTim J. Robbins 		return (1);
1199b50d902SRodney W. Grimes 	case SET:
120e42eb683SAndrey A. Chernov 		if ((ch = s->set[s->cnt++]) == OOBCH) {
1219b50d902SRodney W. Grimes 			s->state = NORMAL;
1229b50d902SRodney W. Grimes 			return (next(s));
1239b50d902SRodney W. Grimes 		}
124e42eb683SAndrey A. Chernov 		s->lastch = ch;
1259b50d902SRodney W. Grimes 		return (1);
12678732475SMark Murray 	default:
12778732475SMark Murray 		return (0);
1289b50d902SRodney W. Grimes 	}
1299b50d902SRodney W. Grimes 	/* NOTREACHED */
1309b50d902SRodney W. Grimes }
1319b50d902SRodney W. Grimes 
1329b50d902SRodney W. Grimes static int
bracket(STR * s)133b173dd44SEd Schouten bracket(STR *s)
1349b50d902SRodney W. Grimes {
13578732475SMark Murray 	char *p;
1369b50d902SRodney W. Grimes 
1379b50d902SRodney W. Grimes 	switch (s->str[1]) {
1389b50d902SRodney W. Grimes 	case ':':				/* "[:class:]" */
139232a0ff5STim J. Robbins 		if ((p = strchr(s->str + 2, ']')) == NULL)
1409b50d902SRodney W. Grimes 			return (0);
141232a0ff5STim J. Robbins 		if (*(p - 1) != ':' || p - s->str < 4)
142232a0ff5STim J. Robbins 			goto repeat;
143232a0ff5STim J. Robbins 		*(p - 1) = '\0';
1449b50d902SRodney W. Grimes 		s->str += 2;
1459b50d902SRodney W. Grimes 		genclass(s);
146232a0ff5STim J. Robbins 		s->str = p + 1;
1479b50d902SRodney W. Grimes 		return (1);
1489b50d902SRodney W. Grimes 	case '=':				/* "[=equiv=]" */
14988deede5SJilles Tjoelker 		if (s->str[2] == '\0' || (p = strchr(s->str + 3, ']')) == NULL)
1509b50d902SRodney W. Grimes 			return (0);
151232a0ff5STim J. Robbins 		if (*(p - 1) != '=' || p - s->str < 4)
152232a0ff5STim J. Robbins 			goto repeat;
1539b50d902SRodney W. Grimes 		s->str += 2;
1549b50d902SRodney W. Grimes 		genequiv(s);
1559b50d902SRodney W. Grimes 		return (1);
1569b50d902SRodney W. Grimes 	default:				/* "[\###*n]" or "[#*n]" */
157232a0ff5STim J. Robbins 	repeat:
1589b50d902SRodney W. Grimes 		if ((p = strpbrk(s->str + 2, "*]")) == NULL)
1599b50d902SRodney W. Grimes 			return (0);
160b3608ae1SEd Schouten 		if (p[0] != '*' || strchr(p, ']') == NULL)
1619b50d902SRodney W. Grimes 			return (0);
1629b50d902SRodney W. Grimes 		s->str += 1;
1639b50d902SRodney W. Grimes 		genseq(s);
1649b50d902SRodney W. Grimes 		return (1);
1659b50d902SRodney W. Grimes 	}
1669b50d902SRodney W. Grimes 	/* NOTREACHED */
1679b50d902SRodney W. Grimes }
1689b50d902SRodney W. Grimes 
1699b50d902SRodney W. Grimes static void
genclass(STR * s)170b173dd44SEd Schouten genclass(STR *s)
1719b50d902SRodney W. Grimes {
1729b50d902SRodney W. Grimes 
173ca99cfddSTim J. Robbins 	if ((s->cclass = wctype(s->str)) == 0)
174af647767SPhilippe Charnier 		errx(1, "unknown class %s", s->str);
1759b50d902SRodney W. Grimes 	s->cnt = 0;
176ca99cfddSTim J. Robbins 	s->lastch = -1;		/* incremented before check in next() */
17700611f04SAndrey A. Chernov 	if (strcmp(s->str, "upper") == 0)
178ca99cfddSTim J. Robbins 		s->state = CCLASS_UPPER;
179035944c3SAndrey A. Chernov 	else if (strcmp(s->str, "lower") == 0)
180ca99cfddSTim J. Robbins 		s->state = CCLASS_LOWER;
181035944c3SAndrey A. Chernov 	else
182ca99cfddSTim J. Robbins 		s->state = CCLASS;
1839b50d902SRodney W. Grimes }
1849b50d902SRodney W. Grimes 
1859b50d902SRodney W. Grimes static void
genequiv(STR * s)186b173dd44SEd Schouten genequiv(STR *s)
1879b50d902SRodney W. Grimes {
18885f6c317STim J. Robbins 	int i, p, pri;
18985f6c317STim J. Robbins 	char src[2], dst[3];
190ca99cfddSTim J. Robbins 	size_t clen;
191ca99cfddSTim J. Robbins 	wchar_t wc;
19285f6c317STim J. Robbins 
1939b50d902SRodney W. Grimes 	if (*s->str == '\\') {
19421f53e91SAndrey A. Chernov 		s->equiv[0] = backslash(s, NULL);
1959b50d902SRodney W. Grimes 		if (*s->str != '=')
196af647767SPhilippe Charnier 			errx(1, "misplaced equivalence equals sign");
197e73c3d27STim J. Robbins 		s->str += 2;
1989b50d902SRodney W. Grimes 	} else {
199ca99cfddSTim J. Robbins 		clen = mbrtowc(&wc, s->str, MB_LEN_MAX, NULL);
200ca99cfddSTim J. Robbins 		if (clen == (size_t)-1 || clen == (size_t)-2 || clen == 0)
201ca99cfddSTim J. Robbins 			errc(1, EILSEQ, NULL);
202ca99cfddSTim J. Robbins 		s->equiv[0] = wc;
203ca99cfddSTim J. Robbins 		if (s->str[clen] != '=')
204af647767SPhilippe Charnier 			errx(1, "misplaced equivalence equals sign");
205ca99cfddSTim J. Robbins 		s->str += clen + 2;
2069b50d902SRodney W. Grimes 	}
20785f6c317STim J. Robbins 
20885f6c317STim J. Robbins 	/*
20985f6c317STim J. Robbins 	 * Calculate the set of all characters in the same equivalence class
21085f6c317STim J. Robbins 	 * as the specified character (they will have the same primary
21185f6c317STim J. Robbins 	 * collation weights).
21285f6c317STim J. Robbins 	 * XXX Knows too much about how strxfrm() is implemented. Assumes
21385f6c317STim J. Robbins 	 * it fills the string with primary collation weight bytes. Only one-
21485f6c317STim J. Robbins 	 * to-one mappings are supported.
215ca99cfddSTim J. Robbins 	 * XXX Equivalence classes not supported in multibyte locales.
21685f6c317STim J. Robbins 	 */
217ca99cfddSTim J. Robbins 	src[0] = (char)s->equiv[0];
21885f6c317STim J. Robbins 	src[1] = '\0';
219ca99cfddSTim J. Robbins 	if (MB_CUR_MAX == 1 && strxfrm(dst, src, sizeof(dst)) == 1) {
22085f6c317STim J. Robbins 		pri = (unsigned char)*dst;
221ca99cfddSTim J. Robbins 		for (p = 1, i = 1; i < NCHARS_SB; i++) {
22285f6c317STim J. Robbins 			*src = i;
22385f6c317STim J. Robbins 			if (strxfrm(dst, src, sizeof(dst)) == 1 && pri &&
22485f6c317STim J. Robbins 			    pri == (unsigned char)*dst)
22585f6c317STim J. Robbins 				s->equiv[p++] = i;
22685f6c317STim J. Robbins 		}
22785f6c317STim J. Robbins 		s->equiv[p] = OOBCH;
22885f6c317STim J. Robbins 	}
22985f6c317STim J. Robbins 
2309b50d902SRodney W. Grimes 	s->cnt = 0;
2319b50d902SRodney W. Grimes 	s->state = SET;
2329b50d902SRodney W. Grimes 	s->set = s->equiv;
2339b50d902SRodney W. Grimes }
2349b50d902SRodney W. Grimes 
2359b50d902SRodney W. Grimes static int
genrange(STR * s,int was_octal)2365b4fa425SAndrey A. Chernov genrange(STR *s, int was_octal)
2379b50d902SRodney W. Grimes {
2385b4fa425SAndrey A. Chernov 	int stopval, octal;
2399b50d902SRodney W. Grimes 	char *savestart;
2405b4fa425SAndrey A. Chernov 	int n, cnt, *p;
241ca99cfddSTim J. Robbins 	size_t clen;
242ca99cfddSTim J. Robbins 	wchar_t wc;
2439b50d902SRodney W. Grimes 
2445b4fa425SAndrey A. Chernov 	octal = 0;
2459b50d902SRodney W. Grimes 	savestart = s->str;
246ca99cfddSTim J. Robbins 	if (*++s->str == '\\')
2475b4fa425SAndrey A. Chernov 		stopval = backslash(s, &octal);
248ca99cfddSTim J. Robbins 	else {
249ca99cfddSTim J. Robbins 		clen = mbrtowc(&wc, s->str, MB_LEN_MAX, NULL);
250ca99cfddSTim J. Robbins 		if (clen == (size_t)-1 || clen == (size_t)-2)
251ca99cfddSTim J. Robbins 			errc(1, EILSEQ, NULL);
252ca99cfddSTim J. Robbins 		stopval = wc;
253ca99cfddSTim J. Robbins 		s->str += clen;
254ca99cfddSTim J. Robbins 	}
2555b4fa425SAndrey A. Chernov 	/*
2565b4fa425SAndrey A. Chernov 	 * XXX Characters are not ordered according to collating sequence in
2575b4fa425SAndrey A. Chernov 	 * multibyte locales.
2585b4fa425SAndrey A. Chernov 	 */
2595b4fa425SAndrey A. Chernov 	if (octal || was_octal || MB_CUR_MAX > 1) {
260ca99cfddSTim J. Robbins 		if (stopval < s->lastch) {
2619b50d902SRodney W. Grimes 			s->str = savestart;
2629b50d902SRodney W. Grimes 			return (0);
2639b50d902SRodney W. Grimes 		}
26430c11564SAndrey A. Chernov 		s->cnt = stopval - s->lastch + 1;
26530c11564SAndrey A. Chernov 		s->state = RANGE;
26630c11564SAndrey A. Chernov 		--s->lastch;
26730c11564SAndrey A. Chernov 		return (1);
26830c11564SAndrey A. Chernov 	}
2695b4fa425SAndrey A. Chernov 	if (charcoll((const void *)&stopval, (const void *)&(s->lastch)) < 0) {
2705b4fa425SAndrey A. Chernov 		s->str = savestart;
2715b4fa425SAndrey A. Chernov 		return (0);
2725b4fa425SAndrey A. Chernov 	}
2735b4fa425SAndrey A. Chernov 	if ((s->set = p = malloc((NCHARS_SB + 1) * sizeof(int))) == NULL)
2745b4fa425SAndrey A. Chernov 		err(1, "genrange() malloc");
2755b4fa425SAndrey A. Chernov 	for (cnt = 0; cnt < NCHARS_SB; cnt++)
2765b4fa425SAndrey A. Chernov 		if (charcoll((const void *)&cnt, (const void *)&(s->lastch)) >= 0 &&
2775b4fa425SAndrey A. Chernov 		    charcoll((const void *)&cnt, (const void *)&stopval) <= 0)
2785b4fa425SAndrey A. Chernov 			*p++ = cnt;
2795b4fa425SAndrey A. Chernov 	*p = OOBCH;
2805b4fa425SAndrey A. Chernov 	n = p - s->set;
2815b4fa425SAndrey A. Chernov 
2825b4fa425SAndrey A. Chernov 	s->cnt = 0;
2835b4fa425SAndrey A. Chernov 	s->state = SET;
2845b4fa425SAndrey A. Chernov 	if (n > 1)
2855b4fa425SAndrey A. Chernov 		mergesort(s->set, n, sizeof(*(s->set)), charcoll);
2865b4fa425SAndrey A. Chernov 	return (1);
2875b4fa425SAndrey A. Chernov }
2889b50d902SRodney W. Grimes 
2899b50d902SRodney W. Grimes static void
genseq(STR * s)290b173dd44SEd Schouten genseq(STR *s)
2919b50d902SRodney W. Grimes {
2929b50d902SRodney W. Grimes 	char *ep;
293ca99cfddSTim J. Robbins 	wchar_t wc;
294ca99cfddSTim J. Robbins 	size_t clen;
2959b50d902SRodney W. Grimes 
2969b50d902SRodney W. Grimes 	if (s->which == STRING1)
297af647767SPhilippe Charnier 		errx(1, "sequences only valid in string2");
2989b50d902SRodney W. Grimes 
2999b50d902SRodney W. Grimes 	if (*s->str == '\\')
30021f53e91SAndrey A. Chernov 		s->lastch = backslash(s, NULL);
301ca99cfddSTim J. Robbins 	else {
302ca99cfddSTim J. Robbins 		clen = mbrtowc(&wc, s->str, MB_LEN_MAX, NULL);
303ca99cfddSTim J. Robbins 		if (clen == (size_t)-1 || clen == (size_t)-2)
304ca99cfddSTim J. Robbins 			errc(1, EILSEQ, NULL);
305ca99cfddSTim J. Robbins 		s->lastch = wc;
306ca99cfddSTim J. Robbins 		s->str += clen;
307ca99cfddSTim J. Robbins 	}
3089b50d902SRodney W. Grimes 	if (*s->str != '*')
309af647767SPhilippe Charnier 		errx(1, "misplaced sequence asterisk");
3109b50d902SRodney W. Grimes 
3119b50d902SRodney W. Grimes 	switch (*++s->str) {
3129b50d902SRodney W. Grimes 	case '\\':
31321f53e91SAndrey A. Chernov 		s->cnt = backslash(s, NULL);
3149b50d902SRodney W. Grimes 		break;
3159b50d902SRodney W. Grimes 	case ']':
3169b50d902SRodney W. Grimes 		s->cnt = 0;
3179b50d902SRodney W. Grimes 		++s->str;
3189b50d902SRodney W. Grimes 		break;
3199b50d902SRodney W. Grimes 	default:
3203281f9d8SJoerg Wunsch 		if (isdigit((u_char)*s->str)) {
3219b50d902SRodney W. Grimes 			s->cnt = strtol(s->str, &ep, 0);
3229b50d902SRodney W. Grimes 			if (*ep == ']') {
3239b50d902SRodney W. Grimes 				s->str = ep + 1;
3249b50d902SRodney W. Grimes 				break;
3259b50d902SRodney W. Grimes 			}
3269b50d902SRodney W. Grimes 		}
327af647767SPhilippe Charnier 		errx(1, "illegal sequence count");
3289b50d902SRodney W. Grimes 		/* NOTREACHED */
3299b50d902SRodney W. Grimes 	}
3309b50d902SRodney W. Grimes 
3319b50d902SRodney W. Grimes 	s->state = s->cnt ? SEQUENCE : INFINITE;
3329b50d902SRodney W. Grimes }
3339b50d902SRodney W. Grimes 
3349b50d902SRodney W. Grimes /*
3359b50d902SRodney W. Grimes  * Translate \??? into a character.  Up to 3 octal digits, if no digits either
3369b50d902SRodney W. Grimes  * an escape code or a literal character.
3379b50d902SRodney W. Grimes  */
3389b50d902SRodney W. Grimes static int
backslash(STR * s,int * is_octal)33921f53e91SAndrey A. Chernov backslash(STR *s, int *is_octal)
3409b50d902SRodney W. Grimes {
34178732475SMark Murray 	int ch, cnt, val;
3429b50d902SRodney W. Grimes 
34321f53e91SAndrey A. Chernov 	if (is_octal != NULL)
34421f53e91SAndrey A. Chernov 		*is_octal = 0;
3459b50d902SRodney W. Grimes 	for (cnt = val = 0;;) {
3463281f9d8SJoerg Wunsch 		ch = (u_char)*++s->str;
34703afb27cSJordan K. Hubbard 		if (!isdigit(ch) || ch > '7')
3489b50d902SRodney W. Grimes 			break;
3499b50d902SRodney W. Grimes 		val = val * 8 + ch - '0';
3509b50d902SRodney W. Grimes 		if (++cnt == 3) {
3519b50d902SRodney W. Grimes 			++s->str;
3529b50d902SRodney W. Grimes 			break;
3539b50d902SRodney W. Grimes 		}
3549b50d902SRodney W. Grimes 	}
35521f53e91SAndrey A. Chernov 	if (cnt) {
35621f53e91SAndrey A. Chernov 		if (is_octal != NULL)
35721f53e91SAndrey A. Chernov 			*is_octal = 1;
3589b50d902SRodney W. Grimes 		return (val);
35921f53e91SAndrey A. Chernov 	}
3609b50d902SRodney W. Grimes 	if (ch != '\0')
3619b50d902SRodney W. Grimes 		++s->str;
3629b50d902SRodney W. Grimes 	switch (ch) {
3639b50d902SRodney W. Grimes 		case 'a':			/* escape characters */
3649b50d902SRodney W. Grimes 			return ('\7');
3659b50d902SRodney W. Grimes 		case 'b':
3669b50d902SRodney W. Grimes 			return ('\b');
3679b50d902SRodney W. Grimes 		case 'f':
3689b50d902SRodney W. Grimes 			return ('\f');
3699b50d902SRodney W. Grimes 		case 'n':
3709b50d902SRodney W. Grimes 			return ('\n');
3719b50d902SRodney W. Grimes 		case 'r':
3729b50d902SRodney W. Grimes 			return ('\r');
3739b50d902SRodney W. Grimes 		case 't':
3749b50d902SRodney W. Grimes 			return ('\t');
3759b50d902SRodney W. Grimes 		case 'v':
3769b50d902SRodney W. Grimes 			return ('\13');
3779b50d902SRodney W. Grimes 		case '\0':			/*  \" -> \ */
3789b50d902SRodney W. Grimes 			s->state = EOS;
3799b50d902SRodney W. Grimes 			return ('\\');
3809b50d902SRodney W. Grimes 		default:			/* \x" -> x */
3819b50d902SRodney W. Grimes 			return (ch);
3829b50d902SRodney W. Grimes 	}
3839b50d902SRodney W. Grimes }
384