Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 3 of 3
  1. #1
    Regular Coder Apothem's Avatar
    Join Date
    Mar 2008
    Posts
    380
    Thanks
    36
    Thanked 25 Times in 25 Posts

    Filling a string with memcpy...

    So I'm trying to replicate PHP's "implode" syntax to work with a game's scripting engine. As I'm extremely new to C coding (haven't really used it a lot), I have no idea what I'm doing half the times.

    Here's the code:
    Code:
    BUILDIN_FUNC(implode)
    {
    	TBL_PC* sd = NULL;
    	struct script_data* data;
    	const char* name;
    	int32 start = 0;
    	int32 end;
    	int32 id;
    	int i, delim_len;
    	unsigned long len = 0, len_sub = 0;
    	char *str;
    	const char *delim_fake;
    	char *delim;
    
    	delim_fake = script_getstr(st, 2);
    	delim = aMalloc(strlen(delim_fake));
    	sprintf(delim, "%s", delim_fake);
    
    	data = script_getdata(st, 3);
    	if( !data_isreference(data) )
    	{
    		ShowError("script:implode: not a variable\n");
    		script_reportdata(data);
    		st->state = END;
    		return 1;// not a variable
    	}
    
    	id = reference_getid(data);
    	start = reference_getindex(data);
    	name = reference_getname(data);
    	if( not_array_variable(*name) )
    	{
    		ShowError("script:implode: illegal scope\n");
    		script_reportdata(data);
    		st->state = END;
    		return 1;// not supported
    	}
    
    	if( not_server_variable(*name) )
    	{
    		sd = script_rid2sd(st);
    		if( sd == NULL )
    			return 0;// no player attached
    	}
    
    	end = getarraysize(st, id, start, is_string_variable(name), reference_getref(data));
    	i = start;
    
    	if( !end )
    		script_pushstrcopy(st, "");
    
    
    	for( start; start < end; start++ ) { // Count the length of the string.
    		void* v;
    		v = get_val2(st, reference_uid(id, start), reference_getref(data));
    		len = len + strlen(v);
    	}
    
    	delim_len = strlen(delim);
    
    	if( end == 1 ) {
    		push_val2(st->stack, C_NAME, reference_uid(id, i), reference_getref(data));
    		return 0;
    	}
    
    	len = len + (end - 1 * delim_len);
    	str = aMalloc(len);
    
    	for( ; i < end; i++ ) {
    		void* value;
    		value = get_val2(st, reference_uid(id, i), reference_getref(data));
    
    		memcpy(str + len_sub, value, strlen(value) + 1);
    
    		len_sub = strlen(value) + len_sub + 1;
    		if( i != end - 1 ) {
    			memcpy(str + delim_len, delim, delim_len + 1);
    			len_sub = len_sub + delim_len + 1;
    		}
    	}
    
    	script_pushstrcopy(st, str);
    	return 0;
    }
    To explain what (I think) some things mean:
    TBL_PC* sd = The attached player's data
    struct script_data* data = I'm guessing this is for getting ready to use variables.
    script_getstr = Gets a constant char variable that was inputted via the game's script.
    script_getdata = Get's the data of an inputted value via the game's script.
    data_isreference = Checks if the data is a real variable or not.
    script_reportdata = I'm guessing this is for error logs.
    reference_* = Gets a certain thing of a variable; not really sure what.
    getarraysize = Get the size of an inputted array.
    get_val2 = Gets a array data depending on the index from reference_uid... the second value in reference_uid is the index for an array.

    If you have any other questions about a certain syntax, please ask.
    I tried to execute this syntax by:
    Code:
    	setarray $@var$[0], "tik", "tak", "toe";
    	debugmes implode("-", $@var$);
    Now here's the problem:
    The debugmes always outputs "tik"...

    Is anybody able to explain to me what's the problem?
    Last edited by Apothem; 08-25-2008 at 02:05 AM.

  • #2
    Regular Coder ralph l mayo's Avatar
    Join Date
    Nov 2005
    Posts
    951
    Thanks
    1
    Thanked 31 Times in 29 Posts
    I don't feel like deciphering all that but the result you get makes me think you might be copying null terminators from every token, which would make the string look like it was the same as the first argument even if its memory really did contain every string.

    When you're using strings, use string functions! You're already using <string.h> for strlen so why are you banging your head against memset at all? strcat was made to do exactly, and I mean EXACTLY, this:

    Code:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main(int argc, char** argv)
    {
    	int ii = 1, size = 0;
    	// Sum lengths
    	for (; ii < argc; ++ii)
    	{
    		size += strlen(argv[ii]); 
    	}
    	
    	// Allocate
    	char* combined = calloc(size + 1, 1); // +1 for null terminator, calloc to zero
    
    	// Implode
    	for (ii = 1; ii < argc; ++ii)
    	{
    		strcat(combined, argv[ii]);
    	}
    	puts(combined);
    	// Deallocate
    	free(combined);
    	return 0;
    }
    Last edited by ralph l mayo; 08-24-2008 at 08:03 AM.

  • #3
    Regular Coder Apothem's Avatar
    Join Date
    Mar 2008
    Posts
    380
    Thanks
    36
    Thanked 25 Times in 25 Posts
    Well, that actually worked when you use puts.

    For some reason, the returned value of implode() is always the first value...

    Code:
    /// Pushes a string into the stack (script engine frees it automatically)
    #define script_pushstr(st,val) push_str((st)->stack, C_STR, (val))
    /// Pushes a copy of a string into the stack
    #define script_pushstrcopy(st,val) push_str((st)->stack, C_STR, aStrdup(val))
    /// Pushes a constant string into the stack (must never change or be freed)
    #define script_pushconststr(st,val) push_str((st)->stack, C_CONSTSTR, (val))
    Code:
    void push_str(struct script_stack* stack, enum c_op type, char* str)
    {
    	if( stack->sp >= stack->sp_max )
    		stack_expand(stack);
    	stack->stack_data[stack->sp].type  = type;
    	stack->stack_data[stack->sp].u.str = str;
    	stack->stack_data[stack->sp].ref   = NULL;
    	stack->sp++;
    }
    Here's the current code:
    Code:
    BUILDIN_FUNC(implode)
    {
    	TBL_PC* sd = NULL;
    	struct script_data* data;
    	const char* name;
    	int32 start = 0;
    	int32 end;
    	int32 id;
    	int i;
    	unsigned long len = 0;
    	char *str;
    	const char *delim;
    
    	delim = script_getstr(st, 2);
    
    	data = script_getdata(st, 3);
    	if( !data_isreference(data) )
    	{
    		ShowError("script:implode: not a variable\n");
    		script_reportdata(data);
    		st->state = END;
    		return 1;// not a variable
    	}
    
    	id = reference_getid(data);
    	start = reference_getindex(data);
    	name = reference_getname(data);
    	if( not_array_variable(*name) )
    	{
    		ShowError("script:implode: illegal scope\n");
    		script_reportdata(data);
    		st->state = END;
    		return 1;// not supported
    	}
    
    	if( not_server_variable(*name) )
    	{
    		sd = script_rid2sd(st);
    		if( sd == NULL )
    			return 0;// no player attached
    	}
    
    	end = getarraysize(st, id, start, is_string_variable(name), reference_getref(data));
    	i = start;
    
    	if( !end )
    		script_pushstrcopy(st, "");
    
    
    	for( start; start < end; start++ ) { // Count the length of the string.
    		void* v;
    		v = get_val2(st, reference_uid(id, start), reference_getref(data));
    		len = len + strlen(v);
    	}
    
    	if( end == 1 ) {
    		push_val2(st->stack, C_NAME, reference_uid(id, i), reference_getref(data));
    		return 0;
    	}
    
    	len = len + (end - 1 * strlen(delim));
    
    	str = calloc(len + 1, 1);
    
    	for( ; i < end; i++ ) {
    		void* v;
    		v = get_val2(st, reference_uid(id, i), reference_getref(data));
    		strcat(str, ((char*)v));
    
    		if( i != end - 1 ) {
    			strcat(str, delim);
    		}
    	}
    
    	script_pushstrcopy(st, str);
    	return 0;
    }
    If you wanna see the whole source code, look: http://svn.eathena.ws/svn/ea/trunk/src/map/script.c
    Last edited by Apothem; 08-24-2008 at 09:23 AM.


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •