17 Jan, 2015, Hades_Kane wrote in the 1st comment:
Votes: 0
I'm having a major brain fart at the moment…


I need to search a string and if it contains anything outside of a predefined list of words, I need it to bail.

Specifically, let's say that the acceptable words are fighter, mage, and thief.

I need to be able to to bail if the string contains anything except those words, BUT, all three words or any combination of two needs to be acceptable.

I'm using 3 as an example, there are a total of 39 acceptable phrases.

I figure between str_cmp and strstr there's a way to do this, it's just been a long day and I'm drawing a blank.

Right now, I have a block of code doing this:

if( argument[0] != '\0'
&& !strstr(argument, "fighter")
&& !strstr(argument, "mage")
&& !strstr(argument, "thief"))
{
return FALSE;
}


Which will bail provided any of those three words are contained in the string, so of course sending an argument "blah" won't work, but "fighter mage blah" will because it contains.

Any help would be appreciated.
18 Jan, 2015, plamzi wrote in the 2nd comment:
Votes: 0
Hades_Kane said:
I'm using 3 as an example, there are a total of 39 acceptable phrases.


Any combination out of 39 words is acceptable? What is the max size of the combination? Three words?

I'm not seeing a simple way to do this with just strstr and str_cmp. But using strtok to split the input on the space character and then checking if each word is contained in the dictionary is fairly simple:

http://www.tutorialspoint.com/c_standard...
18 Jan, 2015, alteraeon wrote in the 3rd comment:
Votes: 0
Admittedly I've not worked much with standard codebases, but how on earth do you guys function without a standard 'wordcmp' function that case-insensitively compares two words against each other? Here's the AA function for setting group comments, which only allows comments to be constructed from a list of about a hundred safe words held in the 'comment_allowed' array:

void set_group_memory_comment(struct character *p, const char *comment)
{
Buffer final;
int count = 0;

while (comment != NULL) {
Buffer b;
b.get_word(comment);
comment = next_arg(comment);

int i = 0;
int found = 0;
while (comment_allowed[i] != NULL) {
const char *temp = comment_allowed[i];
if (wordcmp(b, temp)) {
found = 1;
break;
}
i++;
}

if (found) {
count++;
if (count > 6) {
send_to_char(p, "\n(too many keywords, ignoring extras)");
break;
}
final << b << " ";
}
else {
sprintf(junk_string, "\n(ignoring invalid comment tag '%s%s', see '~H~Ygroup set comment~O' for a list)", (char *) b, COFF);
send_to_char(p, junk_string);
}
}
}
18 Jan, 2015, Hades_Kane wrote in the 4th comment:
Votes: 0
Basically, I'm faking class flags without using flags since we are using 32 bits and we have 36 classes, and that was the last thing I needed to figure out in order for it to not "feel" much different to builders than if it were actually using flags.

strtok worked like a charm… exactly what I was kicking around in my head, I just wasn't sure how to do it or if there was a single function like that which did it. Stock ROM doesn't use strtok anywhere in it, so I hadn't been exposed to it, thanks again.
18 Jan, 2015, Tyche wrote in the 5th comment:
Votes: 0
There's a handy function called one_argument() in Merc/ROM which pretty much does the work of strtok(), but more specific to what is needed.
18 Jan, 2015, quixadhal wrote in the 6th comment:
Votes: 0
Boy, this thread makes me glad I don't work in C anymore. :)

In perl, I'd just use a regexp:

if( $argument =~ /\b(fighter|mage|thief)\b/ ) {
# do stuff
}


Most sane languages have similar one-line ways to do string comparisons like that. :)

NOTE: The \b is regexp-speak for "word boundary", which means either empty or a non-word character. Typically, a word character is alphanumeric or '-' and/or '_'. You can easily build your regexp from your list of classes too, so you don't have to hard code it.
18 Jan, 2015, Tyche wrote in the 7th comment:
Votes: 0
He wants just the opposite: To find if the command string contains bad arguments.
Something like \b(?!fighter|mage|thief)\w+\b

And that's easily done with regex in C as it is in Perl.

#include <stdio.h>
#include <string.h>
#include <pcre.h>

int main()
{
pcre *creg;
const char *err;
int erri;
int vec[30];

char test1[] = "fighter fuzzle";
char test2[] = "mage fighter";
char regex[] = "\\b(?!fighter|mage|thief)\\w+\\b";
creg = pcre_compile(regex,PCRE_CASELESS,&err,&erri,NULL);

if (pcre_exec(creg,NULL,test1,strlen(test1),0,0,vec,30) < 0)
printf("test1 good arguments\n");
else
printf("test1 bad argument\n");

if (pcre_exec(creg,NULL,test2,strlen(test2),0,0,vec,30) < 0)
printf("test2 good arguments\n");
else
printf("test2 bad argument\n");

pcre_free(creg);

return 0;
}


But building a table of strings and flag values and looping through the command arguments using one_argument is probably a more direct way of solving the problem.
21 Jan, 2015, Hades_Kane wrote in the 8th comment:
Votes: 0
I'm sure there's probably a better or more elegant way to do this, but this is how I did it:

char str[MSL];
char * token;
bool match = TRUE;

sprintf(str,argument);
token = strtok (str," ");

while (token != NULL)
{
if(str_cmp(token, "(null)") && str_cmp(token, "none") && str_cmp(token, "all") && str_cmp(token, "fighter")
&& str_cmp(token, "mage") && str_cmp(token, "thief") && str_cmp(token, "knight") && str_cmp(token, "brawler")
&& str_cmp(token, "afighter") && str_cmp(token, "samurai") && str_cmp(token, "monk") && str_cmp(token, "berserker")
&& str_cmp(token, "mfighter") && str_cmp(token, "ronin") && str_cmp(token, "kensai") && str_cmp(token, "pirate")
&& str_cmp(token, "athief") && str_cmp(token, "assassin") && str_cmp(token, "engineer") && str_cmp(token, "beastmaster")
&& str_cmp(token, "mthief") && str_cmp(token, "hunter") && str_cmp(token, "ninja") && str_cmp(token, "paladin")
&& str_cmp(token, "aknight") && str_cmp(token, "darkknight") && str_cmp(token, "einherjar")
&& str_cmp(token, "dragoon") && str_cmp(token, "mknight") && str_cmp(token, "purifier") && str_cmp(token, "fenrisknight")
&& str_cmp(token, "whitewizard") && str_cmp(token, "amage") && str_cmp(token, "blackwizard")
&& str_cmp(token, "geomancer") && str_cmp(token, "sage") && str_cmp(token, "mmage") && str_cmp(token, "sorcerer")
&& str_cmp(token, "magus"))
match = FALSE;
token = strtok (NULL, " ");
}

if(!match)
{
send_to_char( "Syntax: class [string]\r\n", ch );
send_to_char( "Valid class strings:\r\n",ch);
send_to_char( "fighter brawler afighter samurai monk berserker mfighter ronin kensai\r\n",ch);
send_to_char( "thief pirate athief assassin engineer beastmaster mthief hunter ninja\r\n",ch);
send_to_char( "knight paladin aknight darkknight einherjar dragoon mknight purifier fenrisknight\r\n",ch);
send_to_char( "mage whitewizard amage blackwizard geomancer sage mmage sorcerer magus\r\n",ch);
return FALSE;
}
22 Jan, 2015, Lyanic wrote in the 9th comment:
Votes: 0
Wouldn't it be easier just to convert to 64-bit integers, or use bitvectors, or use a string for the class instead of a bit? It's not 1990 anymore.
23 Jan, 2015, Hades_Kane wrote in the 10th comment:
Votes: 0
I'm using a string for it here. That was my solution to get around the 32 bit limitation, and anything else I've looked at for converting to 64 or unlimited or whatever else seemed grossly complicated. I've added additional fields for a couple of other things as needed, too.
24 Jan, 2015, Jindrak wrote in the 11th comment:
Votes: 0
Hades_Kane said:
I'm having a major brain fart at the moment…


I need to search a string and if it contains anything outside of a predefined list of words, I need it to bail.

Specifically, let's say that the acceptable words are fighter, mage, and thief.

I need to be able to to bail if the string contains anything except those words, BUT, all three words or any combination of two needs to be acceptable.

I'm using 3 as an example, there are a total of 39 acceptable phrases.

I figure between str_cmp and strstr there's a way to do this, it's just been a long day and I'm drawing a blank.

Right now, I have a block of code doing this:

if( argument[0] != '\0'
&& !strstr(argument, "fighter")
&& !strstr(argument, "mage")
&& !strstr(argument, "thief"))
{
return FALSE;
}


Which will bail provided any of those three words are contained in the string, so of course sending an argument "blah" won't work, but "fighter mage blah" will because it contains.

Any help would be appreciated.


The following may give you some ideas, it's basically how I check for valid names. Cleaned out some of the code specific to my projects and some other less relevant things, so forgive any errors…

bool check_parse_name( const std::string &name )
{
char tmp[SML_BUFFER];
int16 spaces = 0, i;

if (name.empty())
{
return( false );
}

if (name.size() < 3 || name.size() > 12)
{
return( false );
}

if (!isalpha(name[0]))
{
return( false );
}

std::sprintf( tmp, "grep -ix \"%s\" \"%s\" > /dev/null", name.c_str(), g_mud.name_file.c_str() );
if ((i = system( tmp )) == 0)
{
return( false );
}

std::string::const_iterator str = name.begin();
while(str != name.end())
{
if (ispunct(*str) || !isalpha(*str))
{
return( false );
}

if (isspace(*str) && ++spaces > 0)
{
return( false );
}

str++;
}

return( true );
}


Basically you would keep a file in one of your data directories,

name1
name2
name3
…etc


I have it stored within my system data but doesn't really have to be…
24 Jan, 2015, Tyche wrote in the 12th comment:
Votes: 0
What happens if "& rm-f *" is passed into name?
25 Jan, 2015, alteraeon wrote in the 13th comment:
Votes: 0
Wow Tyche, I didn't even notice that until the second pass through. I think my rating of this thread just went from a single to a double facepalm.

Alter Aeon MUD
http://www.alteraeon.com
25 Jan, 2015, Jindrak wrote in the 14th comment:
Votes: 0
Tyche said:
What happens if "& rm-f *" is passed into name?


What will your name be? & rm -f *
That is not a legal name!
What will your name be?

EDIT:
And btw, before the "check your directories." comment occurs…I did an attempted various derivations of the command. I'm not saying the usage of system() doesn't present possible issues just offered another suggestion for the OP to consider using. Double face palm that if you want…
25 Jan, 2015, Kaz wrote in the 15th comment:
Votes: 0
Jindrak said:
Tyche said:
What happens if "& rm-f *" is passed into name?


What will your name be? & rm -f *
That is not a legal name!
What will your name be?


It's very slightly more complicated, but only because of quotes. A real string to bother it is:
Bob" && rm -f * && echo "


The code only checks that the first character of the name is alphabetical. Ideally, it should also check that all characters are alphanumeric.

std::sprintf( tmp, "grep -ix \"%s\" \"%s\" > /dev/null", name.c_str(), g_mud.name_file.c_str() );


grep -ix "Bob" && rm -f * && echo "" "name_file" > /dev/null
26 Jan, 2015, quixadhal wrote in the 16th comment:
Votes: 0
Actually, grep will just hang on that unless stdin is redirected. If/when you interrupt it, it should return a failure code and not run the rm binary. However… were you to use "; rm -f *"…. :)

Using system() normally attaches stdin/stdout to the stdin/stdout of the curent process unless you specify otherwise.
27 Jan, 2015, Kaz wrote in the 17th comment:
Votes: 0
Also limited to 12 characters, as I noticed later.

Anyway, I think we all agree that it's abusable in principle. :)
0.0/17