views:

499

answers:

4

This is "how to parse a config file" question. Basically i have a text file (/etc/myconfig) that has all kind of settings. I need to read that file and search for the string:

wants_return=yes

once I locate that string I need to parse it and return only whatever it is after the equal sign. I've tried using a combinations of fgets and strtok but I'm getting confused here. in any case anyone knows a function that can perform this?

Code is appreciated.

thanks

A: 

With a POSIX shell, I'd use something like:

answer=`egrep 'wants_config[ ]*=' /etc/myconfig | sed 's/^.*=[ ]*//'`

Of course, if you're looking for an answer that uses the C STDIO library, then you really need to review the STDIO documentation.

Craig Trader
C, i need C code. I know how to do that from the command line. and no, i can't use system()
wonderer
+1  A: 

This works: (note: I'm unsure if fgets is supposed to include the newline character in the returned string; if it isn't, you can drop the check for it)

#include <stdio.h>

const unsigned MAXLINE=9999;
char const* FCFG="/etc/myconfig";
char const* findkey="wants_return=";


char * skip_ws(char *line)
{
    return line+strspn(line," \t");
}

char * findval(char *line,char const* prefix,int prelen)
{
    char *p;
    p=skip_ws(line);
    if (strncmp(p,prefix,prelen)==0)
        return p+prelen;
    else
        return NULL;
}

char *findval_slow(char *line,char const* prefix)
{
    return findval(line,prefix,strlen(prefix));
}

int main() {
    FILE *fcfg;
    char line[MAXLINE];
    char *p,*pend;
    int findlen;

    findlen=strlen(findkey);

    fcfg=fopen(FCFG,"r");

    while (p=fgets(line,MAXLINE,fcfg)) {
        printf("Looking at %s\n",p);
        if (p=findval(line,findkey,findlen)) {
            pend=p+strlen(p)-1; /* check last char for newline terminator */
            if (*pend=='\n') *pend=0;
            printf("Found %s\n",p); /* process/parse the value */
        }
    }
    return 0;
}
wrang-wrang
thank you. that's what i was looking for.
wonderer
+1  A: 

Here's a quick example using strtok:

const int linelen = 256;
char line[linelen];

FILE* fp = fopen(argv[1], "r");
if (fp == NULL) {
    perror("Error opening file");
} else {
    while (! feof(fp)) {
        if (fgets(line, linelen , fp)) {
            const char* name = strtok(line, "= \r\n");
            const char* value = strtok(NULL, "= \r\n");
            printf("%s => %s\n", name, value);
        }
    }
    fclose (fp);
}

Note, you'll need to put some additional error checking around it, but this works to parse the files I threw at it.

jheddings
`feof()` doesn't work the way you think it does.
pmg
Can you explain a little more? From the docs "Checks whether the End-of-File indicator associated with stream is set, returning a value different from zero if it is."
jheddings
It works in your code, but it doesn't check if the file is at the end. It checks if a previous read attempt failed due to the file ending. Usually there's no `feof()` in code that stops when the file ends. In your code, the `if` fails, the code goes back to the `while`, which fails but is redundant.
pmg
It is usual to have 1 single test: `while(fgets(...)) { work(); }`
pmg
There are other reasons for `fgets()` to return null, not just for the file ending. My code will continue to loop until it consumes the entire file. In fact, `feof()` works _just_ as I think it does.
jheddings
If it becomes necessary to handle other errors, an `else` can be added to check `ferror()` and handle the error without breaking out of the read loop. This allows the caller to try to read again if the error is recoverable.
jheddings
I think that other kinds of error besides EOF (eg: network down, media removed) do not set `feof()` to return true. If I'm right you may be stuck in an infinite loop.
pmg
Confirm infinite loop on my machine. I tested with a file on a pen-disk and removed the pen-disk mid-way through the loop (I added a `getchar()` in there). Amazingly the program continued correctly to the end of file (file was cached) but entered an infinite loop after the last line.
pmg
A: 

From your comment, it looks like you're already getting the appropriate line from the text file using fgets and loading it into a character buffer. You can use strtok to parse the tokens from the line.

If you run it with the string buffer as the first argument, it will return the first token from that string. If you run the same command with the first argument set to NULL it will return subsequent tokens from the same original string.

A quick example of how to retrieve multiple tokens:

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

int main() {
  char buffer[17]="wants_return=yes";
  char* tok;
  tok = strtok(buffer, "=");
  printf("%s\n", tok); /* tok points to "wants_return" */
  tok = strtok(NULL, "=");
  printf("%s\n", tok); /* tok points to "yes" */
  return 0;
}

For the second strtok call, you can replace the "=" with "" to return everything to the end of the string, instead of breaking off at the next equal sign.

goldPseudo
The string comes from `fgets()`: it has a final newline. If you do the second `strtok` with `"\n"` you get all the remaining string **AND** get rid of the newline :)
pmg