/*{{{  #includes*/
#ifdef CONFIG_H
#   include "config.h"
#endif

#include <sys/types.h>
#include <ctype.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <local/bool.h>

#define READFOLDS_C
#define I_DISPLAY_C
#define I_FILETYP_C
#define I_FOLDING_C
#define I_GETTK_C
#define I_GETMSG_C
#define I_KEYBOARD_C
#define I_LOOP_C
#define I_MAIN_C
#define I_MESSAGES_C
#define I_MISC_C
#define I_ORIEDT_C
#define I_PROMPT_C
#define I_SCREEN_C
#define I_SIGNALS_C
#define I_STRING_C
#define I_VIRTUAL_C

#include "origami.h"
#include "macros.h"
/*}}}  */

/*{{{  variables*/
private bool lang_def;
private int danf,dend;
private FILE *rf_tdsfile;
private element *rf_current, *rf_trailing;
private int rf_new_indent;
private unsigned char *rf_dsp_line;
/*}}}  */

/*{{{  insert_file*/
/*{{{  node handling*/
/*{{{  create_node*/
private void create_node(void)
{ element *pt;

  proc_new(&pt);
  set_data(pt,rf_dsp_line);
  if (!rf_trailing) pt->UU.U1.indent = rf_new_indent;
  join_links(rf_current, pt);
  rf_current = pt;
}
/*}}}  */
/*{{{  connect_fold*/
private void connect_fold(void)
{ private element *rf_S;

  if (rf_trailing == (element*)0) return;
  rf_S = rf_trailing;
  join_links(rf_current, rf_S->other_end);
  rf_current = rf_S->other_end;
  rf_current->other_end = rf_S;
  rf_trailing = rf_S->fold;
  rf_S->fold = rf_S->next;
  rf_S->other_end = rf_current;
  rf_S->foldline = rf_current->foldline;   /*start*/
  /* _fold or _filed */
  current_ind -= rf_S->UU.U1.fold_indent;
  rf_current->foldline = END_FOLD;
  rf_current = rf_S;
}
/*}}}  */
/*{{{  start_a_fold*/
private void start_a_fold(linetyp start_, int indent)
{
  set_data(rf_current,rf_dsp_line);
  rf_current->UU.U1.fold_indent = indent;
  current_ind += indent;
  rf_current->fold = rf_trailing;
  rf_trailing = rf_current;
  proc_new(&rf_current->other_end);
  rf_current->other_end->foldline = start_;
}
/*}}}  */
/*}}}  */
/*{{{  read_tds*/
/*{{{  read_stream*/
private void read_stream(unsigned char*s,int lg)
{ fread((char*)s,(size_t)1,(size_t)lg,rf_tdsfile);
  if (feof(rf_tdsfile)) exit_origami(r_tds_crash,get_msg(M_TDS_CRASH));
  s[lg]=0;
}
/*}}}  */
/*{{{  read_tag*/
private void read_tag(int *tag, int *value)
{ int x;

  *value = 0;
  do
   { x=getc(rf_tdsfile);
     if (x==EOF)
        if (lang_def)
         /*{{{  return -1,0 indicating the error*/
         { *tag= -1;*value=0;break; }
         /*}}}  */
        else
           exit_origami(r_tds_crash,get_msg(M_TDS_CRASH));
     *tag=(unsigned char)(x>>6);
     *value=((*value)<<6)+((unsigned char)x&63);
   }
  while (*tag == 3);
}
/*}}}  */
/*{{{  read_string*/
private void read_string(unsigned char *s)
{
  int  tag, value;

  read_tag(&tag, &value);
  if (tag != 0) { strcpy((char *)s,i_to_a(value));return; }
  read_stream(s,value);
}
/*}}}  */
/*{{{  read_a_list*/
private void read_fold(int,bool);

private void read_a_list(int indent)
{ int tag, value;

  do
   { read_tag(&tag, &value);
     switch (tag)
      {
        /*{{{  0 = record*/
        case 0:
         { read_stream(rf_dsp_line,value);
           create_node();
           break;
         }
        /*}}}  */
        /*{{{  2 = function*/
        case 2:
         { switch (value)
            {
              /*{{{  2 = startfold*/
              case 2: { read_fold(indent, FALSE);break; }
              /*}}}  */
              /*{{{  4 = startfiled*/
              case 4: { read_fold(indent, TRUE);break; }
              /*}}}  */
            }
           break;
         }
        /*}}}  */
      }
   }
  while (tag != 2 || value != 1);
}
/*}}}  */
/*{{{  read_fold*/
private void read_fold(int indent,bool filed)
{
  int tag, value, t, c, extra_indent;

  read_tag(&tag, &value);   /*startlist*/
  read_string(rf_dsp_line);
  read_tag(&tag, &t);
  read_tag(&tag, &c);
  read_tag(&tag, &extra_indent);
  read_tag(&tag, &value);   /*should be endlist*/
  create_node();
  if (filed)
   { start_a_fold(START_FILED, extra_indent);
     read_string(rf_dsp_line);
     set_data(rf_trailing->other_end,rf_dsp_line);
   }
  else
   { start_a_fold(START_FOLD, extra_indent);
     read_a_list(indent + extra_indent);
   }
  read_tag(&tag, &value);   /*endfold or endfiled*/
  connect_fold();
  rf_current->UU.U1.fold_type = t;
  rf_current->UU.U1.fold_contents = c;
}
/*}}}  */

private bool read_tds(void)
{
  int tag, value;

  if (is_dir(rf_tdsfile)) return(FALSE);
  lang_def=TRUE;
  read_tag(&tag, &value);
  lang_def=FALSE;
  if (tag == 2 && value == 0)
   { dialect = F_C_TDS;
     read_a_list(0);
     if (fgetc(rf_tdsfile)!=EOF) exit_origami(r_tds_crash,get_msg(M_TDS_CRASH));
     return(TRUE);
   }
  return(FALSE);
}
/*}}}  */
/*{{{  ascii helps*/
/*{{{  type allowed mark-extension*/
typedef enum
 { S_S_EX=0,
   AF_S_EX,
   FS_S_EX
 } m_ex;
/*}}}  */
/*{{{  find_dialect*/
private bool find_dialect(unsigned char *ss)
{ unsigned char s[LINELEN + 1];
  f_c_types i;

  while (*ss==' ') ss++;
  if (!*ss) return(TRUE);
  strcpy((char *)s,(char*)ss);
  trailing_spaces(s);
  for (i = F_C_NONE; i<F_C_TDS; i++)
   { if
      (   (strlen((char *)s) == strlen((char *)dialect_start[i]))
       && (strpos2(dialect_start[i],s) == 0)
      )
      { dialect=i;return(TRUE); }
   }
  return(FALSE);
}
/*}}}  */
/*{{{  test_mark*/
private int test_mark(unsigned char *str,m_ex ex,bool *dirty)
{ int i;
  int the_pos;
  char *s;

  the_pos=strpos2(rf_dsp_line,str)+1;
  if (!the_pos) return(0);
  if (lang_def)
   /*{{{  handle the dialect*/
   { unsigned char ch=rf_dsp_line[the_pos-1];
     bool result;

     rf_dsp_line[the_pos-1]='\0';
     result=find_dialect(rf_dsp_line);
     rf_dsp_line[the_pos-1]=ch;
     if (!result) { *dirty=TRUE;return(0); }
     danf=strlen((char *)dialect_start[dialect]);
     dend=strlen((char *)dialect_end[dialect]);
     lang_def=FALSE;
   }
   /*}}}  */
  if (the_pos<=danf) { *dirty=TRUE;return(0); }
  s=(char*)rf_dsp_line+the_pos-1;
  /*{{{  check start-comment*/
  if (danf)
   { if (strncmp(s-danf,(char*)dialect_start[dialect],danf))
      { *dirty=TRUE;return(0); }
   }
  else
   { unsigned char *t=rf_dsp_line;
     int x=the_pos-1;

     while (x && *t==' ') { x--;t++; }
     if (x) { *dirty=TRUE;return(0); }
   }
  /*}}}  */
  s+=tag_length-1;
  /*{{{  check, if mark is followed by correct 2 letters*/
  if (*s && ex!=S_S_EX)
   { if (*(s+1) && (*(s+1)!=' ')) { *dirty=TRUE;return(0); }
     switch (*s)
      { case 'a':
        case 'A':
           if (ex!=AF_S_EX) { *dirty=TRUE;return(0); }
           *s='A';
           break;
        case 'f':
        case 'F':
           if (ex==S_S_EX) { *dirty=TRUE;return(0); }
           *s='F';
        case ' ':
           break;
        default:
           *dirty=TRUE;
           return(0);
      }
   }
  /*}}}  */
  i=strlen(s);
  if ((i<dend)||(dend&&strncmp(s+i-dend,(char*)dialect_end[dialect],dend)))
   { *dirty=TRUE;return(0); }
  *(s+i-dend)='\0';
  rf_dsp_line=(unsigned char*)s;
  return(the_pos-danf);
}
/*}}}  */
/*{{{  tabbed_out*/
private bool tabbed_out(unsigned char *s)
{ int space,j,internal_tab= *tab_size;
  unsigned char STR1[20];
  unsigned char R[LINELEN+1];
  unsigned char *r;
  bool nok=FALSE;

  if (*s)
   /*{{{  convert tab and delete trailing spaces*/
   { j = enter_depth_spaces;
     space = LINELEN-enter_depth_spaces-strlen((char *)s);
     strcpy((char*)R,(char*)s);
     r=s;
     s=R;
     while (*s)
      { if (*s=='\t' && hard_tab!=-1)
         /*{{{  expand the tab*/
         {
           /*{{{  read the tab-width*/
           while (*tab_size <= 0) {
             STR1[0]='\0';
             while (!STR1[0])
              { readprompt(STR1, get_msg(M_TABSIZE), 19);aborted=FALSE; }
             internal_tab= *tab_size=atoi((char *)STR1);
           }
           /*}}}  */
           j++;
           *r++ = (hard_tab==internal_tab) ? '\t' : ' ';
           /*{{{  add missing spaces*/
           while (j%internal_tab)
            { if (!space)
               /*{{{  set tab_size to 1*/
               { internal_tab=1;
                 nok=TRUE;
               }
               /*}}}  */
              else
               /*{{{  insert a ' '*/
               { j++;
                 if (hard_tab!=internal_tab) *r++=' ';
                 space--;
               }
               /*}}}  */
            }
           /*}}}  */
         }
         /*}}}  */
        else
         /*{{{  copy the char*/
         { j++;
           *r++= *s;
         }
         /*}}}  */
        s++;
      }
     while (*(--r)==' ');
     *(r+1) ='\0';
   }
   /*}}}  */
  return(nok);
}
/*}}}  */
/*{{{  complain*/
private void complain(msgtyp x)
{ bell();
  bell_visible();
  message(get_msg(x),TRUE);
  sleep(1);
}
/*}}}  */
/*}}}  */

public void insert_file(element *file_ptr,element *prec_ptr,
                        element *next_ptr,bool remember_file,
                        bool *dir_read,
                        FILE *pipe)
{
  /*{{{  local variables*/
  int marker_pos;
  FILE *tstorefile;
  unsigned char rf_filename[_POSIX_PATH_MAX+1];
  unsigned char dsp_line[LINELEN + 3];
#  ifdef DIREDT
    bool reading_dir=FALSE;
    bool reset_nonfold=FALSE;
#  endif
  element node;
  /*}}}  */

  /*{{{  prepare some variables (pointers, indentation..))*/
  if (dir_read) *dir_read=FALSE;
  rf_filename[0]=0;
  rf_tdsfile = 0;
  node.next=0;
  rf_current= &node;
  rf_new_indent = insert_indent_of(next_ptr);
  rf_trailing = 0;
  rf_dsp_line = dsp_line;
  current_ind = 0;
  /*}}}  */
  /*{{{  get and check filename*/
  if (!pipe) {
    if (!file_ptr)
      /*{{{  use new filename*/
      if (filearg)
       /*{{{  use given one*/
       { strcpy((char *)rf_filename, (char *)filearg);
         filearg=0;
       }
       /*}}}  */
      else
       {
         /*{{{  maybe use pipename*/
#         ifdef PIPE_FILENAME
            if (start_pipe)
               strcpy((char*)rf_filename,PIPE_FILENAME);
            else
#         endif
         /*}}}  */
         /*{{{  else prompt or use default*/
#               ifdef DEFAULT_FILE
                  strcpy((char*)rf_filename,DEFAULT_FILE);
#               else
                  fileprompt(rf_filename);
#               endif
         /*}}}  */
       }
      /*}}}  */
    else
      /*{{{  use file-fold-stack name*/
      strcpy((char *)rf_filename,(char *)get_data(file_ptr));
      /*}}}  */
    if (*rf_filename == '\0' && remember_file)
      exit_origami(file_changed?r_file_nok:r_ok,get_msg(M_BYE));
    if (access((char *)rf_filename,F_OK) ==  0 &&
        access((char *)rf_filename,W_OK) == -1)
      read_only=TRUE;
  }
  title_op(read_only ? SET_VIEW : RESET_VIEW);
  /*{{{  open filehandle for tds, to check, if the file is in tds-format*/
  if (pipe==(FILE*)0)
  {
    if ((rf_tdsfile=fopen((char *)rf_filename,READ_BIN)))
      message(get_msg(F_LOADING,rf_filename),TRUE);
    else
      err_message(get_msg(M_CANTOPEN),rf_filename,TRUE);
  }
  else
    rf_tdsfile=0;
  /*}}}  */
  /*}}}  */
  /*{{{  read the file*/
  if (pipe || fold_in || (rf_tdsfile && !read_tds()))
   /*{{{  read ascii-file*/
   {
     /*{{{  vars for ascii read*/
     bool tab_c=FALSE;
     bool null_found=FALSE;
     bool indent_crash=FALSE;
     bool dirty_fold=FALSE;
     bool line_split=FALSE;
     int  cut_lg=0;
     int  f_depth=0;
     unsigned char cut_char=0;
     unsigned char fold_type_str[tag_length+tag_length-1];
     /*}}}  */

     /*{{{  init local vars*/
     strcpy((char *)fold_type_str,(char *)fold_open_str);
     strcat((char *)fold_type_str,(char *)fold_close_str);
     dialect = F_C_NONE;
     lang_def=TRUE;
     /*}}}  */
     /*{{{  open file tstorefile for ascii reading*/
     if (pipe==(FILE*)0) {
       if ((tstorefile = fopen((char *)rf_filename,READ))==(FILE*)0)
         err_message(get_msg(M_CANTOPEN),rf_filename,TRUE);
     } else
       tstorefile=pipe;
     /*}}}  */
     /*{{{  handle directory editing*/
     if (is_dir(tstorefile)) {
       fclose(tstorefile);
#       ifdef DIREDT
         if (rf_filename[0]) {
           char cmd[LINELEN];

           DIR_CMD(cmd,rf_filename);
           tstorefile=popen(cmd,"r");
           reading_dir=TRUE;
           if (fold_in) { fold_in=FALSE;reset_nonfold=TRUE; }
           if (dir_read) *dir_read=TRUE;
         } else
#       endif
           tstorefile=0;
     }
     /*}}}  */
     if (tstorefile)
      /*{{{  read the file*/
      { bool look_for_att=FALSE;

        if (!executing_macro) enable_abort();
        while (!aborted)
         {
           /*{{{  read the line*/
           rf_dsp_line=dsp_line;
           *rf_dsp_line=cut_char;
           if
            ( !(fgets((char *)rf_dsp_line+cut_lg,
                      LINELEN+2-enter_depth_spaces-rf_new_indent-cut_lg,
                      tstorefile))
            )
              if (cut_lg) *(rf_dsp_line+cut_lg)='\n'; else break;
           /*}}}  */
           /*{{{  check length and cut on '\0'*/
           { int i=LINELEN-enter_depth_spaces-rf_new_indent;
             unsigned char *x=rf_dsp_line;

             for (;;)
                if (*x=='\n')
                 /*{{{  line end, so no cut char and break*/
                 { *x='\0';cut_lg=0;break; }
                 /*}}}  */
                else if (i--==0)
                 /*{{{  no line end found, so cut char and break*/
                 { cut_lg=1;cut_char= *x;*x='\0';line_split=TRUE;break; }
                 /*}}}  */
                else if (*x)
                 /*{{{  skip the character*/
                 { x++; }
                 /*}}}  */
                else
                 /*{{{  null found, no cut and store null! and break*/
                 { null_found=TRUE;break; }
                 /*}}}  */
           }
           /*}}}  */
           /*{{{  handle tabs*/
           tab_c|=(hard_tab!=-1 && tabbed_out(rf_dsp_line));
           /*}}}  */
           if (!fold_in && lang_def)
            /*{{{  check if current line is a user-comment-description*/
            { marker_pos=strpos2(rf_dsp_line,fold_type_str);
              if (marker_pos >= 0)
               /*{{{  user defined comment type*/
               { unsigned char *s=rf_dsp_line;
                 unsigned char *d;
                 bool ok;

                 /*{{{  set dialect start*/
                 if (ok=((danf=marker_pos)<tag_length))
                  { d=dialect_start[F_C_USER];
                    while (marker_pos--) *d++ = *s++;
                    *d='\0';
                  }
                 /*}}}  */
                 if (ok)
                  /*{{{  set dialect end*/
                  { s+=(tag_length+tag_length-2);
                    if (ok=((dend=strlen((char*)s))<tag_length))
                       strcpy((char *)dialect_end[F_C_USER],(char *)s);
                  }
                  /*}}}  */
                 if (ok)
                  /*{{{  set this language and continue*/
                  { lang_def=FALSE;
                    dialect = F_C_USER;
                    continue;
                  }
                  /*}}}  */
               }
               /*}}}  */
            }
            /*}}}  */
           if (c_hash_front && rf_dsp_line[0]=='#')
            /*{{{  move # to linestart*/
            { unsigned char *s=rf_dsp_line;

              *s=' ';
              while (*(++s) == ' ') ;
              *(s-1)='#';
            }
            /*}}}  */
           /*{{{  cut current indentation*/
           { int i=current_ind;

             while (i && *rf_dsp_line==' ')
              { i--;rf_dsp_line++; }
             if (i && *rf_dsp_line) indent_crash=TRUE;
           }
           /*}}}  */
           /*{{{  maybe handle filename- or close-fold-lines*/
           if (!fold_in)
            { if
               (f_depth && test_mark(fold_close_str,S_S_EX,&dirty_fold)>0)
               /*{{{  close-fold*/
               { f_depth--;
                 connect_fold();
                 look_for_att=FALSE;
                 continue;
               }
               /*}}}  */
              else if
               (look_for_att && test_mark(fold_file_str,AF_S_EX,&dirty_fold)>0)
               /*{{{  name of filed or attributes*/
               { bool ff=(rf_dsp_line[0]=='F');

                 if (*(++rf_dsp_line)) rf_dsp_line++;
                 if (ff)
                  /*{{{  filed*/
                  { if (rf_trailing) set_data(rf_trailing->other_end,rf_dsp_line);
                    continue;
                  }
                  /*}}}  */
                 else
                  /*{{{  attibutes*/
                  { if (rf_trailing)
                     { unsigned char *s=rf_dsp_line;

                       /*{{{  set type*/
                       rf_trailing->UU.U1.fold_type=(unsigned char)atoi((char *)rf_dsp_line);
                       /*}}}  */
                       /*{{{  set contents*/
                       while (*s==' ') s++;
                       while (*s!=' ') s++;
                       rf_trailing->UU.U1.fold_contents=(unsigned char)atoi((char *)s);
                       /*}}}  */
                       /*{{{  set data*/
                       while (*s==' ') s++;
                       while (isdigit(*s)) s++;
                       set_data(rf_trailing->other_end,s);
                       /*}}}  */
                     }
                    continue;
                  }
                  /*}}}  */
               }
               /*}}}  */
            }
           /*}}}  */
           create_node();
           /*{{{  maybe handle start-fold-line*/
           if (!fold_in)
            { int the_pos=test_mark(fold_open_str,FS_S_EX,&dirty_fold);

              if (the_pos>0)
               /*{{{  start fold*/
               { bool ff=(rf_dsp_line[0]=='F');

                 lang_def=FALSE;
                 if (*rf_dsp_line && *(++rf_dsp_line)) rf_dsp_line++;
                 f_depth++;
                 if (ff)
                    start_a_fold(START_FILED, the_pos - 1);
                 else
                    start_a_fold(START_FOLD, the_pos - 1);
                 look_for_att=TRUE;
               }
               /*}}}  */
              else
                 look_for_att=FALSE;
            }
           /*}}}  */
         }
        if (!executing_macro) disable_abort();
      }
      /*}}}  */
     /*{{{  check some error-situations on parsed file*/
     if (f_depth)
      /*{{{  autoclose and complain*/
      {
        /*{{{  rf_dsp_line=dsp_line=dialect_start f_depth M_ACF_MSG dialect_end*/
        strcpy((char*)dsp_line,(char*)dialect_start[dialect]);
        strcat((char*)dsp_line,(char*)i_to_a(f_depth));
        strcat((char*)dsp_line,(char*)tspace+2);
        strcat((char*)dsp_line,(char*)get_msg(M_ACF_MSG));
        strcat((char*)dsp_line,(char*)dialect_end[dialect]);
        rf_dsp_line=dsp_line;
        /*}}}  */
        do connect_fold(); while (--f_depth);
        /*{{{  add errormessage and complain*/
        create_node();
        complain(M_ACF_MSG);
        /*}}}  */
      }
      /*}}}  */
     if (dirty_fold)   complain(M_DIRTY_FOLD);
     if (tab_c)        complain(M_TAB_CRASH);
     if (null_found)   complain(M_NULL_CRASH);
     if (indent_crash) complain(M_INDENT_CRASH);
     if (line_split)   complain(M_LONG_LINE);
     /*}}}  */
     /*{{{  mybe reset fold_in*/
#     ifdef DIREDT
       if (reset_nonfold) fold_in=TRUE;
#     endif
     /*}}}  */
     /*{{{  close the file*/
     if (   pipe == (FILE*)0
         && tstorefile
         &&
#            ifdef DIREDT
              (reading_dir?
               pclose(tstorefile):
#            endif
               fclose(tstorefile)
#            ifdef DIREDT
              )<0
#            endif
         )
        err_message(get_msg(M_CLOSE_FAILED),rf_filename,TRUE);
     /*}}}  */
   }
   /*}}}  */
  /*{{{  close tds-file*/
  if (rf_tdsfile && fclose(rf_tdsfile)<0)
     err_message(get_msg(M_CLOSE_FAILED),rf_filename,TRUE);
  /*}}}  */
  /*}}}  */
  /*{{{  link the read tex to the edited text*/
  if (node.next)
   { join_links(prec_ptr, node.next);
     join_links(rf_current, next_ptr);
   }
  /*}}}  */
  /*{{{  filename must maybe moved to internal data*/
  if (remember_file)
   { if (!file_ptr) set_data(prec_ptr,rf_filename);
     set_data(next_ptr,rf_filename);
   }
  /*}}}  */
}
/*}}}  */
