Search code examples
cstringparsingproc

alternative for sscanf string with spaces in c?


I am trying to retrieve information from /proc/cpuinfo file. I have retrieved number of cpu core using sscanf.

Now I am trying to retrieve model name similarly but I sscanf is not working this time because model name is a string including spaces.

Is there any alternative to retrieve it?

char *get_cpu_model()
{
   int fp;
   int r;
   char* match;
   char *cpu_model;

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

    if (fp == -1) 
   {   
       printf("Error! Could not open file\n"); 
       return 0;
   } 
    while( r != EOF){

       r = ReadTextLine(fp, buffer, BUFFER_SIZE);
    //    printf("%d %s\n", buffer_size, buffer);
       match = strstr (buffer, "model name");

       if (match !=NULL){
            /* Parse the line to extract the clock speed. */
            sscanf (match, "model name : %s", cpu_model);
            break;
       }
   }
   close(fp);

   return cpu_model;
}

The proc/cpuinfo file looks something like this:

processor:0
cpu core :1
model name: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz


Solution

  • Out of the fact you use r non initialized when you enter in the loop with an undefined behavior, it is difficult to find your problem without knowing the definition of ReadTextLine.

    The other answer speak about some possibile problems.

    Here is a proposal to do the work :

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    char * get_cpu()
    {
      FILE * fp = fopen("/proc/cpuinfo", "r");
      char line[256];
      char * result = NULL;
    
      if (fp == NULL) {
        /* low probability to happen */
        perror("cannot read /proc/cpuinfo");
        return NULL;
      }
    
      while (fgets(line, sizeof(line), fp) != NULL) {
        char * match = strstr(line, "model name");
    
        /* bypass ':' then possible spaces */
        if ((match != NULL) && ((match = strchr(match, ':')) != NULL)) {
          do
            match += 1;
          while (*match && isspace((unsigned char) *match));
    
          if (*match) {
            /* remove possible spaces including \r\n at end of line */
            char * pend = match + strlen(match);
    
            while (isspace((unsigned char) *--pend))
              *pend = 0;
    
            /* duplicate the string to not return an address in the stack */
            result = strdup(match);
            break;
          }
        }
      }
    
      fclose(fp);
      return result;
    }
    
    int main()
    {
      char * s = get_cpu();
    
      if (s != NULL) {
        printf("(first) cpu is '%s'\n", s);
        /* the result was allocated, free it when not needed anymore */
        free(s);
      }
    
      return 0;
    }
    

    Compilation and execution :

    pi@raspberrypi:/tmp $ gcc -Wall -Werror -pedantic c.c
    pi@raspberrypi:/tmp $ ./a.out
    (first) cpu is 'ARMv7 Processor rev 3 (v7l)'
    pi@raspberrypi:/tmp $ 
    

    In my case the beginning of /proc/cpuinfos is :

    pi@raspberrypi:/tmp $ head -2 /proc/cpuinfo
    processor   : 0
    model name  : ARMv7 Processor rev 3 (v7l)
    pi@raspberrypi:/tmp $ 
    

    If you really want to have a function ReadTextLine it must do more than just a fgets, so let decide it also remove the spaces at the end of the line (it is useless to move spaces at the beginning of the line in this context)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    char * ReadTextLine(FILE * fp, char * line, int size)
    {
      if (fgets(line, size, fp) == NULL)
        return NULL;
    
      /* remove possible spaces including \r\n at end of line */
      char * pend = line + strlen(line);
    
      while ((pend != line) && isspace((unsigned char) *--pend))
        *pend = 0;
    
      return line;
    }
    
    char * get_cpu()
    {
      FILE * fp = fopen("/proc/cpuinfo", "r");
      char line[256];
      char * result = NULL;
    
      if (fp == NULL) {
        /* probably never happend under linux */
        perror("cannot read /proc/cpuinfo");
        return NULL;
      }
    
      while (ReadTextLine(fp, line, sizeof(line)) != NULL) {
        char * match = strstr(line, "model name");
    
        /* bypass ':' then possible spaces */
        if ((match != NULL) && ((match = strchr(match, ':')) != NULL)) {
          do
            match += 1;
          while (*match && isspace((unsigned char) *match));
    
          if (*match) {
            result = strdup(match);
            break;
          }
        }
      }
    
      fclose(fp);
      return result;
    }