This is part of a two-part post (Part 1).
Here, I have two recent projects that parse a file. The first uses a loop that's kind of hard to follow, and the second uses "modes" to decide what to do.
Project 2: A working map renderer that I plan to implement the game of Picross with.
This one uses "modes" to control the flow of the loop. I feel it is much more elegant, but wonder if this is the best way to do it.
The project is nowhere near finished, but I think this is in-line with the posting guidelines, since the part I care about is fully functional.
The data structures:
struct picross_data {
char *name,
*map;
int width,
height;
};
The parser:
enum pfmode {
mode_find_name,
mode_get_name,
mode_find_width,
mode_get_width,
mode_find_height,
mode_get_height,
mode_get_map,
mode_finish,
};
void
parse_file(char *fname, struct picross_data *pd)
{
FILE *f = fopen(fname, "r");
char fbuf[10000] = "",
*fbp = fbuf,
*nbuf = calloc(sizeof(char), 100),
*nbp = nbuf,
numbuf[10] = "",
*nmbp = numbuf,
*mapbuf,
*mbp,
c;
enum pfmode m = mode_find_name;
/* This function uses four buffers for parsing the file.
* fbuf[10000] for the file contents,
* nbuf[100] for the name,
* numbuf[10] for numbers,
* mapbuf[w*h] for the map
* The modes are self-explanatory. They make it easy to focus on one section at a time without an over-complicated hacked-together while loop
*/
while((c = fgetc(f)) != EOF) *(fbp++) = c;
fclose(f);
fbp = fbuf;
while(m != mode_finish){
switch(m){
case mode_find_name:
if(*fbp == '"'){
m = mode_get_name;
}
fbp++;
break;
case mode_get_name:
if(*fbp == '"'){
m = mode_find_width;
pd->name = nbuf;
} else {
*nbp = *fbp;
nbp++;
}
fbp++;
break;
case mode_find_width:
if(*fbp == '['){
m = mode_get_width;
}
fbp++;
break;
case mode_get_width:
if(*fbp == ','){
pd->width = atoi(numbuf);
nmbp = numbuf;
m = mode_find_height;
} else {
*nmbp = *fbp;
nmbp++;
}
fbp++;
break;
case mode_find_height:
while(*fbp == ' ') fbp++;
m = mode_get_height;
break;
case mode_get_height:
if(*fbp == ']'){
pd->height = atoi(numbuf);
mapbuf = calloc(sizeof(char), pd->width * pd->height);
mbp = mapbuf;
m = mode_get_map;
} else {
*nmbp = *fbp;
nmbp++;
}
fbp++;
break;
case mode_get_map:
if(mbp-mapbuf >= pd->width * pd->height){
pd->map = mapbuf;
m = mode_finish;
}
if(*fbp == '.'){
*mbp = 0;
mbp++;
}
if(*fbp == '*'){
*mbp = 1;
mbp++;
}
fbp++;
break;
}
}
}
I feel that while this is definitely more verbose, it is far easier to follow.
Example File:
$ cat puppy.pc "Puppy" [10, 10] * * . . . . . . * * * . * * . . * * . * . . * * . . * * . . . . . . . . . . . . . . . . * * . . . . * . . * * * * . . * * . . . * * . . . * . * . . * * . . * . . . * * . . * * . . . . . . * * . . . .
Result
$ ./a.out puppy.pc Puppy (10 x 10) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Another example:
$ cat test "Smiley"[5,5]......*.*......*...******
Result:
$ ./a.out test Smiley (5 x 5) * * * * * * * * *