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