So the context here is that I'm working on an own implementation of vfscanf. I currently have the following two function signatures:
int k_fscanf_l(uint32 HA, FILE *stream, int max_length, char *format_tok, int *bytes_moved, void *va_list_item)
int k_vfscanf(uint32 HA, FILE *stream, const char *format, va_list args)
k designates kernel, v designates variable length, and the l is just my flavor of string length-accounting variant.
k_fscanf_l is a helper function that handles a bunch of the heavy duty work for k_vfscanf. In terms of what each function does specifically:
k_vfscanf takes a format string and breaks it down into singular tokens and individually passes them down to k_fscanf_l, or in the case of characters to a separate k_fread function (don't... ask). Inside k_fscanf_l I call sscanf(lstrPtr, mod_tok, va_list_item, &offset) to do most of the heavy work for me, I handle the file stuff aside from that myself.
There's a lot more I could say to over-describe this but I believe it's simpler to get to the crux of the question, which has to do with the last argument in each function. My program is currently asserting from indexing into va_list args, so I wanted to ask:
Questions:
- Is it strictly incorrect to index into
va_list argslike so:(void *)args[va_list_index]and pass that along tok_fscanf_l? - Should I be using the predefined macros instead of void pointers to achieve what I want?
- Is it even possible to achieve what I want? That is, can I divvy up the
va_list argsas multiplevoid *va_list_itempointers to be filled bysscanfinside the helper function? Do I have to change the function signature? - Do I have specify the types if I call the
va_argmacro or is there a (void *)-esque solution? - If not answered above, what would you recommend in this situation?
I currently do the tokenization of the format string and calls to the helper function in the same loop, but I could break those into 2 passes instead to know strictly how many iterations I will do before working with va_list, I just don't know if I can pass the individual items down to be filled even if I do that.
Edit 2: Updating the snippet to be more encompassing
else if (tok_len > 1)
{
// Character(s) - Deterministic length and compatibility with byte characters
if (ft_low == 'c')
{
int num_chars;
ret_val = sscanf(formatTok, "%d", &num_chars);
if (ret_val < 1)
num_chars = 1;
k_print_message(HA, NOTE, 0, "vfscanf", "\tGot %d characters for specifier\n", num_chars);
ret_val = k_fread(HA, va_arg(args, void *), num_chars, 1, stream);
}
// %N case - Does not need to actually call scanning function
else if (ft_low == 'n')
{
int *va_list_count = va_arg(args, int *);
*va_list_count = byte_offset;
}
// Strings - Hard to predict strings and scansets, support max length available for buffer
else if (ft_low == 's' || ft_low == ']')
ret_val = k_fscanf_l(HA, stream, MAX_LSTR_IN, formatTok, &temp_offset, va_arg(args, char *));
// Non-Floating Numbers - Common bases shouldn't reach 16 digits, ever
else if (ft_low == 'i' || ft_low == 'd' || ft_low == 'u' || ft_low == 'o' || ft_low == 'x' || ft_low == 'p')
ret_val = k_fscanf_l(HA, stream, 64, formatTok, &temp_offset, va_arg(args, int *));
// Floating Point Numbers - resolutions should never reach 64 digits, ever
else if (ft_low == 'f' || ft_low == 'e' || ft_low == 'g' || ft_low == 'a')
ret_val = k_fscanf_l(HA, stream, 64, formatTok, &temp_offset, va_arg(args, float *));
// Updates value
byte_offset += temp_offset;
if (ret_val > 0)
stored_items++;
else if (ret_val < 0)
break;
}
k_print_message(HA, NOTE, 0, "vfscanf", "\tGot ft_low: %c\n", ft_low);
k_print_message(HA, NOTE, 0, "vfscanf", "\tGot ret_val: %d\n", ret_val);
// Resets flag
null_flag = 0;
// Deallocates memory
kernel_free(HA, formatTok);
}
// Closes va_list from iteration
va_end(args);