Search code examples
cwifi

Code to parse wpa_cli SCAN_RESULTS output


I am working on a C code which executes the SCAN and SCAN_RESULTS command via wpa_cli and gets the wifi scan results. I have a problem in parsing the SCAN_RESULTS output to capture the key management and SSID fields under certain scenarios

The code I use to read all parameters is

sscanf(buf, "%s \t %s \t %s \t %s \t %[^\n]s", bssid, freq, siglevel, keymgmt, ssid);

The output of wpa_cli SCAN_RESULTS command will be

bssid / frequency / signal level / flags / ssid
00:00:00:00:00:00 2462 -49 [WPA2-PSK-CCMP][ESS] MYSSID
11:11:11:11:11:11 2437 -64 [WPA2-PSK-CCMP][ESS] ANOTHERSSID

Where the fields are separated by tabs(\t), with the above output my code works fine. But when my scan result have some open network then my code breaks and I have no idea how to change the code to suit my requirement

The output of wpa_cli SCAN_RESULTS command with open network

bssid / frequency / signal level / flags / ssid
00:00:00:00:00:00 2462 -49 [WPA2-PSK-CCMP][ESS] MYSSID
22:22:22:22:22:22 2437 -72  OPENSSID

with the above output my code reports the keymgmt variable holding "OPENSSID" and ssid variable is empty. But I want to have the keymgmt variable as empty and ssid to hold "OPENSSID". When I tried to capture the above output to file and tried hexdump I could see between the "signal level" and "ssid" there is two consecutive tabs(\t\t) present. Any pointers on how to change the sscanf code to make it work


Solution

  • sscanf() format not working as expected.

    IMHO there is no single sscanf() with a format that will fill a destination string with "" (or skip it) and then fill a subsequent destination string with text. Once scanf() finds nothing to put in a destination string, scanning stops.

    Alternative code is presented.

    int ScanTabbedData(const char *s, char *data[], size_t n) {
      size_t i = 0;
      const char *Start = s;
      while (1) {
        if ((*s == '\t') || (*s == '\0')) {
          if (i >= n) {
            return -1;  // More than n data
          }
          size_t Length = s - Start;
          memcpy(data[i], Start, Length);
          data[i][Length] = '\0';
          i++;
          if (*s == '\0') {
            return i;
          }
          s++;
          Start = s;
        }
        else s++;
      }
      return 0;
    }
    
    int main() {
      char *str1= "Test\tHope\tHello";
      char *str2 = "Test\t\tHello";
      char *t[3];
      t[0] = malloc(100); t[1] = malloc(100); t[2] = malloc(100);
      int n;
      n = ScanTabbedData(str1, t, 3);
      printf("%d:%s:%s:%s\n", n,t[0], t[1], t[2]);
      n = ScanTabbedData(str2, t, 3);
      printf("%d:%s:%s:%s\n", n,t[0], t[1], t[2]);
    }
    

    2nd Alternative, use strtok(). I'm not sure it handles a lead/consecutive \t as OP may want though.

    Below is my original answer, which does not meet OP's need.


    ** Original answer **

    First, the result of any sscanf() should be assessed as in

    if (3 != sscanf(buf, "%s\t%s\t%[^\n]s", a, b, c)) handle_error();
    

    If, as the OP suggest, the fields are truly \t separated, use

    if (3 != sscanf(buf, "%[^\t]%*1[\t]%[^\t]%*1[\t]%[^\n]", a, d, c)) handle_error();
    

    "%[^\t]" scan until a tab is found.
    "%*1[\t]" scan 1 tab, do not save nor add to sscanf() result.
    "%[^\n]" scan until an end-of-line is found.

    Note:
    3 rather than OP's 5 parameters used for simplicity.
    In sscanf(), " \t " does the same thing as " ".
    In "%[^\n]s, certainly the s is not needed.