1274116Sdteske/*-
2274116Sdteske * Copyright (c) 2001-2014 Devin Teske <dteske@FreeBSD.org>
3274116Sdteske * All rights reserved.
4274116Sdteske *
5274116Sdteske * Redistribution and use in source and binary forms, with or without
6274116Sdteske * modification, are permitted provided that the following conditions
7274116Sdteske * are met:
8274116Sdteske * 1. Redistributions of source code must retain the above copyright
9274116Sdteske *    notice, this list of conditions and the following disclaimer.
10274116Sdteske * 2. Redistributions in binary form must reproduce the above copyright
11274116Sdteske *    notice, this list of conditions and the following disclaimer in the
12274116Sdteske *    documentation and/or other materials provided with the distribution.
13274116Sdteske *
14274116Sdteske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15274116Sdteske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16274116Sdteske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17274116Sdteske * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18274116Sdteske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19274116Sdteske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20274116Sdteske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21274116Sdteske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22274116Sdteske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23274116Sdteske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24274116Sdteske * SUCH DAMAGE.
25274116Sdteske */
26274116Sdteske
27274116Sdteske#include <sys/cdefs.h>
28274116Sdteske__FBSDID("$FreeBSD: releng/10.3/lib/libfigpar/string_m.c 274116 2014-11-04 23:46:01Z dteske $");
29274116Sdteske
30274116Sdteske#include <sys/types.h>
31274116Sdteske
32274116Sdteske#include <ctype.h>
33274116Sdteske#include <errno.h>
34274116Sdteske#include <stdio.h>
35274116Sdteske#include <stdlib.h>
36274116Sdteske#include <string.h>
37274116Sdteske
38274116Sdteske#include "string_m.h"
39274116Sdteske
40274116Sdteske/*
41274116Sdteske * Counts the number of occurrences of one string that appear in the source
42274116Sdteske * string. Return value is the total count.
43274116Sdteske *
44274116Sdteske * An example use would be if you need to know how large a block of memory
45274116Sdteske * needs to be for a replaceall() series.
46274116Sdteske */
47274116Sdteskeunsigned int
48274116Sdteskestrcount(const char *source, const char *find)
49274116Sdteske{
50274116Sdteske	const char *p = source;
51274116Sdteske	size_t flen;
52274116Sdteske	unsigned int n = 0;
53274116Sdteske
54274116Sdteske	/* Both parameters are required */
55274116Sdteske	if (source == NULL || find == NULL)
56274116Sdteske		return (0);
57274116Sdteske
58274116Sdteske	/* Cache the length of find element */
59274116Sdteske	flen = strlen(find);
60274116Sdteske	if (strlen(source) == 0 || flen == 0)
61274116Sdteske		return (0);
62274116Sdteske
63274116Sdteske	/* Loop until the end of the string */
64274116Sdteske	while (*p != '\0') {
65274116Sdteske		if (strncmp(p, find, flen) == 0) { /* found an instance */
66274116Sdteske			p += flen;
67274116Sdteske			n++;
68274116Sdteske		} else
69274116Sdteske			p++;
70274116Sdteske	}
71274116Sdteske
72274116Sdteske	return (n);
73274116Sdteske}
74274116Sdteske
75274116Sdteske/*
76274116Sdteske * Replaces all occurrences of `find' in `source' with `replace'.
77274116Sdteske *
78274116Sdteske * You should not pass a string constant as the first parameter, it needs to be
79274116Sdteske * a pointer to an allocated block of memory. The block of memory that source
80274116Sdteske * points to should be large enough to hold the result. If the length of the
81274116Sdteske * replacement string is greater than the length of the find string, the result
82274116Sdteske * will be larger than the original source string. To allocate enough space for
83274116Sdteske * the result, use the function strcount() declared above to determine the
84274116Sdteske * number of occurrences and how much larger the block size needs to be.
85274116Sdteske *
86274116Sdteske * If source is not large enough, the application will crash. The return value
87274116Sdteske * is the length (in bytes) of the result.
88274116Sdteske *
89274116Sdteske * When an error occurs, -1 is returned and the global variable errno is set
90274116Sdteske * accordingly. Returns zero on success.
91274116Sdteske */
92274116Sdteskeint
93274116Sdteskereplaceall(char *source, const char *find, const char *replace)
94274116Sdteske{
95274116Sdteske	char *p;
96274116Sdteske	char *t;
97274116Sdteske	char *temp;
98274116Sdteske	size_t flen;
99274116Sdteske	size_t rlen;
100274116Sdteske	size_t slen;
101274116Sdteske	uint32_t n = 0;
102274116Sdteske
103274116Sdteske	errno = 0; /* reset global error number */
104274116Sdteske
105274116Sdteske	/* Check that we have non-null parameters */
106274116Sdteske	if (source == NULL)
107274116Sdteske		return (0);
108274116Sdteske	if (find == NULL)
109274116Sdteske		return (strlen(source));
110274116Sdteske
111274116Sdteske	/* Cache the length of the strings */
112274116Sdteske	slen = strlen(source);
113274116Sdteske	flen = strlen(find);
114274116Sdteske	rlen = replace ? strlen(replace) : 0;
115274116Sdteske
116274116Sdteske	/* Cases where no replacements need to be made */
117274116Sdteske	if (slen == 0 || flen == 0 || slen < flen)
118274116Sdteske		return (slen);
119274116Sdteske
120274116Sdteske	/* If replace is longer than find, we'll need to create a temp copy */
121274116Sdteske	if (rlen > flen) {
122274116Sdteske		temp = malloc(slen + 1);
123274116Sdteske		if (errno != 0) /* could not allocate memory */
124274116Sdteske			return (-1);
125274116Sdteske		strcpy(temp, source);
126274116Sdteske	} else
127274116Sdteske		temp = source;
128274116Sdteske
129274116Sdteske	/* Reconstruct the string with the replacements */
130274116Sdteske	p = source; t = temp; /* position elements */
131274116Sdteske
132274116Sdteske	while (*t != '\0') {
133274116Sdteske		if (strncmp(t, find, flen) == 0) {
134274116Sdteske			/* found an occurrence */
135274116Sdteske			for (n = 0; replace && replace[n]; n++)
136274116Sdteske				*p++ = replace[n];
137274116Sdteske			t += flen;
138274116Sdteske		} else
139274116Sdteske			*p++ = *t++; /* copy character and increment */
140274116Sdteske	}
141274116Sdteske
142274116Sdteske	/* Terminate the string */
143274116Sdteske	*p = '\0';
144274116Sdteske
145274116Sdteske	/* Free the temporary allocated memory */
146274116Sdteske	if (temp != source)
147274116Sdteske		free(temp);
148274116Sdteske
149274116Sdteske	/* Return the length of the completed string */
150274116Sdteske	return (strlen(source));
151274116Sdteske}
152274116Sdteske
153274116Sdteske/*
154274116Sdteske * Expands escape sequences in a buffer pointed to by `source'. This function
155274116Sdteske * steps through each character, and converts escape sequences such as "\n",
156274116Sdteske * "\r", "\t" and others into their respective meanings.
157274116Sdteske *
158274116Sdteske * You should not pass a string constant or literal to this function or the
159274116Sdteske * program will likely segmentation fault when it tries to modify the data.
160274116Sdteske *
161274116Sdteske * The string length will either shorten or stay the same depending on whether
162274116Sdteske * any escape sequences were converted but the amount of memory allocated does
163274116Sdteske * not change.
164274116Sdteske *
165274116Sdteske * Interpreted sequences are:
166274116Sdteske *
167274116Sdteske * 	\0NNN	character with octal value NNN (0 to 3 digits)
168274116Sdteske * 	\N	character with octal value N (0 thru 7)
169274116Sdteske * 	\a	alert (BEL)
170274116Sdteske * 	\b	backslash
171274116Sdteske * 	\f	form feed
172274116Sdteske * 	\n	new line
173274116Sdteske * 	\r	carriage return
174274116Sdteske * 	\t	horizontal tab
175274116Sdteske * 	\v	vertical tab
176274116Sdteske * 	\xNN	byte with hexadecimal value NN (1 to 2 digits)
177274116Sdteske *
178274116Sdteske * All other sequences are unescaped (ie. '\"' and '\#').
179274116Sdteske */
180274116Sdteskevoid strexpand(char *source)
181274116Sdteske{
182274116Sdteske	uint8_t c;
183274116Sdteske	char *chr;
184274116Sdteske	char *pos;
185274116Sdteske	char d[4];
186274116Sdteske
187274116Sdteske	/* Initialize position elements */
188274116Sdteske	pos = chr = source;
189274116Sdteske
190274116Sdteske	/* Loop until we hit the end of the string */
191274116Sdteske	while (*pos != '\0') {
192274116Sdteske		if (*chr != '\\') {
193274116Sdteske			*pos = *chr; /* copy character to current offset */
194274116Sdteske			pos++;
195274116Sdteske			chr++;
196274116Sdteske			continue;
197274116Sdteske		}
198274116Sdteske
199274116Sdteske		/* Replace the backslash with the correct character */
200274116Sdteske		switch (*++chr) {
201274116Sdteske		case 'a': *pos = '\a'; break; /* bell/alert (BEL) */
202274116Sdteske		case 'b': *pos = '\b'; break; /* backspace */
203274116Sdteske		case 'f': *pos = '\f'; break; /* form feed */
204274116Sdteske		case 'n': *pos = '\n'; break; /* new line */
205274116Sdteske		case 'r': *pos = '\r'; break; /* carriage return */
206274116Sdteske		case 't': *pos = '\t'; break; /* horizontal tab */
207274116Sdteske		case 'v': *pos = '\v'; break; /* vertical tab */
208274116Sdteske		case 'x': /* hex value (1 to 2 digits)(\xNN) */
209274116Sdteske			d[2] = '\0'; /* pre-terminate the string */
210274116Sdteske
211274116Sdteske			/* verify next two characters are hex */
212274116Sdteske			d[0] = isxdigit(*(chr+1)) ? *++chr : '\0';
213274116Sdteske			if (d[0] != '\0')
214274116Sdteske				d[1] = isxdigit(*(chr+1)) ? *++chr : '\0';
215274116Sdteske
216274116Sdteske			/* convert the characters to decimal */
217274116Sdteske			c = (uint8_t)strtoul(d, 0, 16);
218274116Sdteske
219274116Sdteske			/* assign the converted value */
220274116Sdteske			*pos = (c != 0 || d[0] == '0') ? c : *++chr;
221274116Sdteske			break;
222274116Sdteske		case '0': /* octal value (0 to 3 digits)(\0NNN) */
223274116Sdteske			d[3] = '\0'; /* pre-terminate the string */
224274116Sdteske
225274116Sdteske			/* verify next three characters are octal */
226274116Sdteske			d[0] = (isdigit(*(chr+1)) && *(chr+1) < '8') ?
227274116Sdteske			    *++chr : '\0';
228274116Sdteske			if (d[0] != '\0')
229274116Sdteske				d[1] = (isdigit(*(chr+1)) && *(chr+1) < '8') ?
230274116Sdteske				    *++chr : '\0';
231274116Sdteske			if (d[1] != '\0')
232274116Sdteske				d[2] = (isdigit(*(chr+1)) && *(chr+1) < '8') ?
233274116Sdteske				    *++chr : '\0';
234274116Sdteske
235274116Sdteske			/* convert the characters to decimal */
236274116Sdteske			c = (uint8_t)strtoul(d, 0, 8);
237274116Sdteske
238274116Sdteske			/* assign the converted value */
239274116Sdteske			*pos = c;
240274116Sdteske			break;
241274116Sdteske		default: /* single octal (\0..7) or unknown sequence */
242274116Sdteske			if (isdigit(*chr) && *chr < '8') {
243274116Sdteske				d[0] = *chr;
244274116Sdteske				d[1] = '\0';
245274116Sdteske				*pos = (uint8_t)strtoul(d, 0, 8);
246274116Sdteske			} else
247274116Sdteske				*pos = *chr;
248274116Sdteske		}
249274116Sdteske
250274116Sdteske		/* Increment to next offset, possible next escape sequence */
251274116Sdteske		pos++;
252274116Sdteske		chr++;
253274116Sdteske	}
254274116Sdteske}
255274116Sdteske
256274116Sdteske/*
257274116Sdteske * Expand only the escaped newlines in a buffer pointed to by `source'. This
258274116Sdteske * function steps through each character, and converts the "\n" sequence into
259274116Sdteske * a literal newline and the "\\n" sequence into "\n".
260274116Sdteske *
261274116Sdteske * You should not pass a string constant or literal to this function or the
262274116Sdteske * program will likely segmentation fault when it tries to modify the data.
263274116Sdteske *
264274116Sdteske * The string length will either shorten or stay the same depending on whether
265274116Sdteske * any escaped newlines were converted but the amount of memory allocated does
266274116Sdteske * not change.
267274116Sdteske */
268274116Sdteskevoid strexpandnl(char *source)
269274116Sdteske{
270274116Sdteske	uint8_t backslash = 0;
271274116Sdteske	char *cp1;
272274116Sdteske	char *cp2;
273274116Sdteske
274274116Sdteske	/* Replace '\n' with literal in dprompt */
275274116Sdteske	cp1 = cp2 = source;
276274116Sdteske	while (*cp2 != '\0') {
277274116Sdteske		*cp1 = *cp2;
278274116Sdteske		if (*cp2 == '\\')
279274116Sdteske			backslash++;
280274116Sdteske		else if (*cp2 != 'n')
281274116Sdteske			backslash = 0;
282274116Sdteske		else if (backslash > 0) {
283274116Sdteske			*(--cp1) = (backslash & 1) == 1 ? '\n' : 'n';
284274116Sdteske			backslash = 0;
285274116Sdteske		}
286274116Sdteske		cp1++;
287274116Sdteske		cp2++;
288274116Sdteske	}
289274116Sdteske	*cp1 = *cp2;
290274116Sdteske}
291274116Sdteske
292274116Sdteske/*
293274116Sdteske * Convert a string to lower case. You should not pass a string constant to
294274116Sdteske * this function. Only pass pointers to allocated memory with null terminated
295274116Sdteske * string data.
296274116Sdteske */
297274116Sdteskevoid
298274116Sdteskestrtolower(char *source)
299274116Sdteske{
300274116Sdteske	char *p = source;
301274116Sdteske
302274116Sdteske	if (source == NULL)
303274116Sdteske		return;
304274116Sdteske
305274116Sdteske	while (*p != '\0') {
306274116Sdteske		*p = tolower(*p);
307274116Sdteske		p++; /* would have just used `*p++' but gcc 3.x warns */
308274116Sdteske	}
309274116Sdteske}
310