/* formatter.C
 * John Viega
 *
 * Jan 28-29 2000
 */

#include "resultsdb.H"
#include "vulndb.H"
#include "config.H"
#include "query.H"
#include "fatal.H"
#include "strutils.H"
#include "strpool.H"
#include <stdio.h>
#include <stdlib.h>


static int reverse_sort;

int SortSeverityFilename(const void *a, const void *b)
{
  Result *r1 = (Result *)(reverse_sort ? b : a);
  Result *r2 = (Result *)(reverse_sort ? a : b);
  if(r1->s > r2->s) return -1; // R1 comes first
  if(r1->s < r2->s) return 1;
  if(int x=strcmp(r1->source, r2->source))
    return x;
  // TODO:  Sort on which token on the line if the lines are equal
  return (r1->line)-(r2->line);
}
int SortSeverityVulname(const void *a, const void *b)
{
  Result *r1 = (Result *)(reverse_sort ? b : a);
  Result *r2 = (Result *)(reverse_sort ? a : b);
  if(r1->s > r2->s) return -1; // R1 comes first
  if(r1->s < r2->s) return 1;
  if(int x=((r1->id)-(r2->id)))
    return x;
  // Reversed on purpose... 2nd explanation is usually more important
  // all other things equal
  if(int z=((r2->expl)-(r1->expl)))
    return z;
  if(int y=strcmp(r1->source, r2->source))
    return y;
  // TODO:  Sort on which token on the line if the lines are equal
  return (r1->line)-(r2->line);
}
int SortVulnameSeverity(const void *a, const void *b)
{
  Result *r1 = (Result *)(reverse_sort ? b : a);
  Result *r2 = (Result *)(reverse_sort ? a : b);
  if(int x=((r1->id)-(r2->id)))
    return x;
  if(r1->s > r2->s) return -1; // R1 comes first
  if(r1->s < r2->s) return 1;
  // Reversed on purpose... 2nd explanation is usually more important
  // all other things equal
  if(int z=((r2->expl)-(r1->expl)))
    return z;
  if(int y=strcmp(r1->source, r2->source))
    return y;
  // TODO:  Sort on which token on the line if the lines are equal
  return (r1->line)-(r2->line);
}
int SortVulnameFilename(const void *a, const void *b)
{
  Result *r1 = (Result *)(reverse_sort ? b : a);
  Result *r2 = (Result *)(reverse_sort ? a : b);
  if(int x=((r1->id)-(r2->id)))
    return x;
  // Reversed on purpose... 2nd explanation is usually more important
  // all other things equal
  if(int z=((r2->expl)-(r1->expl)))
    return z;
  if(int y=strcmp(r1->source, r2->source))
    return y;
  // TODO:  Sort on which token on the line if the lines are equal
  return (r1->line)-(r2->line);
}
int SortFilenameSeverity(const void *a, const void *b)
{
  Result *r1 = (Result *)(reverse_sort ? b : a);
  Result *r2 = (Result *)(reverse_sort ? a : b);
  if(int x=strcmp(r1->source, r2->source))
    return x; 
  if(r1->s > r2->s) return -1; // R1 comes first
  if(r1->s < r2->s) return 1;
  // TODO:  Sort on which token on the line if the lines are equal
  return (r1->line)-(r2->line);
}
int SortFilenameVulname(const void *a, const void *b)
{
  Result *r1 = (Result *)(reverse_sort ? b : a);
  Result *r2 = (Result *)(reverse_sort ? a : b);
  if(int x=strcmp(r1->source, r2->source))
    return x; 
  if(int y=((r1->id)-(r2->id)))
    return y;
  if(r1->s > r2->s) return -1; // R1 comes first
  if(r1->s < r2->s) return 1;
  // Reversed on purpose... 2nd explanation is usually more important
  // all other things equal
  if(int z=((r2->expl)-(r1->expl)))
    return z;
  // TODO:  Sort on which token on the line if the lines are equal
  return (r1->line)-(r2->line);
}

void Sort(Result *results, unsigned int num_entries)
{
  int sort_type = GetSortType();
  reverse_sort = GetReverseSort();
  switch(sort_type)
    {
    case 1:
      qsort(results, num_entries, sizeof(Result), 
	    SortSeverityFilename);
      break;
    case 2:
      qsort(results, num_entries, sizeof(Result), 
	    SortSeverityVulname);
      break;
    case 3:
      qsort(results, num_entries, sizeof(Result), 
	    SortVulnameSeverity);
      break;
    case 4:
      qsort(results, num_entries, sizeof(Result), 
	    SortVulnameFilename);
      break;
    case 5:
      qsort(results, num_entries, sizeof(Result), 
	    SortFilenameSeverity);
      break;
    case 6:
      qsort(results, num_entries, sizeof(Result), 
	    SortFilenameVulname);
      break;
    default:
      return;

    }
  
}

void PrintFooter()
{
  if(GetMSVSFormat()) { 
    return;
  }
  fprintf(GetOutputFile(), NEWLINE "----------------");
}

char *GetSeverityAsString(Severity s)
{
  switch(s)
    {
    case S_NO_RISK:
      return "No Risk";
    case S_LOW_RISK:
      return "Low Risk";
    case S_MODERATE_RISK:
      return "Some risk";
    case S_RISKY:
      return "Risky";
    case S_VERY_RISKY:
      return "Very Risky";
    case S_MOST_RISKY:
      return "Urgent";
    default:
      return "Unknown";
    }
}

void FormatterOutput()
{
  unsigned int num_entries;
  unsigned int table_size;
  char        *tmp;
  Result      *results = GetResultTable(num_entries, table_size);
  const char  *sep1 = GetMSVSFormat() ? "(" : ":";
  const char  *sep2 = GetMSVSFormat() ? ") : warning ITS4 : " : ":";
  const char  *sep3 = GetMSVSFormat() ? " : " : NEWLINE;

  Sort(results, num_entries);

  Result *r = 0;
  for(unsigned int i=0; i<num_entries; i++)
    {
      Result *last = r;
      r = &(results[i]);
      if((ShowDescription() || ShowSolution()) && last && 
	 ((last->v->id != r->v->id)||(last->expl != r->expl)))
	{
	  if(last->v->printed)
	    {
	      PrintFooter();
	    }
	  else
	    {
	      last->v->printed = 1;
	      if(ShowDescription())
		{
		  tmp = Wrap(GetString(last->v->desc));
		  fprintf(GetOutputFile(), "%s%s", sep3, tmp);
		  delete[] tmp;
		}
	      if(ShowSolution())
		{
		  tmp = Wrap(GetString(last->v->solution));
		  fprintf(GetOutputFile(), "%s%s", sep3, tmp);
		  delete[] tmp;
		}
	      PrintFooter();
	    }
	}

      if(GetShowSeverity())
	{
	  char *severity = GetSeverityAsString(r->s);
	  fprintf(GetOutputFile(), "%s%s%s%d%s(%s) %s%s", 
		  last ? NEWLINE : "", r->source, 
		  sep1,  r->line, sep2, severity, GetNameById(r->id), 
		  r->v->printed ? " (see above)"  : "");  
	}
      else
	{
	  fprintf(GetOutputFile(), "%s%s%s%d%s %s%s", 
		  last ? NEWLINE : "", r->source, sep1,
		  r->line, sep2,  GetNameById(r->id), r->v->printed ? 
		  " (see above)" : "");
	}
    }
  if(r && !r->v->printed && (ShowDescription() || ShowSolution()))
    {
      if(ShowDescription())
	{
	  tmp = Wrap(GetString(r->v->desc));
	  fprintf(GetOutputFile(), "%s%s", sep3, tmp);
	  delete[] tmp;
	}
      if(ShowSolution())
	{
	  tmp = Wrap(GetString(r->v->solution));
	  fprintf(GetOutputFile(), "%s%s", sep3, tmp);
	  delete[] tmp;
	}
    PrintFooter();
    }
  else
    {
      if(r && (ShowDescription() || ShowSolution()))
	{
	  PrintFooter();
	}
    }
  if(num_entries)
    fprintf(GetOutputFile(), NEWLINE);
}

void RunQuery(char *name)
{
  VulnInfo *v = GetVulnInfo(name);
  if(!v)
    {
      fprintf(stderr, "%s:" NEWLINE "No such method in database." NEWLINE,
	      GetProgramName());
    }
  else
    {
      char *severity = GetSeverityAsString(v->severity);
      fprintf(GetOutputFile(), "%s: %s" NEWLINE, name, severity);
      char *tmp;
      if(ShowDescription())
      {
	  tmp = Wrap(GetString(v->desc));
	  fprintf(GetOutputFile(), "%s" NEWLINE, tmp);
	  delete[] tmp;
      }
      if(ShowSolution())
      {
	tmp = Wrap(GetString(v->solution));
	fprintf(GetOutputFile(), "%s" NEWLINE, tmp);
	delete[] tmp;
      }
    }
  PrintFooter();
  fprintf(GetOutputFile(), NEWLINE);
}

void RunQueries()
{
  int  num_queries;
  char **queries = GetQueries(num_queries);

  for(int i=0;i<num_queries;i++)
    RunQuery(queries[i]);
  DeallocQueries();
}
