// This file is part of SmallBASIC
//
// strings
//
// This program is distributed under the terms of the GPL v2.0 or later
// Download the GNU Public License (GPL) from www.gnu.org
//
// Copyright(C) 2000 Nicholas Christopoulos
#include "common/sys.h"
#include "common/str.h"
#include "common/panic.h"
#include "common/fmt.h"
/**
* removes spaces and returns a new string
*/
char *trimdup(const char *str) {
char *buf;
char *p;
buf = tmp_alloc(strlen(str) + 1);
strcpy(buf, str);
if (*str == '\0') {
return buf;
}
p = (char *) str;
while (is_wspace(*p)) {
p++;
}
strcpy(buf, p);
if (*p != '\0') {
p = buf;
while (*p) {
p++;
}
p--;
while (p > buf && is_wspace(*p)) {
p--;
}
p++;
*p = '\0';
}
return buf;
}
/**
* removes spaces
*/
void str_alltrim(char *str) {
char *buf;
buf = trimdup(str);
strcpy(str, buf);
tmp_free(buf);
}
/**
* caseless string compare
*/
int strcaseless(const char *s1, const char *s2) {
const char *p1 = s1;
const char *p2 = s2;
int ch1, ch2;
while (*p1) {
if (*p1 == '\0' && *p2 != '\0') {
return -1;
}
if (*p2 == '\0' && *p1 != '\0') {
return 1;
}
ch1 = to_upper(*p1);
ch2 = to_upper(*p2);
if (ch1 < ch2) {
return -1;
}
if (ch1 > ch2) {
return 1;
}
p1++;
p2++;
}
return 0;
}
/**
* caseless string compare
*/
int strcaselessn(const char *s1, const char *s2, int len) {
const char *p1 = s1;
const char *p2 = s2;
int ch1, ch2, count;
count = 0;
while (*p1) {
if (count == len) {
return 0;
}
if (*p1 == '\0' && *p2 != '\0') {
return -1;
}
if (*p2 == '\0' && *p1 != '\0') {
return 1;
}
ch1 = to_upper(*p1);
ch2 = to_upper(*p2);
if (ch1 < ch2) {
return -1;
}
if (ch1 > ch2) {
return 1;
}
p1++;
p2++;
count++;
}
return 0;
}
/**
* strstr with ignore case
*/
char *stristr(const char *s1, const char *s2) {
char *p;
int l2;
p = (char *) s1;
l2 = strlen(s2);
while (*p) {
if (strcaselessn(p, s2, l2) == 0)
return p;
p++;
}
return NULL;
}
/**
*
*/
char *transdup(const char *src, const char *what, const char *with) {
char *p = (char *) src;
char *dest, *d;
int lwhat, lwith, size, len;
lwhat = strlen(what);
lwith = strlen(with);
size = 256;
dest = tmp_alloc(size);
d = dest;
*d = '\0';
while (*p) {
if (strncmp(p, what, lwhat) == 0) {
if ((d - dest) + lwith >= size - 1) {
len = d - dest;
size += 256;
dest = tmp_realloc(dest, size);
d = dest + len;
}
memcpy(d, with, lwith);
d += lwith;
p += (lwhat - 1);
} else {
if ((d - dest) + 1 >= size - 1) {
len = d - dest;
size += 256;
dest = tmp_realloc(dest, size);
d = dest + len;
}
*d = *p;
d++;
}
p++;
}
*d = '\0';
return dest;
}
/*
* strstr with support for quotes
*/
char *q_strstr(const char *s1, const char *s2, const char *pairs) {
char *p, *z;
int l2;
int wait_q, open_q, level_q;
p = (char *) s1;
l2 = strlen(s2);
wait_q = open_q = level_q = 0;
while (*p) {
if (*p == wait_q) { // i am waiting that. level down
level_q--;
if (level_q <= 0) { // level = 0
level_q = 0;
wait_q = 0;
}
} else if ((z = strchr(pairs, *p)) != NULL) { // character is a
// delimiter;
// level up
open_q = ((z - pairs) + 1) % 2; // true, if its a 'begin'
// delimiter
if (wait_q && open_q) {
if (*(z + 1) == wait_q) // open_q of our pair?
level_q++; // increase level
} else if (wait_q)
; // do nothing, I am waitting something
// else
else { // its a new section
if (open_q) {
level_q++; // level = 1
wait_q = *(z + 1); // what to wait for
}
}
} else if (wait_q == 0) { // it is a regular character
if (strncmp(p, s2, l2) == 0)
return p;
}
// next
p++;
}
return NULL;
}
/**
*
*/
int is_alpha(int ch) {
if (ch == 0)
return 0;
if ((ch > 64 && ch < 91) || (ch > 96 && ch < 123))
return -1;
// return
// (strchr("_���������������������������������������������������������", ch)
// != NULL); // Greek
return (ch & 0x80); // +foreign
}
/**
* returns true if the ch is alphanumeric
*/
int is_alnum(int ch) {
if (ch == 0) {
return 0;
}
return (is_alpha(ch) || is_digit(ch));
}
/**
* returns true if the ch is an 'empty' character
*/
int is_space(int ch) {
return (ch == ' ' || ch == '\t' || ch == '\f' || ch == '\n' || ch == '\r' || ch == '\v') ? -1 : 0;
}
/**
* returns true if 'text' contains digits only
*/
int is_all_digits(const char *text) {
const char *p = text;
if (p == NULL) {
return 0;
}
if (*p == '\0') {
return 0;
}
while (*p) {
if (!is_digit(*p)) {
return 0;
}
p++;
}
return 1;
}
/**
* returns true if the text is keyword
*/
int is_keyword(const char *name) {
char *p = (char *) name;
if (p == NULL) {
return 0;
}
if (is_alpha(name[0])) {
while (is_alnum(*p) || (*p == '_')) {
p++;
}
return (*p == '\0');
}
return 0;
}
/**
* converts the 'str' string to uppercase
*/
char *strupper(char *str) {
char *p = str;
if (p == NULL) {
return 0;
}
while (*p) {
*p = to_upper(*p);
p++;
}
return str;
}
/**
* converts the 'str' string to lowercase
*/
char *strlower(char *str) {
char *p = str;
if (p == NULL) {
return 0;
}
while (*p) {
*p = to_lower(*p);
p++;
}
return str;
}
/**
*
*/
char *get_keyword(char *text, char *dest) {
char *p = (char *) text;
char *d = dest;
if (p == NULL) {
*dest = '\0';
return 0;
}
while (is_space(*p)) {
p++;
}
while (is_alnum(*p) || (*p == '_')) {
*d = to_upper(*p);
d++;
p++;
}
// Code to kill the $
// if ( *p == '$' )
// p ++;
if (*p == '$') {
*d++ = *p++;
}
*d = '\0';
while (is_space(*p)) {
p++;
}
// special case, something wrong, jump to next char
if (p == text) {
*dest = *p;
*(dest + 1) = '\0';
p++;
}
return p;
}
/**
* Returns the number of a string (constant numeric expression)
*
* type <=0 = error
* 1 = int32
* 2 = double
*
* Warning: octals are different from C (QB compatibility: 009 = 9)
*/
char *get_numexpr(char *text, char *dest, int *type, var_int_t *lv, var_num_t *dv) {
char *p = (char *) text;
char *d = dest;
char *epos = NULL, *epos_on_text = NULL;
byte base = 10;
byte dpc = 0, stupid_e_fmt = 0, eop = '+';
int sign = 1;
var_num_t power = 1.0;
var_num_t num;
*type = 0;
*lv = 0;
*dv = 0.0;
if (p == NULL) {
*dest = '\0';
return NULL;
}
// spaces
while (is_space(*p)) {
p++;
}
// sign
if ((*p == '-' || *p == '+') && strchr("0123456789.", *(p + 1)) &&
*(p + 1) != '\0'){if (*p == '-') {
sign = -1;
}
p++; // don't copy it
}
//
// resolve the base (hex, octal and binary)
//
if ((*p == '&') || (*p == '0' && (*(p + 1) != '\0' && strchr("HXBO", to_upper(*(p + 1))) != NULL))) {
p++;
switch (*p) {
case 'H':
case 'h':
case 'X':
case 'x':
base = 16;
break;
case 'O':
case 'o':
base = 8;
break;
case 'B':
case 'b':
base = 2;
break;
default:
*type = -1;
return p; // Unknown base
}
p++;
}
//
// copy parts of number
//
if (base == 16) {
// copy hex
while (is_hexdigit(*p)) {
*d = to_upper(*p);
d++;
p++;
}
} else if (base != 10) {
// copy octal | bin
while (is_digit(*p))
*d++ = *p++;
} else if (is_digit(*p) || *p == '.') {
// copy number (first part)
while (is_digit(*p) || *p == '.') {
if (*p == '.') {
dpc++;
if (dpc > 1) {
*type = -2; // DP ERROR
break;
}
}
*d++ = *p++;
}
// check second part
if ((*p == 'E' || *p == 'e') && (*type == 0)) {
epos = d;
epos_on_text = p;
*d++ = *p++; // E
if (*p == '+' || *p == '-' || is_digit(*p) || *p == '.') {
dpc = 0;
// copy second part (power)
if (*p == '+' || *p == '-') {
*d++ = *p++;
if (strchr("+-*/\\^", *p) != 0) { // stupid E format
// (1E--9 ||
// 1E++9)
stupid_e_fmt = 1;
eop = *p;
*d++ = *p++;
if (*p == '+' || *p == '-')
*d++ = *p++;
}
}
// power
while (is_digit(*p) || *p == '.') {
if (*p == '.') {
dpc++;
if (dpc > 1) {
*type = -4; // DP ERROR (second part)
break;
}
}
*d++ = *p++;
} // after E
} //
else {
*type = -3; // E+- ERROR
}
}
} else
*type = -9; // NOT A NUMBER
*d = '\0';
//
// finaly, calculate the number
//
if (*type == 0) {
switch (base) {
case 10:
if (dpc || (epos != NULL) || (strlen(dest) > 8)) {
*type = 2; // double
if (epos) {
if (stupid_e_fmt) {
int r_type = 1;
*epos = '\0';
num = sb_strtof(dest) * ((double) sign);
*epos = 'E'; // restore E
/*
* if ( *p == 'E' || *p == 'e' ) { long r_lv; double r_dv;
*
* p = get_numexpr(epos_on_text+3, dest, &r_type, &r_lv, &r_dv);
*
* switch ( r_type ) { case 1: power = r_lv; break; case 2: power =
* r_dv; break; default: // error *type = r_type; } } else
*/
power = sb_strtof(epos + 3);
if (r_type > 0) {
switch (eop) {
case '+':
*dv = num + power;
break;
case '-':
*dv = num - power;
break;
case '*':
*dv = num * power;
break;
case '/':
if (ABS(power) != 0.0)
*dv = num / power;
else
*dv = 0;
// else if(comp) sc_raise() else rt_raise
break;
case '\\':
if ((long) power != 0) {
*type = 1;
*lv = num / (long) power;
} else {
*type = 1;
*lv = 0;
}
// else if(comp) sc_raise() else rt_raise
break;
case '^':
*dv = pow(num, power);
break;
}
}
} else {
*epos = '\0';
power = pow(10, sb_strtof(epos + 1));
*dv = sb_strtof(dest) * ((double) sign) * power;
*epos = 'E';
}
} else {
*dv = sb_strtof(dest) * ((double) sign);
}
} else {
// dpc = 0 && epos = 0
*type = 1; // int32
*lv = xstrtol(dest) * sign;
}
break;
case 16:
*type = 1; // int32
*lv = hextol(dest);
break;
case 8:
*type = 1; // int32
*lv = octtol(dest);
break;
case 2:
*type = 1; // int32
*lv = bintol(dest);
break;
}
}
//
if (is_alpha(*p))
*type = -9; // ITS NOT A NUMBER
while (is_space(*p))
p++;
return p;
}
/**
*
*/
var_num_t numexpr_sb_strtof(char *source) {
char buf[256], *np;
int type;
var_int_t lv;
var_num_t dv;
np = get_numexpr(source, buf, &type, &lv, &dv);
if (type == 1 && *np == '\0') {
return (var_num_t) lv;
} else if (type == 2 && *np == '\0') {
return dv;
}
return 0.0;
}
/**
*
*/
var_int_t numexpr_strtol(char *source) {
char buf[256], *np;
int type;
var_int_t lv;
var_num_t dv;
np = get_numexpr(source, buf, &type, &lv, &dv);
if (type == 1 && *np == '\0') {
return lv;
} else if (type == 2 && *np == '\0') {
return (var_int_t) dv;
}
return 0;
}
/**
* convertion: binary to decimal
*/
long bintol(const char *str) {
long r = 0;
char *p = (char *) str;
if (p == NULL) {
return 0;
}
while (*p) {
if (*p == 48 || *p == 49) // 01
r = (r << 1) + ((*p) - 48);
p++;
}
return r;
}
/**
* convertion: octal to decimal
*/
long octtol(const char *str) {
long r = 0;
char *p = (char *) str;
if (p == NULL) {
return 0;
}
while (*p) {
if (*p >= 48 && *p <= 55) // 01234567
r = (r << 3) + ((*p) - 48);
p++;
}
return r;
}
/**
* convertion: hexadecimal to decimal
*/
long hextol(const char *str) {
long r = 0;
char *p = (char *) str;
if (p == NULL) {
return 0;
}
while (*p) {
if (is_digit(*p)) // 0123456789
r = (r << 4) + ((*p) - 48);
else if (*p >= 65 && *p <= 70) // ABCDEF
r = (r << 4) + ((*p) - 55);
else if (*p >= 97 && *p <= 102) // abcdef
r = (r << 4) + ((*p) - 87);
p++;
}
return r;
}
/**
* string to double
*/
var_num_t sb_strtof(const char *str) {
char *p = (char *) str;
var_num_t r = 0.0;
var_num_t d = 10.0;
var_num_t sign = 1;
int decp = 0;
if (p == NULL) {
return 0;
}
if (*p == '-') {
sign = -1;
p++;
} else if (*p == '+')
p++;
while (*p) {
if (is_digit(*p)) {
if (!decp)
r = (r * 10) + ((*p) - 48);
else {
r += (((*p) - 48) * 1 / d);
d *= 10;
}
} else if (*p == '.')
decp = 1;
else if (*p == ' ')
break;
else {
r = 0;
break;
}
p++;
}
return r * sign;
}
/**
*
*/
long xstrtol(const char *str) {
if (str == NULL) {
return 0;
}
return atoi(str);
}
/**
*
*/
int is_number(const char *str) {
char *p = (char *) str;
int dpc = 0, cnt = 0;
if (str == NULL) {
return 0;
}
if (*p == '+' || *p == '-')
p++;
while (*p) {
if (strchr("0123456789.", *p) == NULL
)
return 0;
else
cnt++;
if (*p == '.') {
dpc++;
if (dpc > 1)
return 0;
}
p++;
}
if (cnt)
return 1;
return 0;
}
/**
* double to string
*/
char *ftostr(var_num_t num, char *dest) {
bestfta(num, dest);
return dest;
}
/**
*
*/
char *ltostr(var_int_t num, char *dest) {
if (dest == NULL) {
panic("l2s(..,null)");
}
sprintf(dest, VAR_INT_FMT, num);
return dest;
}
/**
* newdir must ends with dirsep
* new_ext must starts with .
*/
char *chgfilename(char *dest, char *source, char *newdir, char *prefix, char *new_ext, char *suffix) {
char *plast_dir;
char *plast_point;
dest[0] = '\0';
plast_dir = strrchr(source, OS_DIRSEP);
if (!plast_dir) {
plast_dir = source;
} else {
plast_dir++;
}
plast_point = strrchr(source, '.');
if (newdir) {
strcat(dest, newdir);
}
if (prefix) {
strcat(dest, prefix);
}
if (new_ext) {
*plast_point = '\0';
}
strcat(dest, plast_dir);
if (suffix) {
strcat(dest, suffix);
}
if (new_ext) {
strcat(dest, new_ext);
*plast_point = '.';
}
return dest;
}
/**
*
*/
char *xbasename(char *dest, const char *source) {
char *p;
p = strrchr(source, OS_DIRSEP);
if (!p)
p = (char *) source;
else
p++;
strcpy(dest, p);
return dest;
}
/**
*
*/
int is_wspace(int c) {
return (c != 0 && strchr(" \t\n\r\v\f", c));
}
/**
* squeeze (& strdup)
*/
char *sqzdup(const char *source) {
char *rp, *p, *d;
int lc = 0;
rp = tmp_alloc(strlen(source) + 1);
p = (char *) source;
d = rp;
while (*p != '\0' && is_wspace(*p))
p++;
while (*p) {
if (is_wspace(*p)) {
if (!lc) {
lc = 1;
if (p > source) {
if (is_alpha(*(p - 1)) || is_digit(*(p - 1)))
*d++ = ' ';
else {
char *nc;
nc = p;
while (*nc != '\0' && is_wspace(*nc))
nc++;
if (is_alpha(*nc) || is_digit(*nc)
)
*d++ = ' ';
}
}
}
} else
(lc = 0, *d++ = *p);
p++;
}
*d = '\0';
if (d > rp) {
if (is_wspace(*(d - 1)))
*(d - 1) = '\0';
}
return rp;
}
/**
* enclose, returns a newly created string
*/
char *encldup(const char *source, const char *pairs) {
char *rp;
int l;
l = strlen(source);
rp = tmp_alloc(l + 3);
memcpy(rp + 1, source, l);
*(rp) = pairs[0];
if (pairs[1])
*(rp + l + 1) = pairs[1];
else
*(rp + l + 1) = pairs[0];
*(rp + l + 2) = '\0';
return rp;
}
/**
* disclose, returns a newly created string
*/
char *discldup(const char *source, const char *pairs, const char *ignpairs) {
char *rp, *np, *r, *p, *z;
int wait_p = 0, open_p = 0, level_p = 0;
int wait_q = 0, open_q = 0, level_q = 0;
int record = 0;
rp = tmp_strdup(source);
r = rp;
p = (char *) source;
while (*p) {
// ignore pairs
if (*p == wait_q) { // ignore pair - level down
level_q--;
if (level_q <= 0) {
level_q = 0;
wait_q = 0;
}
} else if ((z = strchr(ignpairs, *p)) != NULL) {
open_q = ((z - ignpairs) + 1) % 2;
if (wait_q && open_q) {
if (*(z + 1) == wait_q) // open_q of our pair?
level_q++;
} else if (wait_q)
; // do nothing, I am waitting something
// else
else { // new pair
if (open_q) {
level_q++;
wait_q = *(z + 1);
}
}
}
// primary pairs
else if (*p == wait_p && wait_q == 0) { // primary pair - level
// down
level_p--;
if (level_p <= 0) {
// store and exit
record = 0;
break;
}
} else if ((z = strchr(pairs, *p)) != NULL && wait_q == 0) {
open_p = ((z - pairs) + 1) % 2;
if (wait_p && open_p) {
if (*(z + 1) == wait_p) // open_q of our pair?
level_p++;
} else if (wait_p)
; // do nothing, I am waitting something
// else
else { // new pair
if (open_p) {
level_p++;
wait_p = *(z + 1);
record = 1;
}
}
}
// next
if (record == 1) // ignore the first
record++;
else if (record == 2)
*r++ = *p;
p++;
}
*r = '\0';
np = tmp_strdup(rp); // actually, resize down
tmp_free(rp);
return np;
}
/**
* C-Style control codes
*/
char *cstrdup(const char *source) {
char *buf, *p, *d;
buf = tmp_alloc(strlen(source) + 1);
p = (char *) source;
d = buf;
while (*p) {
if (*p == '\\') {
p++;
switch (*p) {
case 'e':
*d++ = '\033';
break;
case 'v':
*d++ = '\v';
break;
case 't':
*d++ = '\t';
break;
case 'r':
*d++ = '\r';
break;
case 'n':
*d++ = '\n';
break;
case 'b':
*d++ = '\b';
break;
case '\'':
*d++ = '\'';
break;
case '\"':
*d++ = '\"';
break;
case 'a':
*d++ = '\a';
break;
case 'f':
*d++ = '\f';
break;
case '\\':
*d++ = '\\';
break;
case 'x': // hex
if (is_hexdigit(*(p + 1)) && is_hexdigit(*(p + 2))) {
int c = 0;
p++;
if (is_digit(*p))
c |= ((*p - '0') << 4);
else
c |= (((to_upper(*p) - 'A') + 10) << 4);
p++;
if (is_digit(*p))
c |= *p - '0';
else
c |= (to_upper(*p) - 'A') + 10;
*d++ = c;
} else
*d++ = '\0';
break;
case '0': // oct
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
if (is_octdigit(*(p + 1)) && is_octdigit(*(p + 2))) {
int c = 0;
c |= ((*p - '0') << 6);
p++;
c |= ((*p - '0') << 3);
p++;
c |= (*p - '0');
*d++ = c;
} else
*d++ = '\0';
break;
default:
*d++ = *p;
}
p++;
} else
*d++ = *p++;
}
*d = '\0';
return buf;
}
/**
* basic-string to c-string convertion
*/
char *bstrdup(const char *source) {
char *buf, *p, *d;
buf = tmp_alloc(strlen(source) * 4 + 1);
p = (char *) source;
d = buf;
while (*p) {
if (*p < 32 && *p >= 0) {
switch (*p) {
case '\033':
*d++ = '\\';
*d++ = 'e';
break;
case '\v':
*d++ = '\\';
*d++ = 'v';
break;
case '\t':
*d++ = '\\';
*d++ = 't';
break;
case '\r':
*d++ = '\\';
*d++ = 'r';
break;
case '\n':
*d++ = '\\';
*d++ = 'n';
break;
case '\b':
*d++ = '\\';
*d++ = 'b';
break;
case '\'':
*d++ = '\\';
*d++ = '\'';
break;
case '\"':
*d++ = '\\';
*d++ = '\"';
break;
case '\a':
*d++ = '\\';
*d++ = 'a';
break;
case '\f':
*d++ = '\\';
*d++ = 'f';
break;
case '\\':
*d++ = '\\';
*d++ = '\\';
break;
default:
*d++ = '\\';
*d++ = 'x';
*d++ = to_hexdigit((*p & 0xF0) >> 4);
*d++ = to_hexdigit(*p & 0xF);
}
p++;
} else
*d++ = *p++;
}
*d = '\0';
return buf;
}
/**
*
*/
const char *baseof(const char *source, int delim) {
const char *p;
p = strrchr(source, delim);
if (p) {
return p + 1;
}
return source;
}
/**
*
*/
char char_table_replace(const char *what_table, int ch, const char *replace_table) {
const char *p;
p = strchr(what_table, ch);
if (!p) {
return ch;
}
return *(replace_table + (p - what_table));
}