/* relational.c:
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/stdarg.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/vu/safe-vu-utils-vfdbuf.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/sort/qsort.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "tla/libawk/relational.h"


/* __STDC__ prototypes for static functions */
static int rec_cmp_by_field (void * va, void * vb, void * vdata);
static int rec_cmp_by_field_fn (void * va, void * vb, void * vdata);
static int rec_cmp_by_fields (void * va, void * vb, void * vdata);
static int cmp_fields (rel_table table1,
                       ssize_t r1,
                       ssize_t c1,
                       rel_table table2,
                       ssize_t r2,
                       ssize_t c2);
static rel_record rel_read_record (int fd,
                                   int n_fields,
                                   const char * err_name,
                                   const char * err_src);
static void rel_print_record (int fd, rel_record rec);
static void rel_print_pika_escape_iso8859_1_record (int fd, int escape_classes, rel_record rec);
static void rel_print_record_sp (int fd, rel_record rec);
static void rel_print_pika_escape_iso8859_1_record_sp (int fd, int escape_classes, rel_record rec);


int
rel_n_records (rel_table r)
{
  return ar_size ((void *)r._r, 0, sizeof (rel_record));
}


int
rel_n_fields (rel_record r)
{
  return ar_size ((void *)r._c, 0, sizeof (rel_field));
}



rel_record
rel_peek_record (rel_table r, ssize_t row)
{
  rel_record rec;

  if ((row < 0) || (row > rel_n_records (r)))
    return rel_record_nil;

  rec = r._r[row];

  return rec;
}


rel_field
rel_get_field (rel_table r, ssize_t row, ssize_t col)
{
  rel_record rec;

  if ((row < 0) || (row > rel_n_records (r)))
    return rel_field_nil;

  rec = r._r[row];

  if ((col < 0) || (col > rel_n_fields (rec)))
    return rel_field_nil;

  dstr_ref (rec._c[col]._d);

  return rec._c[col];
}


t_dstr
rel_get_dstr (rel_table r, ssize_t row, ssize_t col)
{
  t_dstr answer;

  answer = rel_peek_dstr (r, row, col);
  dstr_ref (answer);
  return answer;
}


t_dstr
rel_peek_dstr (rel_table r, ssize_t row, ssize_t col)
{
  rel_record rec;

  if ((row < 0) || (row > rel_n_records (r)))
    return 0;

  rec = r._r[row];

  if ((col < 0) || (col > rel_n_fields (rec)))
    return 0;

  return rec._c[col]._d;
}


const t_uchar *
rel_peek_str (rel_table r, ssize_t row, ssize_t col)
{
  t_dstr it;

  it = rel_peek_dstr (r, row, col);
  return dstr_data (0, it);
}


t_dstr
rel_rec_peek_dstr (rel_record rec, ssize_t col)
{
  if ((col < 0) || (col > rel_n_fields (rec)))
    return 0;

  return rec._c[col]._d;
}


const t_uchar *
rel_rec_peek_str (rel_record rec, ssize_t col)
{
  t_dstr it;

  it = rel_rec_peek_dstr (rec, col);
  return dstr_data (0, it);
}


int
rel_set_taking (rel_table r,
                ssize_t row,
                ssize_t col,
                rel_field d)
{
  rel_record rec;

  if ((row < 0) || (row > rel_n_records (r)))
    return -1;

  rec = r._r[row];

  if ((col < 0) || (col > rel_n_fields (rec)))
    return -1;

  dstr_unref (rec._c[col]._d);
  rec._c[col] = d;

  return 0;
}


int
rel_set_record (rel_table r, ssize_t row, rel_record rec)
{
  rel_record old_rec;

  if ((row < 0) || (row > rel_n_records (r)))
    return -1;

  old_rec = r._r[row];

  r._r[row] = rec;

  rel_free_record (old_rec);

  return 0;
}


rel_record
rel_make_record_0 (void)
{
  return rel_record_nil;
}


rel_record
rel_make_record_1_taking (rel_field f0)
{
  rel_record answer;
  answer = rel_record_nil;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f0;
  return answer;
}


rel_record
rel_make_record_2_taking (rel_field f0,
                          rel_field f1)
{
  rel_record answer;
  answer = rel_record_nil;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f0;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f1;
  return answer;
}


rel_record
rel_make_record_3_taking (rel_field f0,
                          rel_field f1,
                          rel_field f2)
{
  rel_record answer;
  answer = rel_record_nil;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f0;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f1;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f2;
  return answer;
}


rel_record
rel_make_record_4_taking (rel_field f0,
                          rel_field f1,
                          rel_field f2,
                          rel_field f3)
{
  rel_record answer;
  answer = rel_record_nil;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f0;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f1;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f2;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f3;
  return answer;
}


rel_record
rel_make_record_5_taking (rel_field f0,
                          rel_field f1,
                          rel_field f2,
                          rel_field f3,
                          rel_field f4)
{
  rel_record answer;
  answer = rel_record_nil;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f0;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f1;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f2;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f3;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f4;
  return answer;
}


rel_record
rel_make_record_6_taking (rel_field f0,
                          rel_field f1,
                          rel_field f2,
                          rel_field f3,
                          rel_field f4,
                          rel_field f5)
{
  rel_record answer;
  answer = rel_record_nil;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f0;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f1;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f2;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f3;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f4;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f5;
  return answer;
}


rel_record
rel_make_record_7_taking (rel_field f0,
                          rel_field f1,
                          rel_field f2,
                          rel_field f3,
                          rel_field f4,
                          rel_field f5,
                          rel_field f6)
{
  rel_record answer;
  answer = rel_record_nil;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f0;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f1;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f2;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f3;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f4;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f5;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f6;
  return answer;
}


rel_record
rel_make_record_8_taking (rel_field f0,
                          rel_field f1,
                          rel_field f2,
                          rel_field f3,
                          rel_field f4,
                          rel_field f5,
                          rel_field f6,
                          rel_field f7)
{
  rel_record answer;
  answer = rel_record_nil;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f0;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f1;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f2;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f3;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f4;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f5;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f6;
  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = f7;
  return answer;
}

const rel_record rel_record_null = { 0 } ; /* make this equivalent with rec_record_nil... maybe they are meant to be the same and should be a single variable? */ 

void
rel_add_records (rel_table * table, ...)
{
  va_list rp;
  rel_record r;

  va_start (rp, table);
  for (r = va_arg (rp, rel_record); r._c; r = va_arg (rp, rel_record))
    {
      *(rel_record *)ar_push ((void **)&table->_r, 0, sizeof (rel_record)) = r;
    }
  va_end (rp);
}


void
rel_add_field_taking (rel_record * r, rel_field field)
{
  *(rel_field *)ar_push ((void **)&r->_c, 0, sizeof (rel_field)) = field;
}


void
rel_add_field_to_row_taking (rel_table table, ssize_t row, rel_field field)
{
  invariant ((0 <= row) && (row < rel_n_records (table)));

  *(rel_field *)ar_push ((void **)&table._r[row]._c, 0, sizeof (rel_field)) = field;
}


rel_record
rel_singleton_record_taking (rel_field field)
{
  rel_record answer = rel_record_nil;

  *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (rel_field)) = field;
  return answer;
}




void
rel_free_table (rel_table t)
{
  int lim;
  int x;

  lim = rel_n_records (t);
  for (x = 0; x < lim; ++x)
    rel_free_record (t._r[x]);

  ar_free ((void **)&t._r, 0);
}

void
rel_free_record (rel_record r)
{
  int lim;
  int x;

  lim = rel_n_fields (r);
  for (x = 0; x < lim; ++x)
    dstr_unref (r._c[x]._d);
  ar_free ((void **)&r._c, 0);
}



rel_field
rel_field_ref (rel_field f)
{
  dstr_ref (f._d);
  return f;
}


void
rel_field_unref (rel_field f)
{
  dstr_unref (f._d);
}


const t_uchar *
rel_field_str (rel_field f)
{
  return dstr_data (0, f._d);
}


rel_field
rel_make_field_str (const t_uchar * s)
{
  rel_field f;

  f._d = dstr_save (0, s);
  return f;
}


rel_field
rel_make_field_str_n (const t_uchar * s, ssize_t len)
{
  rel_field f;

  f._d = dstr_save_n (0, s, len);
  return f;
}


rel_table
rel_ws_split (const t_uchar * string)
{
  rel_table answer = rel_table_nil;
  const t_uchar * start;
  const t_uchar * end;

  if (!string)
    return rel_table_nil;

  start = string;

  while (1)
    {
      while (char_is_space (*start))
        ++start;

      if (!*start)
        return answer;

      end = start;

      while (*end && !char_is_space (*end))
        ++end;

      rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str_n (start, end - start)), rel_record_null);

      start = end;
    }
}

rel_table
rel_nl_split (const t_uchar * string)
{
  rel_table answer = rel_table_nil;
  const t_uchar * start;
  const t_uchar * end;

  if (!string)
    return rel_table_nil;

  start = string;

  while (1)
    {
      if (!*start)
        return answer;

      end = start;

      while (*end && (*end != '\n'))
        ++end;

      rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str_n (start, end - start)), rel_record_null);

      if (*end)
        start = end + 1;
      else
        start = end;
    }
}




rel_table
rel_copy_table (rel_table t)
{
  rel_table answer;
  int records;
  int r;

  records = rel_n_records (t);

  answer = rel_table_nil;
  ar_setsize ((void **)&answer._r, 0, records, sizeof (rel_record));
  for (r = 0; r < records; ++r)
    answer._r[r] = rel_copy_record (t._r[r]);

  return answer;
}


rel_record
rel_copy_record (rel_record r)
{
  rel_record answer;
  int fields;
  int f;

  fields = rel_n_fields (r);

  answer = rel_record_nil;
  ar_setsize ((void **)&answer._c, 0, fields, sizeof (rel_field));
  for (f = 0; f < fields; ++f)
    {
      dstr_ref (r._c[f]._d);
      answer._c[f] = r._c[f];
    }

  return answer;
}


void
rel_append_x (rel_table * out, rel_table t)
{
  int lim;
  int x;

  lim = rel_n_records (t);

  for (x = 0; x < lim; ++x)
    {
      *(rel_record *)ar_push ((void **)&out->_r, 0, sizeof (rel_record)) = rel_copy_record (t._r[x]);
    }
}



void
rel_reverse_table (rel_table t)
{
  int a;
  int b;

  a = 0;
  b = rel_n_records (t) - 1;

  while (a < b)
    {
      rel_record tmp;

      tmp = t._r[a];
      t._r[a] = t._r[b];
      t._r[b] = tmp;

      ++a;
      --b;
    }
}


struct rel_sort_spec
{
  int reverse_p;
  int field;
};


void
rel_sort_table_by_field (int reverse_p,
                         rel_table t,
                         int field_n)
{
  struct rel_sort_spec spec;

  spec.reverse_p = reverse_p;
  spec.field = field_n;

  quicksort ((void *)t._r, rel_n_records (t), sizeof (rel_record), rec_cmp_by_field, (void *)&spec);
}


static int
rec_cmp_by_field (void * va, void * vb, void * vdata)
{
  rel_record * a;
  rel_record * b;
  struct rel_sort_spec * spec;
  t_dstr da;
  t_dstr db;
  int raw_cmp;

  a = (rel_record *)va;
  b = (rel_record *)vb;
  spec = (struct rel_sort_spec *)vdata;

  da = (*a)._c[spec->field]._d;
  db = (*b)._c[spec->field]._d;

  raw_cmp = dstr_cmp (da, db);

  if (!raw_cmp && (da != db))
    {
      if ((unsigned long)da < ((unsigned long)db))
        {
          dstr_ref (da);
          dstr_unref (db);
          (*b)._c[spec->field]._d = da;
        }
      else
        {
          dstr_ref (db);
          dstr_unref (da);
          (*a)._c[spec->field]._d = db;
        }
    }

  if (spec->reverse_p)
    {
      return -raw_cmp;
    }
  else
    {
      return raw_cmp;
    }
}



struct rel_sort_by_fn_spec
{
  int reverse_p;
  int field;
  int (*fn) (const t_uchar * va, const t_uchar * vb);
};


void
rel_sort_table_by_field_fn (int reverse_p,
                            rel_table t,
                            int field_n, 
                            int (*fn)(const t_uchar *, const t_uchar *))
{
  struct rel_sort_by_fn_spec spec;

  spec.reverse_p = reverse_p;
  spec.field = field_n;
  spec.fn = fn;

  quicksort ((void *)t._r, rel_n_records (t), sizeof (rel_record), rec_cmp_by_field_fn, (void *)&spec);
}


static int
rec_cmp_by_field_fn (void * va, void * vb, void * vdata)
{
  rel_record * a;
  rel_record * b;
  struct rel_sort_by_fn_spec * spec;

  a = (rel_record *)va;
  b = (rel_record *)vb;
  spec = (struct rel_sort_by_fn_spec *)vdata;

  if (spec->reverse_p)
    {
      return -spec->fn (dstr_data (0, (*a)._c[spec->field]._d), dstr_data (0, (*b)._c[spec->field]._d));
    }
  else
    {
      return spec->fn (dstr_data (0, (*a)._c[spec->field]._d), dstr_data (0, (*b)._c[spec->field]._d));
    }
}



struct rel_nsort_spec
{
  int reverse_p;
  int * fields;
};


void
rel_sort_table_by_fields (int reverse_p,
                          rel_table t,
                          int * fields)
{
  struct rel_nsort_spec spec;

  spec.reverse_p = reverse_p;
  spec.fields = fields;

  quicksort ((void *)t._r, rel_n_records (t), sizeof (rel_record), rec_cmp_by_fields, (void *)&spec);
}


int *
rel_sort_fields (int f, ...)
{
  va_list fp;
  int * answer;

  answer = 0;
  *(int *)ar_push ((void **)&answer, 0, sizeof (int)) = f;

  va_start (fp, f);
  while (1)
    {
      f = va_arg (fp, int);
      *(int *)ar_push ((void **)&answer, 0, sizeof (int)) = f;
      if (f < 0)
        break;
    }
  va_end (fp);
  return answer;
}


static int
rec_cmp_by_fields (void * va, void * vb, void * vdata)
{
  rel_record * a;
  rel_record * b;
  struct rel_nsort_spec * spec;
  int nth;

  a = (rel_record *)va;
  b = (rel_record *)vb;
  spec = (struct rel_nsort_spec *)vdata;

  for (nth = 0; spec->fields[nth] >= 0; ++nth)
    {
      t_dstr da;
      t_dstr db;
      int raw_cmp;
      int cmp;

      da = (*a)._c[spec->fields[nth]]._d;
      db = (*b)._c[spec->fields[nth]]._d;

      raw_cmp = dstr_cmp (da, db);

      if (!raw_cmp && (da != db))
        {
          if ((unsigned long)da < ((unsigned long)db))
            {
              dstr_ref (da);
              dstr_unref (db);
              (*b)._c[spec->fields[nth]]._d = da;
            }
          else
            {
              dstr_ref (db);
              dstr_unref (da);
              (*a)._c[spec->fields[nth]]._d = db;
            }
        }

      if (spec->reverse_p)
        {
          cmp = -raw_cmp;
        }
      else
        {
          cmp = raw_cmp;
        }

      if (cmp)
        return cmp;
    }

  return 0;
}



void
rel_uniq_by_field (rel_table * table,
                   int field)
{
  int lim;
  int dest;
  int src;

  lim = rel_n_records (*table);
  for (dest = 0, src = 0; src < lim; ++dest, ++src)
    {
      table->_r[dest] = table->_r[src];

      while ((src < (lim - 1)) && !cmp_fields (*table, dest, field, *table, src + 1, field))
        {
          rel_free_record (table->_r[src + 1]);
          ++src;
        }
    }
  ar_setsize ((void **)&table->_r, 0, dest, sizeof (rel_record));
}



rel_table
rel_cut (rel_cut_spec fields, rel_table t)
{
  rel_table answer;
  int lim;
  int x;

  answer = rel_table_nil;

  lim = ar_size ((void *)t._r, 0, sizeof (rel_record));
  for (x = 0; x < lim; ++x)
    {
      rel_add_records (&answer, rel_cut_record (fields, t._r[x]), rel_record_null);
    }

  return answer;
}


rel_record
rel_cut_record (rel_cut_spec fields, rel_record r)
{
  rel_record answer;
  int x;

  answer = rel_record_nil;
  for (x = 0; fields[x] >= 0; ++x)
    {
      dstr_ref (r._c[fields[x]]._d);
      *(rel_field *)ar_push ((void **)&answer._c, 0, sizeof (t_uchar *)) = r._c[fields[x]];
    }
  return answer;
}


rel_cut_spec
rel_cut_list (int field, ...)
{
  va_list fp;
  rel_cut_spec answer;

  answer = 0;
  *(int *)ar_push ((void **)&answer, 0, sizeof (int)) = field;

  va_start (fp, field);
  while (1)
    {
      field = va_arg (fp, int);
      *(int *)ar_push ((void **)&answer, 0, sizeof (int)) = field;
      if (field < 0)
        break;
    }
  va_end (fp);
  return answer;
}


static int
cmp_fields (rel_table table1,
            ssize_t r1,
            ssize_t c1,
            rel_table table2,
            ssize_t r2,
            ssize_t c2)
{
  t_dstr d1;
  t_dstr d2;
  int cmp;

  d1 = table1._r[r1]._c[c1]._d;
  d2 = table2._r[r2]._c[c2]._d;

  cmp = dstr_cmp (d1, d2);

  if (!cmp && (d1 != d2))
    {
      if ((unsigned long)d1 < (unsigned long)d2)
        {
          dstr_ref (d1);
          dstr_unref (d2);
          table2._r[r2]._c[c2]._d = d1;
        }
      else
        {
          dstr_ref (d2);
          dstr_unref (d1);
          table1._r[r1]._c[c1]._d = d2;
        }
    }

  return cmp;
}


rel_table
rel_join (int absence_table,
          struct rel_join_output_spec * output,
          int table1_field,
          int table2_field,
          rel_table table1,
          rel_table table2)
{
  int f1_len;
  int f2_len;
  int f1_pos;
  int f2_pos;
  int n_output_fields;
  rel_table answer;

  /* How curious that such a simple loop can do so many useful things.
   */

  answer = rel_table_nil;

  f1_len = rel_n_records (table1);
  f2_len = rel_n_records (table2);

  for (n_output_fields = 0; output[n_output_fields].table != -1; ++n_output_fields)
    ;

  f1_pos = 0;
  f2_pos = 0;

  while ((f1_pos < f1_len) || (f2_pos < f2_len))
    {
      int cmp;
      int want_output;

      if (f2_pos == f2_len)
        cmp = -1;
      else if (f1_pos == f1_len)
        cmp = 1;
      else
        cmp = cmp_fields (table1, f1_pos, table1_field, table2, f2_pos, table2_field);

     if (absence_table < 0)
       want_output = !cmp;
      else if (absence_table == 1)
        want_output = (cmp < 0);
      else
        want_output = (cmp > 0);

      if (want_output)
        {
          rel_record r;
          rel_record f1_rec;
          rel_record f2_rec;
          int x;

          r = rel_record_nil;
          f1_rec = ((f1_pos < f1_len) ? table1._r[f1_pos] : r);
          f2_rec = ((f2_pos < f2_len) ? table2._r[f2_pos] : r);
          for (x = 0; x < n_output_fields; ++x)
            {
              rel_field field;

              field = (((output[x].table == 1)
                        ? f1_rec
                        : f2_rec)
                       ._c[output[x].field]);

              dstr_ref (field._d);
              *(rel_field *)ar_push ((void **)&r._c, 0, sizeof (char *)) = field;
            }
          *(rel_record *)ar_push ((void **)&answer._r, 0, sizeof (rel_record)) = r;
        }

      if ((f1_pos < f1_len) && (cmp <= 0))
        ++f1_pos;

      if ((f2_pos < f2_len) && (cmp >= 0))
        ++f2_pos;
    }

  return answer;
}


struct rel_join_output_spec *
rel_join_output (int table,
                 int field, ...)
{
  va_list ap;
  struct rel_join_output_spec * answer;
  struct rel_join_output_spec * item;
  int x;

  static struct rel_join_output_spec ** cache = 0;

  answer = 0;

  for (x = 0; !answer && x < ar_size (cache, 0, sizeof (struct rel_join_output_spec *)); ++x)
    {
      item = cache[x];

      if (item->table != table || item->field != field)
        continue;

      va_start (ap, field);
      while (1)
        {
          int tmp_table;
          int tmp_field;

          ++item;
          tmp_table = va_arg (ap, int);
          if (tmp_table < 0)
            tmp_field = -1;
          else
            tmp_field = va_arg (ap, int);

          if (item->table != tmp_table || item->field != tmp_field)
            break;
          if (item->table == -1)
            {
              answer = cache[x];
              break;
            }
        }
      va_end (ap);
    }
    
  if (!answer)
    {
      item = (struct rel_join_output_spec *)ar_push ((void **)&answer, 0, sizeof (struct rel_join_output_spec));
      item->table = table;
      item->field = field;

      va_start (ap, field);
      while (1)
        {
          table = va_arg (ap, int);
          if (table < 0)
            break;
          field = va_arg (ap, int);

          item = (struct rel_join_output_spec *)ar_push ((void **)&answer, 0, sizeof (struct rel_join_output_spec));
          item->table = table;
          item->field = field;
        }
      va_end (ap);

      item = (struct rel_join_output_spec *)ar_push ((void **)&answer, 0, sizeof (struct rel_join_output_spec));
      item->table = -1;
      item->field = -1;

      *(struct rel_join_output_spec **)ar_push ((void **)&cache, 0, sizeof (struct rel_join_output_spec *)) = answer;
    }

  return answer;
}


rel_table
rel_read_table (int fd,
                int n_fields,
                const char * err_name,
                const char * err_src)
{
  rel_record rec;
  rel_table answer;

  answer = rel_table_nil;
  while (1)
    {
      rec = rel_read_record (fd, n_fields, err_name, err_src);
      if (!rec._c)
        break;
      *(rel_record *)ar_push ((void **)&answer._r, 0, sizeof (rel_record)) = rec;
    }
  return answer;
}


static rel_record
rel_read_record (int fd,
                 int n_fields,
                 const char * err_name,
                 const char * err_src)
{
  t_uchar * line;
  long len;
  t_uchar * pos;
  int f;
  rel_record answer;

  safe_next_line (&line, &len, fd);
  if (!line)
    return rel_record_nil;

  answer = rel_record_nil;
  ar_setsize ((void **)&answer._c, 0, n_fields, sizeof (rel_field));

  pos = line;
  for (f = 0; f < n_fields; ++f)
    {

      while (len && !char_is_space (*pos))
        {
          ++pos;
          --len;
        }

      if (!len || (pos == line))
        {
          if (err_name)
            {
              safe_printfmt (2, "%s: ill formated input\n", err_name);
              safe_printfmt (2, "   input source: %s\n", err_src);
              exit (2);
            }
        }

      answer._c[f]._d = dstr_save_n (0, line, pos - line);

      while (len && char_is_space (*pos))
        {
          ++pos;
          --len;
        }

      line = pos;
    }

  return answer;
}



void
rel_print_table (int fd, rel_table table)
{
  int recs;
  int r;

  recs = ar_size ((void *)table._r, 0, sizeof (rel_record));

  for (r = 0; r < recs; ++r)
    rel_print_record (fd, table._r[r]);
}


void
rel_print_pika_escape_iso8859_1_table (int fd, int escape_classes, rel_table table)
{
  int recs;
  int r;

  recs = ar_size ((void *)table._r, 0, sizeof (rel_record));

  for (r = 0; r < recs; ++r)
    rel_print_pika_escape_iso8859_1_record (fd, escape_classes, table._r[r]);
}


void
rel_print_table_sp (int fd, rel_table file)
{
  int recs;
  int r;

  recs = ar_size ((void *)file._r, 0, sizeof (rel_record));

  for (r = 0; r < recs; ++r)
    rel_print_record_sp (fd, file._r[r]);
}


void
rel_print_pika_escape_iso8859_1_table_sp (int fd, int escape_classes, rel_table file)
{
  int recs;
  int r;

  recs = ar_size ((void *)file._r, 0, sizeof (rel_record));

  for (r = 0; r < recs; ++r)
    rel_print_pika_escape_iso8859_1_record_sp (fd, escape_classes, file._r[r]);
}


static void
rel_print_record (int fd, rel_record rec)
{
  int fields;
  int f;

  fields = ar_size ((void *)rec._c, 0, sizeof (rel_field));

  for (f = 0; f < fields; ++f)
    {
      safe_printfmt (fd, "%s%s", (f ? "\t" : ""), dstr_data (0, rec._c[f]._d));
    }

  if (f)
    safe_printfmt (fd, "\n");
}


static void
rel_print_pika_escape_iso8859_1_record (int fd, int escape_classes, rel_record rec)
{
  int fields;
  int f;

  fields = ar_size ((void *)rec._c, 0, sizeof (rel_field));

  for (f = 0; f < fields; ++f)
    {
      t_uchar * item;

      item = pika_save_escape_iso8859_1 (0, 0, escape_classes, dstr_data (0, rec._c[f]._d));
      safe_printfmt (fd, "%s%s", (f ? "\t" : ""), item);
      lim_free (0, item);
    }

  if (f)
    safe_printfmt (fd, "\n");
}


static void
rel_print_record_sp (int fd, rel_record rec)
{
  int fields;
  int f;

  fields = ar_size ((void *)rec._c, 0, sizeof (rel_field));

  for (f = 0; f < fields; ++f)
    {
      safe_printfmt (fd, "%s%s", (f ? " " : ""), dstr_data (0, rec._c[f]._d));
    }

  if (f)
    safe_printfmt (fd, "\n");
}


static void
rel_print_pika_escape_iso8859_1_record_sp (int fd, int escape_classes, rel_record rec)
{
  int fields;
  int f;

  fields = ar_size ((void *)rec._c, 0, sizeof (rel_field));

  for (f = 0; f < fields; ++f)
    {
      t_uchar * item;

      item = pika_save_escape_iso8859_1 (0, 0, escape_classes, dstr_data (0, rec._c[f]._d));
      safe_printfmt (fd, "%s%s", (f ? " " : ""), item);
      lim_free (0, item);
    }

  if (f)
    safe_printfmt (fd, "\n");
}

rel_table 
rel_table_substract (rel_table t, rel_table t2)
{
  rel_table answer = rel_table_nil;
  int lim;
  int x;

  lim = rel_n_records (t);

  for (x = 0; x < lim; ++x)
    {
      if (!rel_table_contains(t2, t._r[x]))
          *(rel_record *)ar_push ((void **)&answer._r, 0, sizeof (rel_record)) = rel_copy_record (t._r[x]);
    }

  return answer;
}

int
rel_table_contains(rel_table t, rel_record  r)
{ /* assuming the table is one column only */
  int lim;
  int x;
  t_dstr d1;
  t_dstr d2;
  
  d2 = r._c[0]._d;
  
  lim = rel_n_records(t);
  
  for (x = 0; x < lim; x++)
    {
       d1 = t._r[x]._c[0]._d;
       if (dstr_cmp(d1, d2) == 0)
           return 1;

    }
  return 0;

}




/* tag: Tom Lord Mon May  5 12:50:00 2003 (relational.c)
 */
