/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : Tables.c                               */
/*                                                 */
/* Purpose: Table handling functions.              */
/*                                                 */
/* Author : T.Cheal, adapted by A.D.Hodgkinson     */
/*                                                 */
/* History: 18-Mar-97: Datestamp on code received  */
/*                     from T.Cheal.               */
/*          12-Apr-97: Tidied up, more comments,   */
/*                     and from here on continued  */
/*                     development of the code     */
/*                     content.                    */
/*          18-Jun-97: Moved several functions     */
/*                     that were in the            */
/*                     reformatter here, as this   */
/*                     is a more appropriate       */
/*                     location for them.          */
/***************************************************/

#include <stdlib.h>
#include <string.h>

#include "swis.h"

#include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */

#include "wimp.h"
#include "wimplib.h"
#include "event.h"

#include "toolbox.h"

#include "svcprint.h"
#include "Global.h"
#include "FromROSLib.h"
#include "MiscDefs.h"
#include "Utils.h"

#include "Memory.h"
#include "Reformat.h"
#include "TokenUtils.h"
#include "Toolbars.h"

#include "Tables.h"

/* Local definitions */

#define Tables_Hourglass_WidthingTableDelay 100
#define Tables_Hourglass_FreeingTablesDelay 25

#define CorrectRowSpan(D,p) ((D)->RowSpan > (p)->RowSpan ? (p)->RowSpan : (D)->RowSpan)
#define CorrectColSpan(D,p) ((D)->ColSpan > (p)->ColSpan ? (p)->ColSpan : (D)->ColSpan)

/* Statics */

static int width_table_leds = 1;

/*************************************************/
/* tables_count_table()                          */
/*                                               */
/* Works out the number of rows and columns in a */
/* given table, and writes back into the given   */
/* table_stream structure these values plus      */
/* pointers to two allocated blocks of memory    */
/* that can hold one integer for each row and    */
/* column.                                       */
/*                                               */
/* Parameters: Pointer to a browser_data struct, */
/*             which is the parent of the table; */
/*                                               */
/*             Pointer to a table_stream struct  */
/*             relevant to the table.            */
/*                                               */
/* Returns:    Writes to the given table_stream  */
/*             struct, so this must not be in a  */
/*             read only area. The ColSpan and   */
/*             RowSpan fields are filled in with */
/*             the number of columns and rows    */
/*             respectively, and the ColOffs and */
/*             RowOffs fields are filled with    */
/*             the pointers to the arrays of     */
/*             Cols and Rows ints, respectively. */
/*************************************************/

void tables_count_table(browser_data * b, table_stream * p)
{
  table_row      * R;
  table_headdata * D;
  int              I;
  int            * RowSpill;
  int              ColCount, RowCount;
  int              Cols,     Rows;

  /* Avoid compiler warning - b may be used in later revisions of this code */

  b = b;

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("\ntables_count_table entered: 0x%x\n",(int) P);
  #endif

  RowCount = 0;
  R        = p->List;

  /* Do an initial count of rows/columns just by following the */
  /* structures. This isn't sufficient in itself as rowspan or */
  /* colspan parameters can make the table larger than it      */
  /* initally seems.                                           */

  while (R)
  {
    RowCount ++;

    R = R->Next;
  }

  /* The RowSpill array is used to hold the number of columns */
  /* defined in other rows, when a cell 'spills' onto those   */
  /* other rows by having a rowspan value specified for it.   */
  /*                                                          */
  /* When scanning rows that a cell above has spilled into,   */
  /* the table structures themselves will skip over that      */
  /* column - as a cell above that row has already defined    */
  /* contents here. To be able to work out how many actual    */
  /* columns there are, then, need to fill this array with    */
  /* the number of columns any such cells span.               */
  /*                                                          */
  /* If not clear already, hopefully it'll become more so in  */
  /* the code later which actually deals with the array.      */

  RowSpill = (int *) memory_alloc_and_set(RowCount * (sizeof(int)), 0);

  /* If allocation fails, report the error and jump out to */
  /* polling - can't possibly continue here.               */

  if (!RowSpill) show_error_cont(make_no_table_memory_error(1));

  /* Now restart the process starting the */
  /* column count as the total of all row */
  /* spillage to date.                    */

  Rows = 0;
  Cols = 0;
  R    = p->List;

  /* Move through all rows */

  while (R)
  {
    #ifdef TRACE
      if (tl & (1u<<20)) Printf("tables_count_table row: 0x%x\n",(int) R);
    #endif

    ColCount = RowSpill[Rows];
    D        = R->List;

    /* Move through all columns in the current row */

    while (D)
    {
      #ifdef TRACE
        if (tl & (1u<<20)) Printf("tables_count_table headdata: 0x%x\n",(int) D);
      #endif

      switch (D->Tag)
      {
        /* Items which if present, define a column */

        case TagTableData:
        case TagTableHead:
        {
          if (D->ColSpan) ColCount += D->ColSpan;
          else            ColCount ++;

          /* If this column is meant to span several rows, */
          /* fill in the RowSpill arrays for all the rows  */
          /* it spans (except the current one) with the    */
          /* number of columns this tag defines. So when   */
          /* we get to those rows, they'll already have    */
          /* some columns counted in from previous columns */
          /* which had a rowspan specified.                */

          if (D->RowSpan > 1)
          {
            int index;

            for (I = 1; I < CorrectRowSpan(D,p); I++)
            {
              index = Rows + I;

              if (index < RowCount) /* Stupidity check */
              {
                if (D->ColSpan) RowSpill[index] += CorrectColSpan(D,p);
                else            RowSpill[index] ++;
              }
            }
          }
          break;
        }
      }

      D = D->Next;
    }

    /* Keep Cols up to date as the highest column count */
    /* encountered so far, and move to the next row.    */

    if (Cols < ColCount) Cols = ColCount;

    Rows ++;

    R = R->Next;
  }

  /* Free the temporary rowspill array */

  free(RowSpill);

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("Rows, Cols: %d, %d\n", Rows, Cols);
  #endif

  /* Fill in the table_stream structure with the number of rows */
  /* and columns, and pointers to two allocated blocks of Rows  */
  /* and Cols integers.                                         */
  /*                                                            */
  /* Note that if a table cell specifies colspans or rowspans   */
  /* that extend into rows or columns that would otherwise not  */
  /* be defined anywhere, these extra rows/cols will **NOT** be */
  /* included in the count here. This is why loops dealing with */
  /* such values *must* be very careful not to run off the end  */
  /* of arrays and suchlike!                                    */

  p->ColSpan = Cols;
  p->RowSpan = Rows;

  /* May be reformatting the table, in which case there will already */
  /* be arrays attached to p.                                        */

  if (p->ColOffs) HtmlFree(p->ColOffs);
  if (p->RowOffs) HtmlFree(p->RowOffs);

  /* Now allocate the array, in the context of the HStream list.   */
  /* When the list is eventually discarded this will automatically */
  /* be discard too.                                               */

  p->ColOffs = HtmlMalloc(Cols * sizeof(int), p);

  if (!p->ColOffs) show_error_cont(make_no_table_memory_error(10));

  p->RowOffs = HtmlMalloc(Rows * sizeof(int), p);

  if (!p->RowOffs) show_error_cont(make_no_table_memory_error(11));

  return;
}

/*************************************************/
/* tables_position_table()                       */
/*                                               */
/* Fills in the RowOffs and ColOffs fields for   */
/* the given table and all cells in a given      */
/* table, taking account of multiple row and     */
/* column spanning. These are integers which,    */
/* for a given cell, give the number of rows and */
/* columns from the top left of the table        */
/* (starting at (0, 0)) that the cell is offset  */
/* by.                                           */
/*                                               */
/* Parameters: Pointer to a browser_data struct, */
/*             which is the parent of the table; */
/*                                               */
/*             Pointer to a table_stream struct  */
/*             relevant to the table.            */
/*************************************************/

void tables_position_table(browser_data * b, table_stream * p)
{
  table_row      * R;
  table_headdata * D;
  int              Cols, Rows, ColCount;
  int            * ColOffs;
  int            * RowOffs;
  unsigned char  * RowSpill;

  if (!p->ColSpan || !p->RowSpan) return;

  /* Avoid compiler warning - b may be used in later revisions of this code */

  b = b;

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("tables_position_table entered: 0x%x\n",(int) P);
  #endif

  Rows     = 0;
  RowSpill = (unsigned char *) memory_alloc_and_set(p->ColSpan * p->RowSpan, 0); /* Enough for all rows & cols */
  ColCount = p->ColSpan;
  ColOffs  = p->ColOffs;
  RowOffs  = p->RowOffs;

  /* If allocation fails, report the error and jump out to */
  /* polling - can't possibly continue here.               */

  if (!RowSpill) show_error_cont(make_no_table_memory_error(2));

  R = p->List;

  /* Loop through all rows */

  while (R && Rows < p->RowSpan)
  {
    Cols = 0;

    /* Look through the RowSpill array for this row, skipping */
    /* any columns which have already been done - marked by a */
    /* non-zero value in the array.                           */

    while (RowSpill[Rows * ColCount + Cols]) Cols++;

    /* Cols now points at a column that hasn't been positioned yet. */

    if (R->Width)
    {
      if (ColOffs[Cols] < R->Width) ColOffs[Cols] = R->Width;
    }

    D = R->List;

    /* Loop through all columns pointed to by this row */

    while (D && Cols < ColCount)
    {
      int C, R, I, J;

      switch (D->Tag)
      {
        case TagTableData:
        case TagTableHead:
        {
          #ifdef TRACE
            if (tl & (1u<<20)) Printf("tables_position_table: In scan, D (0x%x) RowOffs = %d, ColOffs = %d\n", D, Rows, Cols);
          #endif

          D->ColOffs = Cols;
          D->RowOffs = Rows; /* This is where this cell is, honest */

          if (D->ColSpan) C = CorrectColSpan(D, p);
          else            C = 1;

          if (D->RowSpan) R = CorrectRowSpan(D, p);
          else            R = 1;

          /* Loops contain a stupidity check... */

          for (I = Cols; I <= Cols + C - 1; I++)
          {
            for (J = Rows; J <= Rows + R - 1; J++)
            {
              /* For all columns and rows that this cell covers, mark that these */
              /* have been dealt with in the RowSpill array.                     */

              if (I < p->ColSpan && J < p->RowSpan) RowSpill[J * ColCount + I] = 0xff;
            }
          }
          Cols += C;

          /* Ensure that Cols is advanced appropriately to get past */
          /* anything marked as dealt with by the above code.       */

          while (RowSpill[Rows * ColCount + Cols]) Cols++;
        }
        break;
      }

      D = D->Next;
    }

    Rows ++;

    R = R->Next;
  }

  /* Free up the temporary array and exit */

  free(RowSpill);
}

/*************************************************/
/* tables_init_table()                           */
/*                                               */
/* For a new, uninitialised array of             */
/* reformat_cell structures, fills in any        */
/* initial information prior to sizing and       */
/* positioning the cells.                        */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the tables;           */
/*                                               */
/*             Pointer to the table_stream       */
/*             struct representing the table;    */
/*                                               */
/*             Pointer to the cell array.        */
/*************************************************/

void tables_init_table(browser_data * b, table_stream * table, reformat_cell * cellarray)
{
  int i, size;

  size = table->ColSpan * table->RowSpan;

  if (!size) return;

  memset(&cellarray[0], 0, sizeof(reformat_cell) * size);

  for (i = 0; i < size; i++)
  {
    cellarray[i].table = table;
  }

  return;
}

/*************************************************/
/* tables_width_table()                          */
/*                                               */
/* Works out the widths in millipoints for all   */
/* cells in a given table. This is called by the */
/* reformatter routines as they try to find the  */
/* width of various page elements. To find out   */
/* the width of, and finally format a particular */
/* cell, this function then itself calls the     */
/* reformatter. This can of course then call     */
/* back here for nested tables - i.e. this is a  */
/* recursive function, though it's not           */
/* immediately obvious as the callers and        */
/* callees are spread over a wide area of code.  */
/*                                               */
/* Because of the way tables define column       */
/* widths, for each cell three reformats take    */
/* place - minimum width find, maximum width     */
/* find, and final decided width.                */
/*                                               */
/* Coupled with all the juggling of those maxs   */
/* and mins - taking into account anything that  */
/* was specified in the HTML - this is something */
/* of a monster function and it is certainly not */
/* efficient at present. Hopefully this will     */
/* improve in a future interation of the tables  */
/* code...                                       */
/*                                               */
/* Parameters: 1 for a top level call, else 0 if */
/*             being called as part of a nested  */
/*             tables reformat session;          */
/*                                               */
/*             Pointer to a browser_data struct, */
/*             which is the parent of the table; */
/*                                               */
/*             Pointer to a table_stream struct  */
/*             relevant to the table;            */
/*                                               */
/*             The available width for the table */
/*             in millipoints;                   */
/*                                               */
/*             Pointer to an array of points to  */
/*             reformat_cell structures which    */
/*             will hold the final table layout. */
/*                                               */
/* Returns:    The table width, in millipoints.  */
/*************************************************/

int tables_width_table(int toplevel, browser_data * b, table_stream * p, int available_width, reformat_cell * cellarray)
{
  table_row      * R;
  table_headdata * D;
  int              I; // S, T; // For code commented out below
  int            * ColOffs;

  int              cellmax  = p->ColSpan * p->RowSpan;
  int              rowcount = 0;

  int              real_width,      new_width;
  int              min_table_width, max_table_width;
  int              remain_min,      remain_max;

  int            * mins,       * maxs;
  int            * overmins,   * overmaxs;

  int              have_colspans = 0;
  int              have_percents = 0;

  int              padding;

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("tables_width_table entered: 0x%x %d (%d OS) 0x%x\n", (int) P, available_width, available_width / 400, cellarray);
  #endif

  if (cellmax) padding = tables_cell_padding(b, &cellarray[0]);
  else return 1600;

  /* Deal with specific table widths */

  if (TABLE_HAS_WIDTH(p))
  {
    int width, dx;

    switch (TABLE_WIDTH_UNITS(p))
    {
      default:
      case UNITS_PIXELS:
      {
        convert_to_points(wimpt_dx(), &dx);
        width = TABLE_WIDTH(p) * dx;
      }
      break;

      case UNITS_PERCENT:
      {
        width = (TABLE_WIDTH(p) * available_width) / 100;
      }
      break;
    }

    available_width = width;
  }

  /* Allocate two arrays of ints to hold the minimum and maximum */
  /* widths of the table's cells.                                */

  maxs = (int *) memory_alloc_and_set(sizeof(int) * cellmax, 0);

  if (!maxs)
  {
    show_error_ret(make_no_table_memory_error(3));

    return 0;
  }

  mins = (int *) memory_alloc_and_set(sizeof(int) * cellmax, 0);
  if (!mins)
  {
    free(maxs);

    show_error_ret(make_no_table_memory_error(4));

    return 0;
  }

  /* Allocate arrays to hold min and max widths of columns */
  /* worked out overall.                                   */

  overmaxs = (int *) memory_alloc_and_set(sizeof(int) * p->ColSpan, 0);
  if (!overmaxs)
  {
    free(mins);
    free(maxs);

    show_error_ret(make_no_table_memory_error(5));

    return 0;
  }

  overmins = (int *) memory_alloc_and_set(sizeof(int) * p->ColSpan, 0);
  if (!overmins)
  {
    free(overmaxs);
    free(mins);
    free(maxs);

    show_error_ret(make_no_table_memory_error(6));

    return 0;
  }

  /* This can get s l o w... */

  _swix(Hourglass_Start,
        _IN(0),

        Tables_Hourglass_WidthingTableDelay);

  /* Treat the p->ColOffs field as an array of millipoint offsets */
  /* from the left of the table for each column.                  */

  ColOffs = p->ColOffs;

  /* First pass - find min and max widths of the columns. */

  R = p->List; /* p is a table_stream, so R now points to the first table_row */

  while (R && rowcount < p->RowSpan)
  {
    #ifdef TRACE
      if (tl & (1u<<20)) Printf("tables_width_table: Row struct 0x%x\n",(int) R);
    #endif

    /* Flick the LEDs around a bit to show progress. Not much point */
    /* trying to do %s because of nested tables.                    */

    _swix(Hourglass_LEDs,
          _INR(0,1),

          3,
          width_table_leds);

    width_table_leds ^= 3;

    /* R is a table_row, so D now points to a table_headdata */

    D = R->List;

    while (D && D->ColOffs < p->ColSpan)
    {
      #ifdef TRACE
        if (tl & (1u<<20)) Printf("tables_width_table: Head struct, tag 0x%x 0x%x\n",(int) D,(int) D->Tag);
      #endif

      switch(D->Tag)
      {
        case TagTableData:
        case TagTableHead:
        {
          int minw, maxw;
          int width      = 0;
          int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs;

          if (cellnumber < cellmax)
          {
            #ifdef TRACE
              if (tl & (1u<<20)) Printf("tables_width_table: Dealing with column tag 0x%x\n",(int) D->Tag);
            #endif

            /* Will need to do another scan for colspan cells */

            if (D->ColSpan > 1) have_colspans = 1;

            if (TD_HAS_WIDTH(D))
            {
              /* Deal with specific cell widths */

              switch (TD_WIDTH_UNITS(D))
              {
                default:
                case UNITS_PIXELS:
                {
                  int dx;

                  convert_to_points(wimpt_dx(), &dx); /* Millipoints per pixel in dx */
                  width = TD_WIDTH(D) * dx;
                }
                break;

                case UNITS_PERCENT:
                {
                  /* Need to deal with this after anything else, as % width specifiers */
                  /* mean 'n% of the space left after other cells have been widthed'.  */

                  have_percents = 1;
                }
                break;
              }

              maxs[cellnumber] = mins[cellnumber] = width;
            }

            /* Unfortunately, still need to find the minimum size. So bang */
            /* goes any possible speed up from knowing it in advance. If   */
            /* only web page designers could count...                      */

            reformat_find_cell_limits(toplevel,
                                      b,
                                      (HStream *) D->List,
                                      p,
                                      cellarray,
                                      D->RowOffs,
                                      D->ColOffs,
                                      &minw,
                                      &maxw);

            /* Take the largest - the values found from the reformatter, */
            /* or any values specified in the cell.                      */

            if (minw > mins[cellnumber]) mins[cellnumber] = minw;
            if (maxw > maxs[cellnumber]) maxs[cellnumber] = maxw;

            /* Deal with rowspans */

            if (D->RowSpan > 1)
            {
              int rsc, index;

              for (rsc = 1; rsc < CorrectRowSpan(D, p); rsc ++)
              {
                index = p->ColSpan * rsc + D->ColOffs;

                /* Sanity check */

                if (index >= cellmax) break;

                /* Write the values into the row */

                mins[index] = mins[cellnumber];
                maxs[index] = maxs[cellnumber];
              }
            }

          /* Closure of 'if (cellnumber < cellmax)' */
          }
        }
        break;

      /* Closure of 'switch (D->Tag)' */
      }

      D = D->Next;

    /* Closure of 'while (D && ...)' */
    }

    /* Carry on for the other rows. */

    rowcount ++;
    R = R->Next;

  /* Closure of 'while (R && ...)' */
  }

  #ifdef TRACE

    if (tl & (1u<<20))
    {
      int rowc,colc;

      Printf("\nTable size: mins\n");

      for(rowc=0;rowc<p->RowSpan;rowc++)
      {
        Printf("%d: ",rowc);

        for(colc=0;colc<p->ColSpan;colc++)
        {
          Printf("%d (%d) ",mins[rowc*p->ColSpan+colc],mins[rowc*p->ColSpan+colc]/400);
        }
        Printf("\n");
      }

      Printf("\nTable size: maxs\n");

      for(rowc=0;rowc<p->RowSpan;rowc++)
      {
        Printf("%d: ",rowc);

        for(colc=0;colc<p->ColSpan;colc++)
        {
          Printf("%d (%d) ",maxs[rowc*p->ColSpan+colc],maxs[rowc*p->ColSpan+colc]/400);
        }
        Printf("\n");
      }
      Printf("\n");
    }

  #endif

  /* Now have the mins and maxs arrays filled with the calculated */
  /* minimum and maximum size of some cells; those with no width  */
  /* specifier, those with a pixel size specifier, but not those  */
  /* with percentage specifiers yet.                              */
  /*                                                              */
  /* We now need to work out the overall greatest minimum and     */
  /* maximum width for each column. Any cells specifying a        */
  /* colspan > 1 are ignored in this, as they will have a large   */
  /* width (they span several cells, after all). We're only       */
  /* interested in single cells here.                             */
  /*                                                              */
  /* Must follow the linked list here to ensure that colspans are */
  /* known about and to ensure that spurious rowspan values, put  */
  /* into the array for the above percentage width calculation    */
  /* code to work, are not thought to represent real cell widths. */
  /*                                                              */
  /* The next step is to use this to find what is left of the     */
  /* available width for percentage width specifiers.             */

  R        = p->List;
  rowcount = 0;

  while (R && rowcount < p->RowSpan)
  {
    D = R->List;

    while (D && D->ColOffs < p->ColSpan)
    {
      switch(D->Tag)
      {
        case TagTableData:
        case TagTableHead:
        {
          if (D->ColSpan <= 1)
          {
            int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs;

            if (cellnumber < cellmax)
            {
              if (mins[cellnumber] > overmins[D->ColOffs]) overmins[D->ColOffs] = mins[cellnumber];
              if (maxs[cellnumber] > overmaxs[D->ColOffs]) overmaxs[D->ColOffs] = maxs[cellnumber];
            }
          }
        }
        break;

      /* Closure of 'switch (D->Tag)' */
      }

      D = D->Next;

    /* Closure of 'while (D && ...)' */
    }

    rowcount ++;
    R = R->Next;

  /* Closure of 'while (R && ...)' */
  }

  #ifdef TRACE

    if (tl & (1u<<20))
    {
      int colc;

      Printf("\nTable overall 1: mins\n");

      for(colc=0;colc<p->ColSpan;colc++)
      {
        Printf("%d (%d) ",overmins[colc],overmins[colc]/400);
      }
      Printf("\n\nTable overall 1: maxs\n");

      for(colc=0;colc<p->ColSpan;colc++)
      {
        Printf("%d (%d) ",overmaxs[colc],overmaxs[colc]/400);
      }
      Printf("\n\n");
    }

  #endif

  /* OK, now deal with percentage specifiers. First, work out the */
  /* remaining widths.                                            */

  remain_min = remain_max = available_width;

  for (I = 0; I < p->ColSpan; I++)
  {
    remain_min -= overmins[I];
    remain_max -= overmaxs[I];
  }

  if (remain_min < 0) remain_min = 0;
  if (remain_max < 0) remain_max = 0;

  /* Now go through the table structure working out the widths from this, */
  /* doing it all on a cell by cell level.                                */

  if (have_percents)
  {
    R        = p->List;
    rowcount = 0;

    while (R && rowcount < p->RowSpan)
    {
      D = R->List;

      while (D && D->ColOffs < p->ColSpan)
      {
        switch(D->Tag)
        {
          case TagTableData:
          case TagTableHead:
          {
            if (TD_HAS_WIDTH(D) && TD_WIDTH_UNITS(D) == UNITS_PERCENT)
            {
              int min_p_w;
              int max_p_w;
              int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs;

              if (cellnumber < cellmax)
              {
                min_p_w = (remain_min * TD_WIDTH(D)) / 100;
                max_p_w = (remain_max * TD_WIDTH(D)) / 100;

                #ifdef TRACE
                  if (tl & (1u<<20)) Printf("min_p_w, max_p_w: %d, %d\n", min_p_w, max_p_w);
                #endif

                /* Does this work out larger than values already obtained? */

                if (min_p_w > mins[cellnumber]) mins[cellnumber] = min_p_w;
                if (max_p_w > maxs[cellnumber]) maxs[cellnumber] = max_p_w;

                /* Deal with rowspans */

                if (D->RowSpan > 1)
                {
                  int rsc, index;

                  for (rsc = 1; rsc < CorrectRowSpan(D, p); rsc ++)
                  {
                    index = p->ColSpan * rsc + D->ColOffs;

                    /* Sanity check */

                    if (index >= cellmax) break;

                    /* Write the values into the row */

                    mins[index] = mins[cellnumber];
                    maxs[index] = maxs[cellnumber];
                  }
                }

              /* Closure of 'if (cellnumber < cellmax)' */
              }
            }
          }
          break;

        /* Closure of 'switch (D->Tag)' */
        }

        D = D->Next;

      /* Closure of 'while (D && ...)' */
      }

      rowcount ++;
      R = R->Next;

    /* Closure of 'while (R && ...)' */
    }

    /* Tedious but true - we now have to rescan the updated   */
    /* mins and maxs array to see if the percentage specified */
    /* cells will affect the overall column sizes in overmins */
    /* and overmaxs.                                          */

    R        = p->List;
    rowcount = 0;

    while (R && rowcount < p->RowSpan)
    {
      D = R->List;

      while (D && D->ColOffs < p->ColSpan)
      {
        switch(D->Tag)
        {
          case TagTableData:
          case TagTableHead:
          {
            if (D->ColSpan <= 1)
            {
              int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs;

              if (cellnumber < cellmax)
              {
                if (mins[cellnumber] > overmins[D->ColOffs]) overmins[D->ColOffs] = mins[cellnumber];
                if (maxs[cellnumber] > overmaxs[D->ColOffs]) overmaxs[D->ColOffs] = maxs[cellnumber];
              }
            }
          }
          break;

        /* Closure of 'switch (D->Tag)' */
        }

        D = D->Next;

      /* Closure of 'while (D && ...)' */
      }

      rowcount ++;
      R = R->Next;

    /* Closure of 'while (R && ...)' */
    }

    #ifdef TRACE

      if (tl & (1u<<20))
      {
        int colc;

        Printf("\nTable overall 2 (post-percents): mins\n");

        for(colc=0;colc<p->ColSpan;colc++)
        {
          Printf("%d (%d) ",overmins[colc],overmins[colc]/400);
        }
        Printf("\n\nTable overall 2 (post-percents): maxs\n");

        for(colc=0;colc<p->ColSpan;colc++)
        {
          Printf("%d (%d) ",overmaxs[colc],overmaxs[colc]/400);
        }
        Printf("\n\n");
      }

    #endif

  /* Closure of 'if (have_percents)' */
  }








// ***************** DON'T FORGET!
// That the old tables source
// has the better distribution code base on the
// RFC1942 table autowidth stuff.
// *****************





  /* Now deal with cells with colspans > 1. For each case, add up   */
  /* the width of the spanned cells from the array just calculated. */
  /* If these work out more than the width calculated for that      */
  /* cell, then nothing needs to be done; else distribute the       */
  /* difference over the spanned cells.                             */

  if (have_colspans)
  {
    R        = p->List;
    rowcount = 0;

    while (R && rowcount < p->RowSpan)
    {
      D = R->List;

      while (D && D->ColOffs < p->ColSpan)
      {
        switch(D->Tag)
        {
          case TagTableData:
          case TagTableHead:
          {
            if (D->ColSpan > 1)
            {
              int currcol;
              int cellnumber         = D->RowOffs * p->ColSpan + D->ColOffs;
              int spanned_total_mins = 0;
              int spanned_total_maxs = 0;

              if (cellnumber < cellmax)
              {
                /* Count the spanned columns */

                for (
                      currcol = D->ColOffs;
                      currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan;
                      currcol ++
                    )
                    spanned_total_mins += overmins[currcol],
                    spanned_total_maxs += overmaxs[currcol];

                #ifdef TRACE

                  if (tl & (1u<<20)) Printf("spanned total maxs, mins: %d, %d (%d, %d)\n",
                                           spanned_total_mins,
                                           spanned_total_maxs,
                                           spanned_total_mins/400,
                                           spanned_total_maxs/400);
                #endif

                /* If not big enough, distribute the difference over the columns */

                if (spanned_total_mins < mins[cellnumber])
                {
                  int min_diff  = mins[cellnumber] - spanned_total_mins;
                  int basic     = min_diff / D->ColSpan;
                  int remainder = min_diff - (basic * D->ColSpan);

                  /* Distribute for minimums, handling rounding carefully */

                  for (
                        currcol = D->ColOffs;
                        currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan;
                        currcol ++
                      )
                  {
                    if (remainder < 0)
                    {
                      overmins[currcol] += basic - 1;
                      remainder ++;
                    }
                    else if (remainder > 0)
                    {
                      overmins[currcol] += basic + 1;
                      remainder --;
                    }
                    else overmins[currcol] += basic;
                  }
                }

                /* Same for maximums */

                if (spanned_total_maxs < maxs[cellnumber])
                {
                  int max_diff  = maxs[cellnumber] - spanned_total_maxs;
                  int basic     = max_diff / D->ColSpan;
                  int remainder = max_diff - (basic * D->ColSpan);

                  for (
                        currcol = D->ColOffs;
                        currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan;
                        currcol ++
                      )
                  {
                    if (remainder < 0)
                    {
                      overmaxs[currcol] += basic - 1;
                      remainder ++;
                    }
                    else if (remainder > 0)
                    {
                      overmaxs[currcol] += basic + 1;
                      remainder --;
                    }
                    else overmaxs[currcol] += basic;
                  }
                }

              /* Closure of 'if (cellnumber < cellmax)' */
              }
            }
          }
          break;

        /* Closure of 'switch (D->Tag)' */
        }

        D = D->Next;

      /* Closure of 'while (D && ...)' */
      }

      rowcount ++;
      R = R->Next;

    /* Closure of 'while (R && ...)' */
    }

  /* Closure of 'if (have_colspans)' */
  }

  /* RFC 1942-like autolayout algorithm - if the *minimum* widths exceed */
  /* the width we're trying to fit the table into, assign the min sizes  */
  /* anyway and (elsewhere) allow horizontal scrolling.                  */

  min_table_width = max_table_width = 0;

  for (I = 0; I < p->ColSpan; I++) min_table_width += overmins[I], max_table_width += overmaxs[I];

  if (min_table_width > available_width)
  {
    /* Assign minimum widths */

    #ifdef TRACE
      if (tl & (1u<<20)) Printf("\ntables_width_table: Forced to assign minimum widths:\n\n");
    #endif

    for (I = 0; I < p->ColSpan; I++)
    {
      ColOffs[I] = overmins[I];

      #ifdef TRACE
        if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, overmins[I] / 400);
      #endif
    }

    #ifdef TRACE
      if (tl & (1u<<20)) Printf("\n");
    #endif
  }

  /* Otherwise, do the maximum widths fit in? If so, assign the minimum */
  /* widths (and in future - not implemented yet - possibly linearly    */
  /* expand those widths out to fill the given space).                  */

  else if (max_table_width <= available_width)
  {
    /* Assign maximum widths */

    #ifdef TRACE
      if (tl & (1u<<20)) Printf("\ntables_width_table: Table fits, assigning maximum widths:\n\n");
    #endif

    for (I = 0; I < p->ColSpan; I++)
    {
      ColOffs[I] = overmaxs[I];

      #ifdef TRACE
        if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, overmaxs[I] / 400);
      #endif
    }

    #ifdef TRACE
      if (tl & (1u<<20)) Printf("\n");
    #endif
  }

  /* Otherwise the maximum width of the table is greater than the available */
  /* space, but the minimum width is less than it.                          */
  /*                                                                        */
  /* According to RFC 1942, the following widthing algorithm is best.       */

  else
  {
    float d, W, D; /* Must be floats as ints will overflow, hmph. */

    #ifdef TRACE
      if (tl & (1u<<20)) Printf("\ntables_width_table: Assigning variable column widths:\n\n");
    #endif

    W = (float) available_width - (float) min_table_width; /* Difference between minimum table width and available width */
    D = (float) max_table_width - (float) min_table_width; /* Difference between minimum and maximum table width */

    if (D > 0)
    {
      for (I = 0; I < p->ColSpan; I++)
      {
        d = (float) overmaxs[I] - (float) overmins[I]; /* Difference between minimum and maximum column width */

        /* The following makes columns with large differences between their minimum and maximum */
        /* widths wider than columns with small differences.                                    */

        ColOffs[I] = overmins[I] + (int) (d * W / D);

        #ifdef TRACE
          if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, ColOffs[I] / 400);
        #endif
      }
    }

    #ifdef TRACE

      else
      {
        if (tl & (1u<<20)) Printf("...can't, max_table_width = min_table_width\n");
      }

      if (tl & (1u<<20)) Printf("\n");

    #endif
  }

  /* Add up the widths */

  real_width = 0;

  for (I = 0; I < p->ColSpan; I++) real_width += ColOffs[I];

  /* Did the table specify a width to fit to? */

  if (TABLE_HAS_WIDTH(p))
  {
    /* If the width is less than that available, distribute */
    /* the extra width over the table to make it fit.       */

    if (real_width && real_width < available_width)
    {
      float W, add = 0;

      #ifdef TRACE
        if (tl & (1u<<20)) Printf("\ntables_width_table: Resizing all columns to fit space:\n\n");
      #endif

      W = (float) available_width - (float) real_width;

      for (I = 0; I < p->ColSpan; I++)
      {
        add = W * ((float) ColOffs[I] / (float) real_width);

        ColOffs[I] += (int) add;

        #ifdef TRACE
          if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, ColOffs[I] / 400);
        #endif
      }
    }
    #ifdef TRACE

      else
      {
        if (tl & (1u<<20)) Printf("...can't, real_width = 0\n");
      }

      if (tl & (1u<<20)) Printf("\n");

    #endif
  }

  /* We've finished with the temporary mins and maxs arrays */

  free(maxs);
  free(mins);
  free(overmaxs);
  free(overmins);

  /* Now we need to go back and reformat the cell contents to the known width */

  rowcount = 0;
  R        = p->List;

  while (R && rowcount < p->RowSpan)
  {
    D = R->List;

    while (D && D->ColOffs < p->ColSpan)
    {
      switch(D->Tag)
      {
        case TagTableData:
        case TagTableHead:
        {
          /* Use the tag's number of columns offset from the left  */
          /* of the table as an index into the array of millipoint */
          /* offsets.                                              */

          new_width = abs(ColOffs[D->ColOffs]);

          /* If the tag spans more than one column, add in the widths */
          /* of the extra columns it covers.                          */

          if (D->ColSpan > 1)
          {
            int index;

            for(I = 1; I < D->ColSpan; I++)
            {
              index = D->ColOffs + I;

              if (index < p->ColSpan) new_width += abs(ColOffs[index]) - padding;
            }
          }

          /* Reformat the cell, recursing for inner tables */

          real_width = tables_width_cell(toplevel,
                                         b,
                                         (HStream *) D->List,
                                         p,
                                         cellarray,
                                         new_width,
                                         D->RowOffs,
                                         D->ColOffs);

// Never should be run, but low overhead to check it
// and adds robustness; so keep this code?
//
//          /* If the returned width is greater than the cell width worked */
//          /* out above, distribute that extra width evenly through the   */
//          /* columns that the tag spans.                                 */
//
//          if (new_width < real_width)
//          {
//            T = real_width - new_width;
//
//            if (D->ColSpan) C = D->ColSpan;
//            else            C = 1;
//
//            T = T / C;
//
//            for (I = 0; I < C; I++)
//            {
//              // Should keep the lowest value too if S<0 somewhere else...
//
//              S = (ColOffs[D->ColOffs + I] >= 0) ? 1 : -1;
//
//              ColOffs[D->ColOffs + I] += S * T;
//            }
//          }
        }
        break;
      }

      D = D->Next;
    }

    rowcount ++;
    R = R->Next;
  }

  /* Finally, add up all the widths of the columns and return it */

  max_table_width = 0;

  for (I = 0; I < p->ColSpan; I++) max_table_width += abs(ColOffs[I]);

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("tables_width_table: Finished, returning %d (%d OS)\n", max_table_width, max_table_width / 400);
  #endif

  _swix(Hourglass_Off, 0);

  return max_table_width;
}

/*************************************************/
/* tables_width_cell()                           */
/*                                               */
/* Returns the actual width of a given table     */
/* cell after attempting to reformat it to a     */
/* given width.                                  */
/*                                               */
/* Parameters: 1 for a top level call, 0 if      */
/*             being called as part of a nested  */
/*             table parse;                      */
/*                                               */
/*             Pointer to a browser_data struct  */
/*             relevant to the table;            */
/*                                               */
/*             Pointer to the first HStream      */
/*             structure in the stream that the  */
/*             cell is to contain;               */
/*                                               */
/*             Pointer to the table_stream       */
/*             struct representing the table;    */
/*                                               */
/*             Pointer to the table's array of   */
/*             reformat_cell structures;         */
/*                                               */
/*             Allowed column width (millipts);  */
/*                                               */
/*             Row number of the cell;           */
/*                                               */
/*             Column number of the cell.        */
/*                                               */
/* Returns:    Actual width after formatting,    */
/*             in millipoints.                   */
/*************************************************/

int tables_width_cell(int toplevel, browser_data * b, HStream * streambase, table_stream * table,
                      reformat_cell * cellarray, int ColWidth, int Row, int Column)
{
  return reformat_format_cell(toplevel,
                              b,
                              streambase,
                              table,
                              cellarray,
                              ColWidth,
                              Row,
                              Column);
}

/*************************************************/
/* tables_height_table()                         */
/*                                               */
/* Works out the heights in millipoints for all  */
/* cells in a given table. This may be called    */
/* from recursively called functions, but does   */
/* not enter into recursion itself as the height */
/* information for particular cells is stored    */
/* when finding the width - there's no need to   */
/* call the reformatter again.                   */
/*                                               */
/* Parameters: 1 for a top level call, else 0 if */
/*             being called as part of a nested  */
/*             tables reformat session;          */
/*                                               */
/*             Pointer to a browser_data struct, */
/*             which is the parent of the table; */
/*                                               */
/*             Pointer to a table_stream struct  */
/*             relevant to the table;            */
/*                                               */
/*             Pointer to an array of points to  */
/*             reformat_cell structures which    */
/*             will hold the final table layout. */
/*                                               */
/* Returns:    Height of the whole table, in     */
/*             millipoints.                      */
/*************************************************/

int tables_height_table(int toplevel, browser_data * b, table_stream * p, reformat_cell * cellarray)
{
  int              ReturnHeight = 0, Height, NewWidth;
  int            * RowOffs;
  int            * ColOffs;
  int              Row;
  table_row      * R;
  table_headdata * D;
  int              I, C;
  int              cellcount = 0;
  int              cellmax   = p->ColSpan * p->RowSpan;
  int              padding;

  if (cellmax) padding = tables_cell_padding(b, &cellarray[0]) * 2;
  else return 1600;

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("tables_height_table entered: 0x%x 0x%x\n",(int) P,(int) cellarray);
  #endif

  /* To work out height, must know the width (so that the */
  /* reformatter knows what width to wrap to, etc.). So   */
  /* must only call this *after* tables_width_table.      */

  RowOffs = p->RowOffs;
  ColOffs = p->ColOffs;

  Row = 0;
  R   = p->List;

  while (R && cellcount < cellmax && Row < p->RowSpan)
  {
    D = R->List;

    while (D && cellcount < cellmax && Row < p->RowSpan)
    {
      switch (D->Tag)
      {
        case TagTableData:
        case TagTableHead:
        {
          /* Work out the width that this cell uses */

          if (D->ColOffs < p->ColSpan && D->RowOffs < p->RowSpan)
          {
            NewWidth = abs(ColOffs[D->ColOffs]);

            if (D->ColSpan > 1)
            {
              int index;

              for (I = 1; I < D->ColSpan; I++)
              {
                index = D->ColOffs + I;

                if (index < p->ColSpan) NewWidth += abs(ColOffs[index]);
              }
            }

            /* Find the required cell height (this is not   */
            /* a recursive call - the height is stored in   */
            /* the relevant place by tables_width_cell called from  */
            /* tables_width_table above, which is where the */
            /* recursion is handled).                       */

            Height = tables_height_cell(toplevel,
                                        b,
                                        (HStream *) D->List,
                                        p,
                                        cellarray,
                                        NewWidth,
                                        D->RowOffs,
                                        D->ColOffs);

            if (TD_HAS_HEIGHT(D) && TD_HEIGHT_UNITS(D) == UNITS_PIXELS) /* Can't see how a cell height in % could possibly be meaningful... */
            {
              int dy, hc;

              convert_to_points(wimpt_dy(), &dy);

              hc = TD_HEIGHT(D) * dy;

              if (hc > Height) Height = hc;
            }


            if (D->RowSpan) C = CorrectRowSpan(D, p);
            else            C = 1;

            /* Distribute the height over the rows this cell spans */

            Height = Height / C;

            for (I = 0; I < C; I++)
            {
              if (Height > RowOffs[Row + I]) RowOffs[Row + I] = Height;
            }
          }
        }
        break;
      }

      cellcount++;

      D = D->Next;
    }

    #ifdef TRACE
      if (tl & (1u<<20)) Printf("tables_height_table: Row %d, height %d (%d OS)\n", Row, RowOffs[Row], RowOffs[Row] / 400);
    #endif

    ReturnHeight += RowOffs[Row];

    Row ++;

    R=R->Next;
  }

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("tables_height_table: Finished, returning %d (%d OS)\n\n", ReturnHeight, ReturnHeight / 400);
  #endif

  return ReturnHeight;
}

/*************************************************/
/* tables_height_cell()                          */
/*                                               */
/* Returns the height of a cell widthed by a     */
/* previous call to tables_width_cell.           */
/*                                               */
/* Parameters: 1 for a top level call, 0 if      */
/*             being called as part of a nested  */
/*             table parse;                      */
/*                                               */
/*             Pointer to a browser_data struct  */
/*             relevant to the table;            */
/*                                               */
/*             Pointer to the first HStream      */
/*             structure in the stream that the  */
/*             cell is to contain;               */
/*                                               */
/*             Pointer to the table_stream       */
/*             struct representing the table;    */
/*                                               */
/*             Pointer to the table's array of   */
/*             reformat_cell structures;         */
/*                                               */
/*             Allowed column width (millipts -  */
/*             this is not used at present);     */
/*                                               */
/*             Row number of the cell;           */
/*                                               */
/*             Column number of the cell.        */
/*                                               */
/* Returns:    Cell height in millipoints.       */
/*************************************************/

int tables_height_cell(int toplevel, browser_data * b, HStream * streambase, table_stream * table,
                       reformat_cell * cellarray, int ColWidth, int Row, int Column)
{
  int             cellindex = Row * table->ColSpan + Column;
  reformat_cell * c;

  /* Can't do anything if the cell index is out of range */

  if (cellindex >= table->RowSpan * table->ColSpan) return 1600;
  else c = &cellarray[cellindex];

  /* OK, so not used for now, but may be - this is to avoid a compiler warning */

  b = b;

  /* a second pass is required because the width may have changed  */
  /* currently this is kludge to save throwing away all the memory */
  /* add it back when a throw-away routine exists                  */
  /* method is then... tables_width_table,ThrowAway,tables_height_table            */

  #ifdef TRACE
    if (tl & (1u<<20))
    {
      Printf("tables_height_cell: %p %p %d %d %d\n",streambase,table,ColWidth,Row,Column);
      Printf("tables_height_cell returning %d\n",c->height);
    }
  #endif

  return c->height;
}

/*************************************************/
/* tables_fix_table()                            */
/*                                               */
/* Having found the width and height of the      */
/* cells in a given table, and their row/column  */
/* offset from the top left, this fixes the      */
/* cell positions as x and y coordinates         */
/* in millipoints from the top left.             */
/*                                               */
/* Parameters: Pointer to a browser_data struct, */
/*             which is the parent of the table; */
/*                                               */
/*             Pointer to a table_stream struct  */
/*             relevant to the table;            */
/*                                               */
/*             Pointer to an array of points to  */
/*             reformat_cell structures which    */
/*             will hold the final table layout. */
/*************************************************/

void tables_fix_table(browser_data * b, table_stream * p, reformat_cell * cellarray)
{
  table_row      * R;
  table_headdata * D;
  int              I;
  int              Width, Height;
  int            * ColOffs;
  int            * RowOffs;
  int              CurX, CurY;
  int              cellcount = 0;
  int              cellmax   = p->ColSpan * p->RowSpan;

  /* Have ColOffs and RowOffs as the arrays holding millipoint */
  /* offsets from the top left of the table.                   */

  R       = p->List;
  ColOffs = p->ColOffs;
  RowOffs = p->RowOffs;

  while (R && cellcount < cellmax)
  {
    D = R->List;

    /* In here, D->RowOffs or D->ColOffs will be the number of */
    /* rows / columns from the top left that this cell is      */
    /* offset by.                                              */

    while (D && cellcount < cellmax)
    {
      if (D->ColOffs < p->ColSpan && D->RowOffs < p->RowSpan)
      {
        /* If the cell is offset horizontally, add up the widths of */
        /* all the columns before it into CurX.                     */

        CurX = 0;

        if (D->ColOffs)
        {
          for (I = 0; I < D->ColOffs; I++)
          {
            CurX += abs(ColOffs[I]);
          }
        }

        CurY = 0;

        /* Similarly, add up the row heights */

        if (D->RowOffs)
        {
          for(I = 0; I < D->RowOffs; I++)
          {
            CurY -= abs(RowOffs[I]);
          }
        }

        /* Set Width to the width of this cell, */
        /* taking account of column spanning.   */

        Width = abs(ColOffs[D->ColOffs]);

        if (D->ColSpan > 1)
        {
          for (I = 1; I < D->ColSpan; I++)
          {
            Width += abs(ColOffs[D->ColOffs + I]);
          }
        }

        /* Do the same for the height */

        Height = abs(RowOffs[D->RowOffs]);

        if (D->RowSpan > 1)
        {
          for (I = 1; I < CorrectRowSpan(D, p); I++)
          {
            Height += abs(RowOffs[D->RowOffs + I]);
          }
        }

        /* We now know the X and Y millipoint offsets of the cell and */
        /* its width and height (again, in millipoints). Now call     */
        /* tables_fix_cell to fix the positions of the cell inside    */
        /* the relevant data structure components.                    */

        switch(D->Tag)
        {
          case TagTableData: tables_fix_cell(b, (HStream *) D->List, p, cellarray, CurX, CurY, Width, Height, D->RowOffs, D->ColOffs); break;
          case TagTableHead: tables_fix_cell(b, (HStream *) D->List, p, cellarray, CurX, CurY, Width, Height, D->RowOffs, D->ColOffs); break;
        }
      }

      cellcount++;

      D = D->Next;
    }

    R = R->Next;
  }
}

/*************************************************/
/* tables_fix_cell()                             */
/*                                               */
/* Fixes the position of a given table cell as   */
/* an offset from the top left of the table in   */
/* millipoints.                                  */
/*                                               */
/* Parameters: 1 for a top level call, 0 if      */
/*             being called as part of a nested  */
/*             table parse;                      */
/*                                               */
/*             Pointer to the first HStream      */
/*             structure in the stream that the  */
/*             cell is to contain;               */
/*                                               */
/*             Pointer to the table_stream       */
/*             struct representing the table;    */
/*                                               */
/*             Pointer to the table's array of   */
/*             reformat_cell structures;         */
/*                                               */
/*             X offset from top left, in        */
/*             millipoints;                      */
/*                                               */
/*             Yoffset from top left, in         */
/*             millipoints;                      */
/*                                               */
/*             Total cell width (millipoints);   */
/*                                               */
/*             Total cell height (millipoints);  */
/*                                               */
/*             Row number of the cell;           */
/*                                               */
/*             Column number of the cell.        */
/*************************************************/

void tables_fix_cell(browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray,
                     int x, int y, int Width, int Height, int Row, int Column)
{
  int             cellindex = Row * table->ColSpan + Column;
  reformat_cell * c;

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("tables_fix_cell: %p %p %d %d\n",streambase,table,Row,Column);
  #endif

  /* Can't do anything if the cell index is out of range */

  if (cellindex >= table->RowSpan * table->ColSpan) return;
  else c = &cellarray[cellindex];

  /* OK, so not used for now, but may be - this is to avoid a compiler warning */

  b = b;

  //the 3200's in here should be replaced by border sizes in due course

  c->x = x + Column;// * 3200 + 1600;
  c->y = y - Row;// * 3200 - 1600;

  c->cellwidth  = Width;
  c->cellheight = Height;

  #ifdef TRACE
    if (tl & (1u<<20)) Printf("tables_fix_cell: Exitting\n");
  #endif
}

/*************************************************/
/* tables_free_memory()                          */
/*                                               */
/* Goes through the lines of a reformat_cell     */
/* structure, freeing all allocated memory       */
/* relating to any tables in that list; that is, */
/* the lines and chunks allocated in any assoc-  */
/* iated cells. Does not free the given cell's   */
/* line or chunk arrays, or any HTMLLib          */
/* allocated data such as the cell arrays        */
/* allocated through HTMLLib or the HStream      */
/* lists (HtmlStreamFree should be used for      */
/* those).                                       */
/*                                               */
/* Parameters: 1 for top level call, 0 for a     */
/*             recursive one (only used for the  */
/*             hourglass);                       */
/*                                               */
/*             Pointer to a browser_data struct  */
/*             relevant to the tables;           */
/*                                               */
/*             Pointer to a reformat_cell struct */
/*             holding the line array;           */
/*                                               */
/*             Line number in that line array    */
/*             from which freeing is to start.   */
/*************************************************/

void tables_free_memory(int toplevel, browser_data * b, reformat_cell * d, int line)
{
  int l, c;

  if (!d->nlines || line >= d->nlines) return;
  if (line < 0) line = 0;

  if (toplevel) _swix(Hourglass_Start,
                      _IN(0),

                      Tables_Hourglass_FreeingTablesDelay);

  /* Scan through the lines from last line to the requested one */
  /* (this is so that flex thrashage will hopefully be reduced, */
  /* as other cells in lines of higher numbers will on average  */
  /* appear in memory above cells in lines of lower numbers).   */

  for (l = d->nlines - 1; l >= line; l--)
  {
    /* Scan through all chunks on the line */

    for (c = d->ldata[l].chunks + d->ldata[l].n - 1; c >= d->ldata[l].chunks; c--)
    {
      /* Does the chunk represent a table? */

      if (d->cdata[c].t->tag == TABLE && ISBODY(d->cdata[c].t))
      {
        table_stream   * table     = (table_stream *) d->cdata[c].t;
        table_row      * row       = NULL;
        table_headdata * head      = NULL;
        reformat_cell  * cellarray = table->cells;
        reformat_cell  * cell;
        int              cellindex;
        int              cellcount = 0;
        int              cellmax   = table->ColSpan * table->RowSpan;

        if (cellarray)
        {
          row = table->List;

          while (row && cellcount < cellmax)
          {
            head = row->List;

            while (head && cellcount < cellmax)
            {
              switch (head->Tag)
              {
                case TagTableData:
                case TagTableHead:
                {
                  cellindex = head->RowOffs * table->ColSpan + head->ColOffs;

                  if (cellindex < cellmax)
                  {
                    cell = &cellarray[cellindex];

                    /* Recursive call to free any nested tables inside this cell */

                    tables_free_memory(0, b, cell, 0);

                    /* Free the line and chunk lists for the cell */

                    if (cell->ldata) flex_free((flex_ptr) &cell->ldata);
                    if (cell->cdata) flex_free((flex_ptr) &cell->cdata);
                    cell->nlines = 0;
                  }
                }
                break;

              /* Closure of 'switch (head->Tag)' */
              }

              cellcount++;

              head = head->Next;

            /* Closure of 'while (head && ...)' */
            }

            row = row->Next;

          /* Closure of 'while (row && ...)' */
          }

        /* Closure of 'if (cellarray)' */
        }

      /* Closure of 'if (d->cdata[c].t->tag == TABLE && ISBODY(d->cdata[c].t))' */
      }

    /* Closure of loop scanning this line's chunks */
    }

    if (toplevel) _swix(Hourglass_Percentage, _IN(0), (100 * (d->nlines - l - 1)) / d->nlines);

  /* Closure of loop scanning the cell's lines */
  }

  if (toplevel) _swix(Hourglass_Off, 0);
}

/*************************************************/
/* tables_cell_padding()                         */
/*                                               */
/* Works out the cell padding (internal top,     */
/* left, right and bottom margin) for a given    */
/* table cell, in millipoints.                   */
/*                                               */
/* Returns: Cell padding (internal margin), in   */
/*          millipoints.                         */
/*************************************************/

int tables_cell_padding(browser_data * b, reformat_cell * cell)
{
  int margin = TABLES_DEFAULT_CELLPADDING;

// This works, but the external stuff that calls it will start messing
// up for non-zero returns, since I didn't get the time to finish the
// implementation. **AGAIN**. To be continued (perhaps).
return 0;

  if (TABLE_HAS_CELLPADDING(cell->table)) convert_to_points(TABLE_CELLPADDING(cell->table) * wimpt_dx(), &margin);

  return margin;
}