0

I have to find within a text file the specific line that starts with a key word and then I have to analyze this line to extract informations. I'll make it clear by an example:

processor       : 0 
vendor_id       : GenuineIntel 
cpu family      : 6 
model           : 5 
model name      : Pentium II (Deschutes) 
stepping        : 2 
cpu MHz         : 400.913520 
cache size      : 512 KB 
fdiv_bug        : no 
hlt_bug         : no 

this is the text file (/proc/cpuinfo from Linux). I have to write a function that parses the file until it finds "model name : " and then it has to store in a char Array the information "Pentium II (Deschutes)". This is what I coded until now:

int get_cpu(char* info)
{
    FILE *fp; 
    char buffer[1024]; 
    size_t bytes_read; 
    char *match; 

    /* Read the entire contents of /proc/cpuinfo into the buffer.  */ 
    fp = fopen("/proc/cpuinfo", "r"); 

    bytes_read = fread(buffer, 1, sizeof (buffer), fp); 

    fclose (fp); 

    /* Bail if read failed or if buffer isn't big enough.  */ 
    if (bytes_read == 0 || bytes_read == sizeof (buffer)) 
        return 0; 

    /* NUL-terminate the text.  */ 
    buffer[bytes_read] == '\0'; 

    /* Locate the line that starts with "model name".  */ 
    match = strstr(buffer, "model name"); 

    if (match == NULL) 
        return 0; 

    /* copy the line */
    strcpy(info, match);
}

it says that the buffer is always not big enough......

rugrag
  • 163
  • 1
  • 1
  • 7
  • 5
    "it says that the buffer is always not big enough". That's because it is not big enough. Just manually run `cat /proc/cpuinfo > output` and have a look at the size of the `output` file. What do you find? – kaylum Oct 19 '15 at 21:50
  • on my system it is a very large document... I should write a program that could work on every Linux system – rugrag Oct 19 '15 at 21:58
  • @kaylum *useless use of cat* detected \*scnr\* (still +1) [explanation: a simple `cp` would do if you want to examine with `ls` -- better just use `wc -c`] –  Oct 20 '15 at 00:12

2 Answers2

2

Well going beyond the simple fact that /proc/cpuinfo is typically bigger than 1024 bytes:

> wc -c </proc/cpuinfo
3756

and such, of course your buffer is to small to read the whole file at once ...

What you try here is to process a text file and the natural way to do it would be line by line.

Try something like

(edit: finally replacing the whole thing with tested code ... it's not so easy to get strtok() right ... hehe)

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

int main(void)
{
    char buf[1024];
    char *val = 0;
    FILE *fp = fopen("/proc/cpuinfo", "r");
    if (!fp)
    {
        perror("opening `/proc/cpuinfo'");
        return 1;
    }

    while (fgets(buf, 1024, fp))        /* reads one line */
    {
        char *key = strtok(buf, " ");   /* gets first word separated by space */
        if (!strcmp(key, "model"))
        {
            key = strtok(0, " \t");     /* gets second word, separated by
                                         * space or tab */
            if (!strcmp(key, "name"))
            {
                strtok(0, " \t");         /* read over the colon */
                val = strtok(0, "\r\n");  /* read until end of line */
                break;
            }
        }
    }

    fclose(fp);

    if (val)
    {
        puts(val);
    }
    else
    {
        fputs("CPU model not found.\n", stderr);
    }
    return 0;
}

usage:

> gcc -std=c89 -Wall -Wextra -pedantic -o cpumodel cpumodel.c
> ./cpumodel
AMD A6-3670 APU with Radeon(tm) HD Graphics
  • I used to use the implicit test of a `NULL` pointer being `0` as you do, and the explicit use in `strtok()` too, until I came across articles such as this SO answer. http://stackoverflow.com/questions/9894013/is-null-always-zero-in-c – Weather Vane Oct 19 '15 at 22:03
  • @WeatherVane `NULL` doesn't need to be `0` (as in all bits zero), but `0` in a pointer context means the `NULL` pointer, whatever its representation may be, and a `NULL` pointer always evaluates false in a boolean context ... so the "traditional" way is safe. –  Oct 19 '15 at 22:06
  • @WeatherVane that being said, I just prefer the "terse" version here. It's a matter of style and `NULL` has of course the benefit of being more expressive. –  Oct 19 '15 at 22:12
  • I do take your stance with `int` used implicity as boolean (I hate the formal type) - for example `if(apples)` will be true if I have any - or even owe some! – Weather Vane Oct 19 '15 at 22:16
  • @WeatherVane not exactly sure what you're after with that but I guess you mean in a boolean context, only `0` is `false`, everything else is `true` -- leading to very weird constructs like `if (!(strcmp(...))` for testing equality of "strings". –  Oct 19 '15 at 22:18
  • @WeatherVane just assure you here the same holds for pointers and as `0` in a pointer context *means* the "NULL-pointer" (no matter whether its representation is all zero bits or not) ... something like `void *p; [...] if (!p)` is absolutely fine and safe :) –  Oct 19 '15 at 22:20
  • @WeatherVane found some nice source about that in the [`comp.lang.c` FAQ](http://c-faq.com/null/ptrtest.html) (ah, the good old usenet days) –  Oct 19 '15 at 23:16
-1

Please try this, it works, there are different ways to do it.

#include <usual.h>

int get_cpu( char *info )
{
  FILE *fp;
  char buffer[1024];
  size_t bytes_read;
  char *match;
  char *matchend;
  /* Read the entire contents of /proc/cpuinfo into the buffer.  */
  fp = fopen( "/proc/cpuinfo", "r" );
  bytes_read = fread( buffer, 1, sizeof( buffer ), fp );
  fclose( fp );
  /* Bail if read failed or if buffer isn't big enough.  */
  if ( bytes_read == 0 || bytes_read == sizeof( buffer ) )
    return 0;
  /* NUL-terminate the text.  */
  buffer[bytes_read] == '\0';
  // match=buffer;
  /* Locate the line that starts with "model name".  */
  match = strstr( buffer, "model name" );
  if ( match == NULL )
    return 0;
  /* copy the line */
  strncpy( info, match, 41 );
}

int main(  )
{
  char info[255];
  memset( info, '\0', 255 );
  get_cpu( info );
  printf( "\nthe data we extracted: %s ", info );
  getchar(  );
}
BobRun
  • 756
  • 5
  • 12
  • What the hell is `usual.h`? And how will this **not** fail in the same way as the code in the question? –  Oct 19 '15 at 23:56
  • That's where I put my usual includes, so it became : usual , simpler. Now you could compile and run. And it works. – BobRun Oct 19 '15 at 23:58
  • No it won't as long as `/proc/cpuinfo` is bigger than 1024 bytes. Same problem as in the question. –  Oct 19 '15 at 23:59
  • and btw, doing something like this is really a *bad idea* ... use `#include`s *explicitly*. Include exactly what you need, not more, not less. –  Oct 20 '15 at 00:01
  • I took the file you showed, it has 10 lines and for that file, it works. If your real file is bigger you'll need more space. – BobRun Oct 20 '15 at 00:02
  • It's not *my* file, it's the file of the OP, and he obviously didn't quote the *whole* file. His buffer was 1024 bytes and it was to small to read it all at once. –  Oct 20 '15 at 00:03