/*
// ColdMUD was created and is copyright 1993, 1994 by Greg Hudson
//
// ColdX is a derivitive work, and is copyright 1995 by the ColdX Project.
// Full copyright information can be found in the file doc/CREDITS
//
// File: stringop.c
// Version: 0.1-5
// Last Edited: 18 May 1995
//
// ---
//
// Function operators acting on strings.
*/
#define _POSIX_SOURCE
#include <string.h>
#include <stdlib.h>
#include "x.tab.h"
#include "operator.h"
#include "execute.h"
#include "cmstring.h"
#include "data.h"
#include "match.h"
#include "ident.h"
#include "util.h"
void op_strlen(void)
{
Data *args;
int len;
/* Accept a string to take the length of. */
if (!func_init_1(&args, STRING))
return;
/* Replace the argument with its length. */
len = string_length(args[0].u.str);
pop(1);
push_int(len);
}
void op_substr(void)
{
int num_args, start, len, string_len;
Data *args;
/* Accept a string for the initial string, an integer specifying the start
* of the substring, and an optional integer specifying the length of the
* substring. */
if (!func_init_2_or_3(&args, &num_args, STRING, INTEGER, INTEGER))
return;
string_len = string_length(args[0].u.str);
start = args[1].u.val - 1;
len = (num_args == 3) ? args[2].u.val : string_len - start;
/* Make sure range is in bounds. */
if (start < 0) {
cthrow(range_id, "Start (%d) is less than one.", start + 1);
} else if (len < 0) {
cthrow(range_id, "Length (%d) is less than zero.", len);
} else if (start + len > string_len) {
cthrow(range_id,
"The substring extends to %d, past the end of the string (%d).",
start + len, string_len);
} else {
/* Replace first argument with substring, and pop other arguments. */
anticipate_assignment();
args[0].u.str = string_substring(args[0].u.str, start, len);
pop(num_args - 1);
}
}
void op_explode(void)
{
int num_args, sep_len, len, want_blanks;
Data *args, d;
List *exploded;
char *sep, *s, *p, *q;
String *word;
/* Accept a string to explode and an optional string for the word
* separator. */
if (!func_init_1_to_3(&args, &num_args, STRING, STRING, 0))
return;
want_blanks = (num_args == 3) ? data_true(&args[2]) : 0;
if (num_args >= 2) {
sep = string_chars(args[1].u.str);
sep_len = string_length(args[1].u.str);
} else {
sep = " ";
sep_len = 1;
}
if (!*sep) {
cthrow(range_id, "Null string as separator.");
return;
}
s = string_chars(args[0].u.str);
len = string_length(args[0].u.str);
exploded = list_new(0);
p = s;
for (q = strcstr(p, sep); q; q = strcstr(p, sep)) {
if (want_blanks || q > p) {
/* Add the word. */
word = string_from_chars(p, q - p);
d.type = STRING;
d.u.str = word;
exploded = list_add(exploded, &d);
string_discard(word);
}
p = q + sep_len;
}
if (*p || want_blanks) {
/* Add the last word. */
word = string_from_chars(p, len - (p - s));
d.type = STRING;
d.u.str = word;
exploded = list_add(exploded, &d);
string_discard(word);
}
/* Pop the arguments and push the list onto the stack. */
pop(num_args);
push_list(exploded);
list_discard(exploded);
}
void op_strsub(void)
{
int len, search_len, replace_len;
Data *args;
char *search, *replace, *s, *p, *q;
String *subbed;
/* Accept a base string, a search string, and a replacement string. */
if (!func_init_3(&args, STRING, STRING, STRING))
return;
s = string_chars(args[0].u.str);
len = string_length(args[0].u.str);
search = string_chars(args[1].u.str);
search_len = string_length(args[1].u.str);
replace = string_chars(args[2].u.str);
replace_len = string_length(args[2].u.str);
if (*s == NULL || *search == NULL) {
subbed = string_dup(args[0].u.str);
} else {
subbed = string_new(search_len);
p = s;
for (q = strcstr(p, search); q; q = strcstr(p, search)) {
subbed = string_add_chars(subbed, p, q - p);
subbed = string_add_chars(subbed, replace, replace_len);
p = q + search_len;
}
subbed = string_add_chars(subbed, p, len - (p - s));
}
/* Pop the arguments and push the new string onto the stack. */
pop(3);
push_string(subbed);
string_discard(subbed);
}
/* Pad a string on the left (positive length) or on the right (negative
* length). The optional third argument gives the fill character. */
void op_pad(void)
{
int num_args, len, padding, filler_len;
Data *args;
char *filler;
String *padded;
if (!func_init_2_or_3(&args, &num_args, STRING, INTEGER, STRING))
return;
if (num_args == 3) {
filler = string_chars(args[2].u.str);
filler_len = string_length(args[2].u.str);
} else {
filler = " ";
filler_len = 1;
}
len = (args[1].u.val > 0) ? args[1].u.val : -args[1].u.val;
padding = len - string_length(args[0].u.str);
/* Construct the padded string. */
anticipate_assignment();
padded = args[0].u.str;
if (padding == 0) {
/* Do nothing. Easiest case. */
} else if (padding < 0) {
/* We're shortening the string. Almost as easy. */
padded = string_truncate(padded, len);
} else if (args[1].u.val > 0) {
/* We're lengthening the string on the right. */
padded = string_add_padding(padded, filler, filler_len, padding);
} else {
/* We're lengthening the string on the left. */
padded = string_new(padding + args[0].u.str->len);
padded = string_add_padding(padded, filler, filler_len, padding);
padded = string_add(padded, args[0].u.str);
string_discard(args[0].u.str);
}
args[0].u.str = padded;
/* Discard all but the first argument. */
pop(num_args - 1);
}
void op_match_begin(void)
{
Data *args;
int sep_len, search_len;
char *sep, *search, *s, *p;
int num_args;
/* Accept a base string, a search string, and an optional separator. */
if (!func_init_2_or_3(&args, &num_args, STRING, STRING, STRING))
return;
s = string_chars(args[0].u.str);
search = string_chars(args[1].u.str);
search_len = string_length(args[1].u.str);
if (num_args > 2) {
sep = string_chars(args[2].u.str);
sep_len = string_length(args[2].u.str);
} else {
sep = " ";
sep_len = 1;
}
for (p = s - sep_len; p; p = strcstr(p + 1, sep)) {
/* We found a separator; see if it's followed by search. */
if (strnccmp(p + sep_len, search, search_len) == 0) {
pop(num_args);
push_int(1);
return;
}
}
pop(num_args);
push_int(0);
}
/* Match against a command template. */
void op_match_template(void)
{
Data *args;
List *fields;
char *ctemplate, *str;
/* Accept a string for the template and a string to match against. */
if (!func_init_2(&args, STRING, STRING))
return;
ctemplate = string_chars(args[0].u.str);
str = string_chars(args[1].u.str);
fields = match_template(ctemplate, str);
pop(2);
if (fields) {
push_list(fields);
list_discard(fields);
} else {
push_int(0);
}
}
/* Match against a command template. */
void op_match_pattern(void)
{
Data *args;
List *fields;
char *pattern, *str;
/* Accept a string for the pattern and a string to match against. */
if (!func_init_2(&args, STRING, STRING))
return;
pattern = string_chars(args[0].u.str);
str = string_chars(args[1].u.str);
fields = match_pattern(pattern, str);
pop(2);
if (!fields) {
push_int(0);
return;
}
/* fields is backwards. Reverse it. */
fields = list_reverse(fields);
push_list(fields);
list_discard(fields);
}
void op_match_regexp(void)
{
Data *args, d;
regexp *reg;
List *fields = (List *)0;
List *elemlist;
int num_args, case_flag, i;
char *s;
if (!func_init_2_or_3(&args, &num_args, STRING, STRING, 0))
return;
case_flag = (num_args == 3) ? data_true(&args[2]) : 0;
reg = string_regexp(args[0].u.str);
if (!reg) {
cthrow(regexp_id, "%s", regerror(NULL));
return;
}
/* Execute the regexp. */
s = string_chars(args[1].u.str);
if (regexec(reg, s, case_flag)) {
/* Build the list of fields. */
fields = list_new(NSUBEXP);
for (i = 0; i < NSUBEXP; i++) {
elemlist = list_new(2);
d.type = INTEGER;
/* BUG: backwards logic broke regexp matching
found by Miroslav Silovic (Jenner) */
if (!reg->startp[i]) {
d.u.val = 0;
elemlist = list_add(elemlist, &d);
elemlist = list_add(elemlist, &d);
} else {
d.u.val = reg->startp[i] - s + 1;
elemlist = list_add(elemlist, &d);
d.u.val = reg->endp[i] - reg->startp[i];
elemlist = list_add(elemlist, &d);
}
d.type = LIST;
d.u.list = elemlist;
fields = list_add(fields, &d);
}
}
pop(num_args);
if (fields) {
push_list(fields);
list_discard(fields);
} else {
push_int(0);
}
}
/* Encrypt a string. */
void op_crypt(void)
{
int num_args;
Data *args;
char *s, *encrypted;
String *str;
/* Accept a string to encrypt and an optional salt. */
if (!func_init_1_or_2(&args, &num_args, STRING, STRING))
return;
if (num_args == 2 && string_length(args[1].u.str) != 2) {
cthrow(salt_id, "Salt (%S) is not two characters.", args[1].u.str);
return;
}
s = string_chars(args[0].u.str);
if (num_args == 2) {
encrypted = crypt_string(s, string_chars(args[1].u.str));
} else {
encrypted = crypt_string(s, NULL);
}
pop(num_args);
str = string_from_chars(encrypted, strlen(encrypted));
push_string(str);
string_discard(str);
}
void op_uppercase(void)
{
Data *args;
/* Accept a string to uppercase. */
if (!func_init_1(&args, STRING))
return;
args[0].u.str = string_uppercase(args[0].u.str);
}
void op_lowercase(void)
{
Data *args;
/* Accept a string to uppercase. */
if (!func_init_1(&args, STRING))
return;
args[0].u.str = string_lowercase(args[0].u.str);
}
void op_strcmp(void)
{
Data *args;
int val;
/* Accept two strings to compare. */
if (!func_init_2(&args, STRING, STRING))
return;
/* Compare the strings case-sensitively. */
val = strcmp(string_chars(args[0].u.str), string_chars(args[1].u.str));
pop(2);
push_int(val);
}
#if 0
void op_strfmt(void) {
Data *args;
String *str;
char *fmt;
/* accept two arguments, second is a list for the format */
if (!func_init_2(&args, STRING, LIST))
return;
fmt = string_chars(args[0].u.str);
/* parse it here */
}
#endif