/* $Id: text-fmt.cc,v 1.13 1997/04/13 04:15:33 dps Exp $ */

/* Everything is declared as static to avoid namespace polution but */
/* is reachable externally via the exported table (and this it the  */
/* only way they get called).                                       */
#ifdef __GNUC__
#define alloca __builtin_alloca
#else
#if HAVE_ALLOCA_H
#include <alloca.h>
#else /* Do not have alloca.h */
#ifdef _AIX
#pragma alloca
#else /* not _AIX */
extern "C" char *alloca(int);
#endif /* _AIX */
#endif /* HAVE_ALLOCA_H */
#endif /* __GNUC__ */

#include <stdio.h>
#include <stdlib.h>
#include "interface.h"
#include "lib.h"
#include "text-table.h"

/* Local data */
struct text_data
{
   text_table *tabl;
   int col;
   int row;
   int margin;
   int l_indent;
};

/* Most of these values are thanks to catdoc by
* Victor B. Wagner <[email protected]> et al */
static const cmap char_map[]=
{
   { 0x1E, "-" },      // Unbreakable join
   { 0x1F, "" },       // Soft hypen
   { 0x7f, "" },       // Delete 127s
   { 0x85, "..." },    // Dots
   { 0x91, "`" },      // 91 = left quote
   { 0x92, "'" },      // 92 = right quote
   { 0x93, "\"" },     // 93 = opening double quote
   { 0x94, "\"" },     // 94 = closing double quote
   { 0x95, "o" },      // 95 = Bullet
   { 0x96, "-" },      // em-dash
   { 0x97, "-" },      // en-dash
   { 0x99, "(tm)" },   // Trademark
   { 0xA0, " " },      // Unbreakable space
   { 0xA3, "<=" },     // Less than or = came out as A3
   { 0xA9, "(c)" },    // Copyright
   { 0xAB, "<<" },     // Openning << quotes
   { 0xAE, "(R)" },    // Reserved sign
   { 0xB3, ">=" },     // Greater than or = came out as B3
   { 0xBB, ">>" },     // Closing >> quotes
};

/* Allocate local data */
static void *allocate_txt(void)
{
   struct text_data *tdata;

   tdata=new(struct text_data);
   tdata->tabl=NULL;
   tdata->margin=0;
   tdata->l_indent=0;
   return tdata;
}

static void txt_code(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                    void *d)
{
   d=d;
   switch(*(t->data.d))
   {
   case CH_PAGE:
       if (fmt->flags.new_pages)
           fputc('\014', out);
       break;

   default:
       break;
   }
}

/* Free local data */
static void free_txt(void *d)
{
   struct text_data *tdata;

   tdata=(struct text_data *) d;
   if (tdata->tabl!=NULL)
       delete(tdata->tabl);
}


/* list start increases margin */
static void inc_margin(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct text_data *tdata;
   t=t;
   fmt=fmt;
   out=out;

   tdata=(struct text_data *) d;
   tdata->margin+=4;
}


/* list start decreases margin */
static void dec_margin(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct text_data *tdata;
   t=t;
   fmt=fmt;
   out=out;

   tdata=(struct text_data *) d;
   tdata->margin-=4;
}


/* items are easy */
static void txt_item(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   tblock *ts;
   const char *s;
   int i, m;
   struct text_data *dp;

   fmt=fmt;                    // No need for fmt
   dp=(struct text_data *) d;
   ts=map_string(t->data.d, char_map);
   s=*ts;
   fputc('\n', out);
   m=dp->margin-strlen(s)-1;
   for (i=0; i<m; i++)
       fputc(' ', out);
   fputs(s, out);
   fputc(' ', out);
   dp->l_indent=dp->margin;
   delete(ts);
}


/* Paragraphs are easy */
static void fold_para(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   tblock *b, *ts;
   const char *s;
   char *nl;
   struct text_data *dp;
   int i;

   dp=(struct text_data *) d;
   if (dp->margin==0 || (nl=(char *) alloca(dp->margin+2))==NULL)
       nl="\n";
   else
   {
       *nl='\n';
       for (i=1; i<=dp->margin; i++)
           *(nl+i)=' ';
       *(nl+dp->margin+1)='\0';
   }
   ts=map_string(t->data.d, char_map);
   b=word_wrap((*ts), nl, nl, fmt->maxline-(dp->margin), 0);
   delete(ts);
   s=*b;
   for (i=dp->l_indent; i<dp->margin; i++)
       fputc(' ', out);
   fputs(s, out);
   fputs("\n\n", out);
   dp->l_indent=0;
   delete(b);
}

/* Start a table === allocate table and initialise */
static void alloc_tbl(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct text_data *tdata;

   out=out;
   fmt=fmt;
   tdata=(struct text_data *) d;
   tdata->col=0;
   tdata->row=0;
   tdata->tabl=new(text_table)(t->data.table.cols, t->data.table.rows);
}


/* End of a table==print the table */
static void format_tbl(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                      void *d)
{
   struct text_data *tdata;

   t=t;
   tdata=(struct text_data *) d;
   tdata->tabl->print_table(fmt->maxline, out); // Print table
   delete(tdata->tabl);
   tdata->tabl=NULL;
}

/* start row==set column to 0 */
static void start_row(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct text_data *tdata;

   out=out;
   fmt=fmt;
   t=t;
   tdata=(struct text_data *) d;
   tdata->col=0;
}

/* end row==add one to row */
static void inc_row(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   struct text_data *tdata;

   out=out;
   fmt=fmt;
   t=t;
   tdata=(struct text_data *) d;
   tdata->row++;
}


/* Start field === set field */
static void set_field(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct tblock *ts;
   struct text_data *tdata;

   out=out;
   fmt=fmt;
   ts=map_string(t->data.d, char_map);
   tdata=(struct text_data *) d;
   tdata->tabl->set(tdata->col, tdata->row, (*ts));
   delete(ts);
}

/* end field==add one to col */
static void inc_col(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   struct text_data *tdata;

   out=out;
   fmt=fmt;
   t=t;
   tdata=(struct text_data *) d;
   tdata->col++;
}

/* pointers to the functions that do the work */
docfmt txtfmt=
{
   { 0 },                          // No page breaks
   76,                             // Width
   "\n",                           // Use \n as line ends
   allocate_txt,                   // Allocate space
   free_txt,                       // Free text
   {
       { null_proc, null_proc },   // End and start of document---do nothing
       { fold_para, null_proc },   // Paragraph
       { alloc_tbl, format_tbl },  // Start/end table
       { set_field, inc_col },     // Start/end field
       { start_row, inc_row },     // Start/end row
       { null_proc, null_proc },   // Throw away embed messages
       { inc_margin, dec_margin }, // list start and end
       { txt_item, null_proc},     // Items
       { txt_code, null_proc},     // Code
       { null_proc, null_proc }    // Do not understanding anything else
   }
};