immortalwrt/package/jsda/libericstools/src/string_util.c
2019-07-29 03:03:28 +08:00

670 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work as-is we provide.
* No warranty, express or implied.
* Weve done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#include "erics_tools.h"
#define malloc safe_malloc
#define strdup safe_strdup
char* replace_prefix(char* original, char* old_prefix, char* new_prefix)
{
char* replaced = NULL;
if(original != NULL && old_prefix != NULL && new_prefix != NULL && strstr(original, old_prefix) == original)
{
int old_prefix_length = strlen(old_prefix);
int new_prefix_length = strlen(new_prefix);
int remainder_length = strlen(original) - old_prefix_length;
int new_length = new_prefix_length + remainder_length;
/* printf("%d %d %d %d\n", old_prefix_length, new_prefix_length, remainder_length, new_length); */
replaced = malloc(new_length+1);
memcpy(replaced, new_prefix, new_prefix_length);
memcpy(replaced+new_prefix_length, original+old_prefix_length, remainder_length);
replaced[new_prefix_length+remainder_length] = '\0';
}
return replaced;
}
char* trim_flanking_whitespace(char* str)
{
int new_start = 0;
int new_length = 0;
char whitespace[5] = { ' ', '\t', '\n', '\r', '\0' };
int num_whitespace_chars = 4;
int index = 0;
int is_whitespace = 1;
int test;
while( (test = str[index]) != '\0' && is_whitespace == 1)
{
int whitespace_index;
is_whitespace = 0;
for(whitespace_index = 0; whitespace_index < num_whitespace_chars && is_whitespace == 0; whitespace_index++)
{
is_whitespace = test == whitespace[whitespace_index] ? 1 : 0;
}
index = is_whitespace == 1 ? index+1 : index;
}
new_start = index;
index = strlen(str) - 1;
is_whitespace = 1;
while( index >= new_start && is_whitespace == 1)
{
int whitespace_index;
is_whitespace = 0;
for(whitespace_index = 0; whitespace_index < num_whitespace_chars && is_whitespace == 0; whitespace_index++)
{
is_whitespace = str[index] == whitespace[whitespace_index] ? 1 : 0;
}
index = is_whitespace == 1 ? index-1 : index;
}
new_length = str[new_start] == '\0' ? 0 : index + 1 - new_start;
if(new_start > 0)
{
for(index = 0; index < new_length; index++)
{
str[index] = str[index+new_start];
}
}
str[new_length] = 0;
return str;
}
int safe_strcmp(const char* str1, const char* str2)
{
if(str1 == NULL && str2 == NULL)
{
return 0;
}
else if(str1 == NULL && str2 != NULL)
{
return 1;
}
else if(str1 != NULL && str2 == NULL)
{
return -1;
}
return strcmp(str1, str2);
}
void to_lowercase(char* str)
{
int i;
for(i = 0; str[i] != '\0'; i++)
{
str[i] = tolower(str[i]);
}
}
void to_uppercase(char* str)
{
int i;
for(i = 0; str[i] != '\0'; i++)
{
str[i] = toupper(str[i]);
}
}
/* returns number freed */
int free_null_terminated_string_array(char** strs)
{
unsigned long str_index = 0;
if(strs != NULL)
{
for(str_index=0; strs[str_index] != NULL; str_index++)
{
free(strs[str_index]);
}
free(strs);
}
return str_index;
}
char** copy_null_terminated_string_array(char** original)
{
unsigned long size;
char** new;
for(size=0; original[size] != NULL ; size++) ;
new = (char**)malloc( (size+1)*sizeof(char*));
for(size=0; original[size] != NULL; size++)
{
new[size] = strdup(original[size]);
}
new[size] = NULL;
return new;
}
char* dynamic_strcat(int num_strs, ...)
{
va_list strs;
int new_length = 0;
int i;
int next_start;
char* new_str;
va_start(strs, num_strs);
for(i=0; i < num_strs; i++)
{
char* next_arg = va_arg(strs, char*);
if(next_arg != NULL)
{
new_length = new_length + strlen(next_arg);
}
}
va_end(strs);
new_str = malloc((1+new_length)*sizeof(char));
va_start(strs, num_strs);
next_start = 0;
for(i=0; i < num_strs; i++)
{
char* next_arg = va_arg(strs, char*);
if(next_arg != NULL)
{
int next_length = strlen(next_arg);
memcpy(new_str+next_start,next_arg, next_length);
next_start = next_start+next_length;
}
}
new_str[next_start] = '\0';
return new_str;
}
char* dcat_and_free(char** one, char** two, int free1, int free2)
{
char* s = NULL;
if(one != NULL && two != NULL) { s = dynamic_strcat(2, *one, *two); }
else if(one != NULL) { s = strdup(*one); }
else if(two != NULL) { s = strdup(*two); }
else { s= strdup(""); }
if(free1){ free(*one); *one=s; }
if(free2){ free(*two); *two=s; }
return s;
}
/*
* line is the line to be parsed -- it is not modified in any way
* max_pieces indicates number of pieces to return, if negative this is determined dynamically
* include_remainder_at_max indicates whether the last piece, when max pieces are reached,
* should be what it would normally be (0) or the entire remainder of the line (1)
* if max_pieces < 0 this parameter is ignored
*
*
* returns all non-separator pieces in a line
* result is dynamically allocated, MUST be freed after call-- even if
* line is empty (you still get a valid char** pointer to to a NULL char*)
*/
char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max, unsigned long *num_pieces)
{
char** split;
*num_pieces = 0;
if(line != NULL)
{
int split_index;
int non_separator_found;
char* dup_line;
char* start;
if(max_pieces < 0)
{
/* count number of separator characters in line -- this count + 1 is an upperbound on number of pieces */
int separator_count = 0;
int line_index;
for(line_index = 0; line[line_index] != '\0'; line_index++)
{
int sep_index;
int found = 0;
for(sep_index =0; found == 0 && sep_index < num_separators; sep_index++)
{
found = separators[sep_index] == line[line_index] ? 1 : 0;
}
separator_count = separator_count+ found;
}
max_pieces = separator_count + 1;
}
split = (char**)malloc((1+max_pieces)*sizeof(char*));
split_index = 0;
split[split_index] = NULL;
dup_line = strdup(line);
start = dup_line;
non_separator_found = 0;
while(non_separator_found == 0)
{
int matches = 0;
int sep_index;
for(sep_index =0; sep_index < num_separators; sep_index++)
{
matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0;
}
non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0;
if(non_separator_found == 0)
{
start++;
}
}
while(start[0] != '\0' && split_index < max_pieces)
{
/* find first separator index */
int first_separator_index = 0;
int separator_found = 0;
while( separator_found == 0 )
{
int sep_index;
for(sep_index =0; separator_found == 0 && sep_index < num_separators; sep_index++)
{
separator_found = separators[sep_index] == start[first_separator_index] || start[first_separator_index] == '\0' ? 1 : 0;
}
if(separator_found == 0)
{
first_separator_index++;
}
}
/* copy next piece to split array */
if(first_separator_index > 0)
{
char* next_piece = NULL;
if(split_index +1 < max_pieces || include_remainder_at_max <= 0)
{
next_piece = (char*)malloc((first_separator_index+1)*sizeof(char));
memcpy(next_piece, start, first_separator_index);
next_piece[first_separator_index] = '\0';
}
else
{
next_piece = strdup(start);
}
split[split_index] = next_piece;
split[split_index+1] = NULL;
split_index++;
}
/* find next non-separator index, indicating start of next piece */
start = start+ first_separator_index;
non_separator_found = 0;
while(non_separator_found == 0)
{
int matches = 0;
int sep_index;
for(sep_index =0; sep_index < num_separators; sep_index++)
{
matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0;
}
non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0;
if(non_separator_found == 0)
{
start++;
}
}
}
free(dup_line);
*num_pieces = split_index;
}
else
{
split = (char**)malloc((1)*sizeof(char*));
split[0] = NULL;
}
return split;
}
char* join_strs(char* separator, char** parts, int max_parts, int free_parts, int free_parts_array)
{
char* joined = NULL;
int num_parts = 0;
for(num_parts=0; parts[num_parts] != NULL && (max_parts < 0 || num_parts < max_parts); num_parts++){}
if(num_parts > 0)
{
num_parts--;
joined = strdup(parts[num_parts]);
if(free_parts){ free(parts[num_parts]); }
num_parts--;
while(num_parts >= 0)
{
char* tmp = joined;
joined = dynamic_strcat(3, parts[num_parts], separator, joined);
free(tmp);
if(free_parts){ free(parts[num_parts]); }
num_parts--;
}
}
if(free_parts_array)
{
free(parts);
}
return joined;
}
char* dynamic_replace(char* template_str, char* old, char* new)
{
char *ret;
int i, count = 0;
int newlen = strlen(new);
int oldlen = strlen(old);
char* dyn_template = strdup(template_str);
char* s = dyn_template;
for (i = 0; s[i] != '\0'; i++)
{
if (strstr(&s[i], old) == &s[i])
{
count++;
i += oldlen - 1;
}
}
ret = malloc(i + 1 + count * (newlen - oldlen));
i = 0;
while (*s)
{
if (strstr(s, old) == s)
{
strcpy(&ret[i], new);
i += newlen;
s += oldlen;
}
else
{
ret[i++] = *s++;
}
}
ret[i] = '\0';
free(dyn_template);
return ret;
}
/*
requires expression to be surrounded by '/' characters, and deals with escape
characters '\/', '\r', '\n', and '\t' when escapes haven't been interpreted
(e.g. after recieving regex string from user)
returns 1 on good regex, 0 on bad regex
*/
int convert_to_regex(char* str, regex_t* p)
{
char* trimmed = trim_flanking_whitespace(strdup(str));
int trimmed_length = strlen(trimmed);
char* new = NULL;
int valid = 1;
/* regex must be defined by surrounding '/' characters */
if(trimmed[0] != '/' || trimmed[trimmed_length-1] != '/')
{
valid = 0;
free(trimmed);
}
if(valid == 1)
{
char* internal = (char*)malloc(trimmed_length*sizeof(char));
int internal_length = trimmed_length-2;
int new_index = 0;
int internal_index = 0;
char previous = '\0';
memcpy(internal, trimmed+1, internal_length);
internal[internal_length] = '\0';
free(trimmed);
new = (char*)malloc(trimmed_length*sizeof(char));
while(internal[internal_index] != '\0' && valid == 1)
{
char next = internal[internal_index];
if(next == '/' && previous != '\\')
{
valid = 0;
}
else if((next == 'n' || next == 'r' || next == 't' || next == '/') && previous == '\\')
{
char previous2 = '\0';
if(internal_index >= 2)
{
previous2 = internal[internal_index-2];
}
new_index = previous2 == '\\' ? new_index : new_index-1;
switch(next)
{
case 'n':
new[new_index] = previous2 == '\\' ? next : '\n';
break;
case 'r':
new[new_index] = previous2 == '\\' ? next : '\r';
break;
case 't':
new[new_index] = previous2 == '\\' ? next : '\t';
break;
case '/':
new[new_index] = previous2 == '\\' ? next : '/';
break;
}
previous = '\0';
internal_index++;
new_index++;
}
else
{
new[new_index] = next;
previous = next;
internal_index++;
new_index++;
}
}
if(valid == 0 || previous == '\\')
{
valid = 0;
free(new);
new = NULL;
}
else
{
new[new_index] = '\0';
}
free(internal);
}
if(valid == 1)
{
valid = regcomp(p,new,REG_EXTENDED) == 0 ? 1 : 0;
if(valid == 0)
{
regfree(p);
}
free(new);
}
return valid;
}
/* note: str element in return value is dynamically allocated, need to free */
dyn_read_t dynamic_read(FILE* open_file, char* terminators, int num_terminators, unsigned long* read_length)
{
fpos_t start_pos;
unsigned long size_to_read = 0;
int terminator_found = 0;
int terminator;
char* str;
dyn_read_t ret_value;
fgetpos(open_file, &start_pos);
while(terminator_found == 0)
{
int nextch = fgetc(open_file);
int terminator_index = 0;
for(terminator_index = 0; terminator_index < num_terminators && terminator_found == 0; terminator_index++)
{
terminator_found = nextch == terminators[terminator_index] ? 1 : 0;
terminator = nextch;
}
terminator_found = nextch == EOF ? 1 : terminator_found;
terminator = nextch == EOF ? EOF : nextch;
if(terminator_found == 0)
{
size_to_read++;
}
}
str = (char*)malloc((size_to_read+1)*sizeof(char));
if(size_to_read > 0)
{
int i;
fsetpos(open_file, &start_pos);
for(i=0; i<size_to_read; i++)
{
str[i] = (char)fgetc(open_file);
}
fgetc(open_file); /* read the terminator */
}
str[size_to_read] = '\0';
*read_length = size_to_read;
ret_value.str = str;
ret_value.terminator = terminator;
return ret_value;
}
/* convenience method for calling dynamic_read with end-of-line separators */
int dyn_read_line(FILE* open_file, char** dest, unsigned long* read_len)
{
char line_seps[] = "\r\n";
dyn_read_t read = dynamic_read(open_file, line_seps, 2, read_len);
*dest = read.str;
return read.terminator;
}
unsigned char* read_entire_file(FILE* in, unsigned long read_block_size, unsigned long *length)
{
int max_read_size = read_block_size;
unsigned char* read_string = (unsigned char*)malloc(max_read_size+1);
unsigned long bytes_read = 0;
int end_found = 0;
while(end_found == 0)
{
int nextch = '?';
while(nextch != EOF && bytes_read < max_read_size)
{
nextch = fgetc(in);
if(nextch != EOF)
{
read_string[bytes_read] = (unsigned char)nextch;
bytes_read++;
}
}
read_string[bytes_read] = '\0';
end_found = (nextch == EOF) ? 1 : 0;
if(end_found == 0)
{
unsigned char *new_str;
max_read_size = max_read_size + read_block_size;
new_str = (unsigned char*)malloc(max_read_size+1);
memcpy(new_str, read_string, bytes_read);
free(read_string);
read_string = new_str;
}
}
*length = bytes_read;
return read_string;
}
char** get_shell_command_output_lines(char* command, unsigned long* num_lines)
{
char** ret = NULL;
if(command != NULL && num_lines != NULL)
{
FILE* shell_out = popen(command, "r");
*num_lines = 0;
if(shell_out != NULL)
{
char linebreaks[] = { '\n', '\r' };
unsigned long read_length;
char* all_data = (char*)read_entire_file(shell_out, 2048, &read_length);
ret = split_on_separators(all_data, linebreaks, 2, -1, 0, num_lines);
free(all_data);
pclose(shell_out);
}
}
return ret;
}
/* comparison functions for qsort */
int sort_string_cmp(const void *a, const void *b)
{
const char **a_ptr = (const char **)a;
const char **b_ptr = (const char **)b;
return strcmp(*a_ptr, *b_ptr);
}
int sort_string_icmp(const void *a, const void *b)
{
const char **a_ptr = (const char **)a;
const char **b_ptr = (const char **)b;
return stricmp(*a_ptr, *b_ptr);
}
/* wrappers for qsort calls */
void do_str_sort(char** string_arr, unsigned long string_arr_len)
{
qsort(string_arr, string_arr_len, sizeof(char*), sort_string_cmp);
}
void do_istr_sort(char** string_arr, unsigned long string_arr_len)
{
qsort(string_arr, string_arr_len, sizeof(char*), sort_string_icmp);
}