/*

IsMyHwOK: A tool for checking how well-supported hardware is using Free drivers.
Copyright (C) 2010  Thomas Glyn Dennis

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hardware.h"
#include "osstuff.h"

/* [INTERNAL] Define column indices. */

#define LSPCI_DEVID 2
#define LSUSB_DEVID 5

/* [INTERNAL] Find the manufacturer and name for a device using an ID. */

void getNameAndMake (const CsvTable *lst, const char *id, char **nm, char **mk)
{

  long rec = 0;

  /* Initialise. */

  if (nm) { *nm = NULL; }
  if (mk) { *mk = NULL; }

  /* Validate parameters. */

  if (!lst || !id || !nm || !mk) { return; }

  if (lst->width < 2) { return; }

  if (strlen (id) != 9) { return; }

  /* Search through the list. */

  for (rec = 0; rec < lst->height; rec++)
  {

    /* Determine whether to look for a manufacturer or a device name. */

    if (*mk == NULL)
    {

      /* Check for a matching manufacturer. */

      if (lst->cell[0][rec])
      {

        if (strlen (lst->cell[0][rec]) >= 6)
        {

          if (memcmp (id, lst->cell[0][rec], 4) == 0)
          {

            *mk = &lst->cell[0][rec][6];

          }

        }

      }

    }
    else
    {

      /* Check for a matching device. */

      if (lst->cell[1][rec])
      {

        if (strlen (lst->cell[1][rec]) >= 6)
        {

          if (memcmp (&id[5], lst->cell[1][rec], 4) == 0)
          {

            *nm = &lst->cell[1][rec][6];

            break;

          }

        }

      }

    }

  }

}

/* [INTERNAL] Get a string from a CsvTable using a device ID. */

char *getRatingString (const CsvTable *list, const char *id)
{

  long i = 0;

  if (!list || !id) { return NULL; }

  if (list->width < 2) { return NULL; }

  for (i = 0; i < list->height; i++)
  {

    if (list->cell[0][i])
    {

      if (strcmp (list->cell[0][i], id) == 0) { return list->cell[1][i]; }

    }

  }

  return NULL;

}

/* [PUBLIC] Get a list of hardware attached to this system. */

CsvTable *getHardware (void)
{

  CsvTable *hw_list = NULL;  /* End result. */

  CsvTable *pci_ids = NULL;  /* PCI Device/Manufacturer name. */
  CsvTable *usb_ids = NULL;  /* USB Device/Manufacturer name. */
  CsvTable *dev_pci = NULL;  /* List of detected PCI devices. */
  CsvTable *dev_usb = NULL;  /* List of detected USB devices. */
  CsvTable *txt_pci = NULL;  /* Comment file for PCI devices. */
  CsvTable *txt_usb = NULL;  /* Comment file for USB devices. */
  CsvTable *sup_pci = NULL;  /* Ratings file for PCI devices. */
  CsvTable *sup_usb = NULL;  /* Ratings file for USB devices. */

  char *name = NULL;
  char *make = NULL;
  char *rate = NULL;
  char *info = NULL;

  char file_name[512];

  long rec, i;

  /* Get lspci output. */

  dev_pci = lspci ();

  if (!dev_pci) { fprintf (stderr, "Couldn't read lspci output.\n"); }

  /* Get lsusb output. */

  dev_usb = lsusb ();

  if (!dev_usb) { fprintf (stderr, "Couldn't read lsusb output.\n"); }

  /* Load the PCI device names file. */

  pci_ids = csvLoadFile ("/usr/share/misc/pci.ids", '\t', '"');

  if (!pci_ids) { pci_ids = csvLoadFile ("./pci.ids", '\t', '"'); }

  if (!pci_ids) { fprintf (stderr, "Couldn't load pci.ids\n"); }

  /* Load the USB device names file. */

  usb_ids = csvLoadFile ("/usr/share/misc/usb.ids", '\t', '"');

  if (!usb_ids) { usb_ids = csvLoadFile ("./usb.ids", '\t', '"'); }

  if (!usb_ids) { fprintf (stderr, "Couldn't load usb.ids\n"); }

  /* Load the PCI device comments file. */

  sprintf (file_name, "/usr/share/ismyhwok/pci_%s.csv", getenv ("LANG"));

  txt_pci = csvLoadFile (file_name, ',', '"');

  if (!txt_pci)
  {

    sprintf (file_name, "./pci_%s.csv", getenv ("LANG"));

    txt_pci = csvLoadFile (file_name, ',', '"');

    if (!txt_pci) { fprintf (stderr, "Couldn't load PCI comments file.\n"); }

  }

  /* Load the USB device comments file. */

  sprintf (file_name, "/usr/share/ismyhwok/usb_%s.csv", getenv ("LANG"));

  txt_usb = csvLoadFile (file_name, ',', '"');

  if (!txt_usb)
  {

    sprintf (file_name, "./usb_%s.csv", getenv ("LANG"));

    txt_usb = csvLoadFile (file_name, ',', '"');

    if (!txt_usb) { fprintf (stderr, "Couldn't load USB comments file.\n"); }

  }

  /* Load the PCI device ratings file. */

  sup_pci = csvLoadFile ("/usr/share/ismyhwok/pci_ratings.csv", ',', '"');

  if (!sup_pci) { sup_pci = csvLoadFile ("./pci_ratings.csv", ',', '"'); }

  if (!sup_pci) { fprintf (stderr, "Couldn't load PCI ratings file.\n"); }

  /* Load the USB device ratings file. */

  sup_usb = csvLoadFile ("/usr/share/ismyhwok/usb_ratings.csv", ',', '"');

  if (!sup_usb) { sup_usb = csvLoadFile ("./usb_ratings.csv", ',', '"'); }

  if (!sup_usb) { fprintf (stderr, "Couldn't load USB ratings file.\n"); }

  /* Build the main CsvTable. */

  i = 1;

  if (dev_pci) { i += dev_pci->height; }
  if (dev_usb) { i += dev_usb->height; }

  hw_list = csvCreateTable (6, i);

  if (!hw_list) { return NULL; }

  /* Write header. */

  csvEditCell (hw_list, 0, 0, "?");
  csvEditCell (hw_list, 1, 0, "PCI/USB ID");
  csvEditCell (hw_list, 2, 0, "Type");
  csvEditCell (hw_list, 3, 0, "Name");
  csvEditCell (hw_list, 4, 0, "Make");
  csvEditCell (hw_list, 5, 0, "Comments");

  /* Add PCI devices. */

  rec = 1;

  if (dev_pci)
  {

    for (i = 0; i < dev_pci->height; i++)
    {

      /* Get info. */

      getNameAndMake (pci_ids, dev_pci->cell[LSPCI_DEVID][i], &name, &make);

      rate = getRatingString (sup_pci, dev_pci->cell[LSPCI_DEVID][i]);
      info = getRatingString (txt_pci, dev_pci->cell[LSPCI_DEVID][i]);

      /* Add it to the list. */

      csvEditCell (hw_list, 0, rec, rate);
      csvEditCell (hw_list, 1, rec, dev_pci->cell[LSPCI_DEVID][i]);
      csvEditCell (hw_list, 2, rec, "PCI");
      csvEditCell (hw_list, 3, rec, name);
      csvEditCell (hw_list, 4, rec, make);
      csvEditCell (hw_list, 5, rec, info);

      rec++;

    }

  }

  /* Add USB devices. */

  if (dev_usb)
  {

    for (i = 0; i < dev_usb->height; i++)
    {

      /* Get info. */

      getNameAndMake (usb_ids, dev_usb->cell[LSUSB_DEVID][i], &name, &make);

      rate = getRatingString (sup_usb, dev_usb->cell[LSUSB_DEVID][i]);
      info = getRatingString (txt_usb, dev_usb->cell[LSUSB_DEVID][i]);

      /* Add it to the list. */

      csvEditCell (hw_list, 0, rec, rate);
      csvEditCell (hw_list, 1, rec, dev_usb->cell[LSUSB_DEVID][i]);
      csvEditCell (hw_list, 2, rec, "USB");
      csvEditCell (hw_list, 3, rec, name);
      csvEditCell (hw_list, 4, rec, make);
      csvEditCell (hw_list, 5, rec, info);

      rec++;

    }

  }

  /* Free the tables. */

  csvFree (txt_usb);
  csvFree (txt_pci);
  csvFree (sup_usb);
  csvFree (sup_pci);
  csvFree (dev_usb);
  csvFree (dev_pci);
  csvFree (usb_ids);
  csvFree (pci_ids);

  /* Return the result. */

  return hw_list;

}
