int run_add_line_after(Document *doc, char *command) {
int paragraph_num, line_num;
char com[MAX_STR_SIZE 1], extra[MAX_STR_SIZE 2], line[MAX_STR_SIZE 1];
if (sscanf(command, " %s %d %d %s", com, ¶graph_num, &line_num, extra)
== 4 && paragraph_num > 0 && line_num >= 0 && extra[0] == '*') {
strcpy(line, &(extra[1]));
if (add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
printf("add_line_after failed\n");
}
return SUCCESS;
}
return FAILURE;
}
I want sscanf
to read everything left in command
to extra
but it's only taking the first word. For example, if I have:
- command: "add_line_after 1 0 *first line of the document"
I want to see:
- com: "add_line_after"
- paragraph_num: 1
- line_num: 0
- extra: "first line of the document"
but instead I get:
- com: "add_line_after"
- paragraph_num: 1
- line_num: 0
- extra: "first"
because %s
stops when it hits the space. How do I read the rest of the line while still ignoring any whitespace between '0'
and '*'
?
For reference, MAX_STR_SIZE
is 80 and command
is a 1025 character array (though I don't think that matters). Just assume extra
is large enough to hold the rest of the line.
CodePudding user response:
sscanf
is really the wrong tool to use here. It can be done, but probably should not be used the way you are trying. Fortunately, you are not passing com
to add_line_after
, which means it is not necessary to ensure that com
is a properly null-terminated string, and this allows you to avoid all of that unnecessary string copying. (If you were passing com
, you would either have to copy it, or write a null terminator into command
.) You don't want or need to use sscanf
to move data at all. You can just use it to parse the numeric values. It's not clear to me if you want to discard any whitespace that follows the *
, and this code does not. If you want to do so, removing that whitespace is trivial and left as an exercise for the reader:
int
run_add_line_after(struct document *doc, const char *command)
{
int paragraph_num, line_num, n;
const char *line;
if( 2 == sscanf(command, "%*s %d %d %n", ¶graph_num, &line_num, &n)
&& paragraph_num > 0 && line_num >= 0 && command[n] == '*' )
{
line = command n 1;
if( add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
fprintf(stderr, "add_line_after failed\n");
} else {
return SUCCESS;
}
}
return FAILURE;
}
The idea here is to simply use sscanf
to figure out where the extra
data is in the string and to parse the integer values. This is absolutely the wrong tool to use (the scanf
family is (almost) always the wrong tool), and is used here only for demonstration.
-- edit --
But, of course, this doesn't do exactly what you want. It would be much cleaner to move some functionality into add_line_after
to handle the newline, but since you also need to remove the newline, it becomes necessary to do something like:
int
run_add_line_after(struct document *doc, char *command)
{
int paragraph_num, line_num, n;
char *line;
if( 2 == sscanf(command, "%*s %d %d %n", ¶graph_num, &line_num, &n)
&& paragraph_num > 0 && line_num >= 0 && command[n] == '*' )
{
line = command n 1;
line[strcspn(line, "\n")] = '\0';
if( add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
fprintf(stderr, "add_line_after failed\n");
} else {
return SUCCESS;
}
}
return FAILURE;
}
This is not ideal. It would be better if you modify the API so that you can avoid both copying the data and modifying the string that you are given.
CodePudding user response:
Got it. Format string should be " %s %d %d %[^\n]s"
. It will keep reading into the last string variable until it hits the enter key.