1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
|
/* string-utils.c - Functions dealing with string contents.
*
* Copyright (C) 1998 Oskar Liljeblad
*
* 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 <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h> /* C89 */
#include <stdarg.h> /* Gnulib/C89 */
#include <stdio.h> /* Gnulib/C89 */
#include <string.h> /* Gnulib/C89 */
#include <stdlib.h> /* Gnulib/C89 */
#include "xalloc.h" /* Gnulib */
#include "xvasprintf.h" /* Gnulib */
#include "common.h" /* common */
#include "error.h" /* common */
#include "string-utils.h" /* common */
/**
* Return a zero-based of a character in a string.
*/
/*inline*/ int
strindex(const char *str, char ch)
{
char *pos = strchr(str, ch);
return (pos == NULL ? -1 : pos - str);
}
/**
* Remove trailing newline from a character, if
* the last character is a newline.
*/
void
chomp(char *str)
{
int len;
len = strlen(str);
if (len > 0 && str[len-1] == '\n')
str[len-1] = '\0';
}
/**
* Strip leading characters from a string, modifying the
* string. What characters to strip are determined by the
* function, which can be an isalpha, isalnum etc.
*
* @param line
* Line to strip characters from. This string will be modified.
* @param check
* A function to be called for each character. If it returns
* a non-zero value, the character will be stripped.
*/
void
strip_leading(char *line, int (*check)(int))
{
int start;
int end;
if (line[0] != '\0' && check(line[0])) {
for (start = 1; line[start] != '\0' && check(line[start]); start++);
for (end = start; line[end] != '\0'; end++);
memmove(line, line+start, end-start+1);
}
}
/**
* Return the index of the first character of some type.
* For this purpose a check function is used.
*
* @param line
* String to look for characters in.
* @param check
* A function to be called for each character. If it returns
* a non-zero value, we has found a character we were looking
* for, and char_index returns with the index of this character.
*/
int
char_index(const char *line, int (*check)(int))
{
int c;
for (c = 0; line[c] != '\0'; c++) {
if (check(line[c]))
return c;
}
return -1;
}
/**
* Return true if the first string ends with the second.
*
* @param str
* String to look for end in.
* @param end
* String to look for.
*/
bool
ends_with(const char *str, const char *end)
{
int c;
int diff = strlen(str) - strlen(end);
if (diff < 0)
return false;
for (c = 0; end[c] != '\0'; c++) {
if (end[c] != str[c+diff])
return false;
}
return true;
}
/**
* Return true if the first string ends with the second,
* ignoring case.
*
* @param str
* String to look for end in.
* @param end
* String to look for.
*/
bool
ends_with_nocase(const char *str, const char *end)
{
int c;
int diff = strlen(str) - strlen(end);
if (diff < 0)
return false;
for (c = 0; end[c] != '\0'; c++) {
if (tolower(end[c]) != tolower(str[c+diff]))
return false;
}
return true;
}
/**
* Return true if the first string starts with the second.
*
* @param str
* String to look for start in.
* @param start
* String to look for.
*/
bool
starts_with(const char *str, const char *start)
{
int c;
for (c = 0; str[c] != '\0'; c++) {
if (str[c] != start[c])
break;
}
return (start[c] == '\0');
}
/**
* Return true if the first string starts with the second,
* ignoring case.
*
* @param str
* String to look for start in.
* @param start
* String to look for.
*/
bool
starts_with_nocase(const char *str, const char *start)
{
int c;
for (c = 0; str[c] != '\0'; c++) {
if (tolower(str[c]) != tolower(start[c]))
break;
}
return (start[c] == '\0');
}
/**
* Translate each occurence of `from' in `str' to `to'.
*
* @returns
* Number of changes made.
*/
int
translate_char(char *str, char from, char to)
{
int changes = 0;
int c;
for (c = 0; str[c] != '\0'; c++) {
if (str[c] == from) {
str[c] = to;
changes++;
}
}
return changes;
}
/**
* Convert all characters in the specified string using
* some character-conversion function.
*/
void
str_convert(char *str, int (*convert)(int))
{
int c;
for (c = 0; str[c] != '\0'; c++)
str[c] = convert(str[c]);
}
/**
* Return true if the specified character is a Perl "word"
* character (`\w'), that is: if it is an alphanumeric character
* or `_'.
*/
int
iswordchar(int ch)
{
return isalnum(ch) || ch == '_';
}
/**
* Replace an occurence of `from' in `str' with `to'.
* If `from' is longer than `to', then `str' must contain
* enough space for the new string.
*
* Note: This implementation is slow and not optimized.
*
* @returns
* true if a replacement was made.
*/
bool
replace_str(char *str, const char *from, const char *to)
{
char *pos;
int s1;
int s2;
if ((pos = strstr(str, from)) == NULL)
return false;
s1 = strlen(from);
s2 = strlen(to);
memmove(pos+s2, pos+s1, strlen(pos)-s2+1);
memcpy(pos, to, s2);
return true;
}
/**
* Concatenate two file names into a new string.
* If file is "." return a duplicate of file2.
*/
char *
cat_files(const char *file, const char *file2)
{
if (strcmp(file, ".") == 0)
return xstrdup(file2);
return xasprintf("%s%s%s", file, (ends_with(file, "/") ? "" : "/"), file2);
}
/**
* Return the substring of a string, putting it into a newly allocated
* string.
*
* @param start
* Index of first character to copy. If this value is
* negative, the index is counted from the end of the string.
* E.g. -1 is before last non-null character.
* @param end
* Index of first character not to copy. If this value is
* negative, the index is counted from the end of the string.
* E.g. -1 is before last non-null character.
*/
char *
substring(const char *buf, int start, int end)
{
int len = strlen(buf);
if (start < 0)
start += len;
if (end < 0)
end += len;
return strndup(buf+start, end-start);
}
/**
* Strip all trailing characters listed in `stripchars'.
*
* @param str
* String to remove characters from. This string will be
* modified.
*/
int
string_strip_trailing(char *str, const char *stripchars)
{
int a = strlen(str)-1;
int b = a;
while (a >= 0 && string_index_of_c(stripchars, str[a]) != -1)
a--;
str[a+1] = '\0';
return b-a;
}
/**
* Strip all trailing characters that are `stripchar'.
*
* @param str
* String to remove characters from. This string will be
* modified.
*/
int
string_strip_trailing_c(char *str, char stripchar)
{
int a = strlen(str-1);
int b = a;
while (a >= 0 && str[a] == stripchar)
a--;
str[a+1] = '\0';
return b-a;
}
/* XXX: document */
int
string_strip_leading(char *str, const char *stripchars)
{
int a = 0;
int b = 0;
while (str[a] != '\0' && string_index_of_c(stripchars, str[a]) != -1)
a++;
while (str[a] != '\0')
str[b++] = str[a++];
str[b] = str[a];
return a-b;
}
/* XXX: document */
int
string_strip_leading_c(char *str, char stripchar)
{
int a = 0;
int b = 0;
while (str[a] != '\0' && str[a] == stripchar)
a++;
while (str[a] != '\0')
str[b++] = str[a++];
str[b] = str[a];
return a-b;
}
/**
* Return the index of the first character listed in `findchars'
* in `str'.
*
* @param str
* The string to look for characters in.
* @param findchars
* The characters to look for.
*/
int
string_index_of_any(const char *str, const char *findchars)
{
int c;
for (c = 0; str[c] != '\0'; c++) {
if (string_index_of_c(findchars, str[c]) != -1)
return c;
}
return -1;
}
int
word_get_index(const char *str, int pos)
{
bool in_word = false;
int words = 0;
int c;
for (c = 0; str[c] != '\0' && c < pos; c++) {
if (!in_word && !isspace(str[c])) {
in_word = true;
}
if (in_word && isspace(str[c])) {
in_word = false;
words++;
}
}
return words;
}
char *
word_get(const char *str, int idx)
{
bool in_word = false;
int words = 0;
int start = -1;
int c;
for (c = 0; str[c] != '\0'; c++) {
if (!in_word && !isspace(str[c])) {
words++;
start = c;
in_word = true;
}
if (in_word && isspace(str[c])) {
if (words > idx)
return substring(str, start, c);
in_word = false;
}
}
if (words-1 == idx)
return substring(str, start, c);
return NULL;
}
int
uintlen(uint64_t value)
{
int len;
for (len = 1; value >= 10; len++)
value /= 10;
return len;
}
/* Like dirname, but returns a newly allocated string.
*/
char *
xdirname(const char *path)
{
char *pos = strrchr(path, '/');
if (pos == NULL)
return xstrdup(".");
if (pos == path)
return xstrdup("/");
return substring(path, 0, pos-path);
}
|