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

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

#define MISC_C
#define I_BUFFLOOP_C
#define I_DISPLAY_C
#define I_GETMSG_C
#define I_GETTK_C
#define I_FINDS_C
#define I_FOLDING_C
#define I_KEYBOARD_C
#define I_LOOP_C
#define I_MAIN_C
#define I_MESSAGES_C
#define I_ORIEDT_C
#define I_PROMPT_C
#define I_SCREEN_C
#define I_SIGNALS_C
#define I_VIRTUAL_C

#include "origami.h"
#include <h/envvar_str.h>
#include <lib/ori_add_lib.h>
/*}}}  */

/*{{{  debug stuff*/
#ifdef DEBUGGING
  /*{{{  l_to_a*/
  private char *l_to_a(long x)
  {
#  define NBSIZE ((sizeof(size_t)<<2)+1)
    static char number[NBSIZE];
    char *s;
    boolean neg;

    if ((neg=(x<0))) x= -x;
    s=number+NBSIZE-1;
    *s=0;
    do
     { *(--s)=hex_digits[x%10]; }
    while ((x/=10)>0);
    if (neg) *(--s)='-';
    return(s);
  }
  /*}}}  */

  /*{{{  ocl debug*/
#  ifdef OCL_DEBUG
     /*{{{  variables*/
     FILE *o_dumb=0;
     char *o_name=0;
     FILE *o_keyf=0;
     char *o_key_file=0;
     private boolean ocl_show_stack=False;
     private int cur_stack=0;
     private boolean ocl_show_max_s=False;
     private int max_stack=0;
     private long ocl_slowdown=0;
     private boolean ocl_debug_scr=False;
     private int ocl_scr_active=0;
     private
#     include "viewrc/name.h"
     /*}}}  */

     /*{{{  ocl_key*/
     public void ocl_key(int x)
      { if (o_keyf)
         { fputc(x,o_keyf);
           fflush(o_keyf);
         }
      }
     /*}}}  */
     /*{{{  ocl_msg*/
     public void ocl_msg(char*s,int i)
     {
       if (o_dumb)
        { fprintf(o_dumb,"\t\t\t");
          fprintf(o_dumb,s,i);
          fprintf(o_dumb,"\n");
          fflush(o_dumb);
        }
     }
     /*}}}  */
     /*{{{  ocl_get_arg_dbg*/
     public void ocl_get_arg_dbg(TOKEN a,char *s)
     {
       if (o_dumb)
        { fprintf(o_dumb,"\t\t\targ %d %s\n",a,s);
          fflush(o_dumb);
        }
     }
     /*}}}  */
     /*{{{  ocl_debug*/
     public void ocl_debug(int stack,TOKEN tok)
     { if (stack!=cur_stack)
        /*{{{  store stack data, handle status line*/
        { if (ocl_show_stack || ocl_show_max_s) title_op(CHGTITLE);
          cur_stack=stack;
          if (max_stack<cur_stack) max_stack=cur_stack;
        }
        /*}}}  */
       if (o_dumb)
        /*{{{  dumb to file*/
        { static long count=0;
          extern TOKEN *macro_pos;

          fprintf(o_dumb,"%7ld%c%015ld=",count++,bd.m.file_changed_status?'*':' ',(long)macro_pos);
          while (stack>16) { fputc('>',o_dumb);stack-=16; }
          while (stack--) fputc('*',o_dumb);
          if (tok>=O_NOP && tok<=O_EXE_MACRO)
             fputs(dec[tok-O_NOP],o_dumb);
          else if (tok>O_EXE_MACRO)
             fprintf(o_dumb,"O_CALL_FIX %d",tok-O_EXE_MACRO);
          else if (tok<O_NOP && tok>=' ')
             fputc(tok,o_dumb);
          else
             fprintf(o_dumb,"%d",tok);
          fputc('\n',o_dumb);
          fflush(o_dumb);
        }
        /*}}}  */
       oflush;
       usleep(ocl_slowdown*1000);
     }
     /*}}}  */
     /*{{{  o_arg_handle*/
     private void o_arg_handle(char *a)
     {
       switch (*a++)
        { case 'f':
             o_name=a;
             break;
          case 's':
             ocl_show_stack=True;
             break;
          case 'S':
             ocl_show_max_s=True;
             break;
          case 't':
             ocl_slowdown=atoi(a);
             break;
          case 'd':
             ocl_debug_scr=True;
             break;
          case 'k':
             o_key_file=a;
             break;
          default:
             break;
        }
     }
     /*}}}  */
     /*{{{  o_info*/
     private boolean o_info(unsigned char* t)
     { if (ocl_debug_scr || ocl_show_stack || ocl_show_max_s)
        { ustrcat(t,(unsigned char*)"(");
          /*{{{  screen on/off*/
          if (ocl_debug_scr)
           { ustrcat(t,ocl_scr_active?(unsigned char*)"OFF":(unsigned char*)"ON");
             if (ocl_show_stack || ocl_show_max_s)
                ustrcat(t,one_space);
           }
          /*}}}  */
          /*{{{  stack*/
          if (ocl_show_stack)
           { ustrcat(t,(unsigned char*)"s");
             ustrcat(t,i_to_ua(cur_stack));
             if (ocl_show_stack || ocl_show_max_s)
                ustrcat(t,one_space);
           }
          if (ocl_show_max_s)
           { ustrcat(t,(unsigned char*)"S");
             ustrcat(t,i_to_ua(max_stack));
           }
          /*}}}  */
          ustrcat(t,(unsigned char*)")");
          return(True);
        }
       return(False);
     }
     /*}}}  */
#  else
#      define o_info(x) 0
#  endif
  /*}}}  */
  /*{{{  malloc debug*/
#  ifdef MALLOC_DEBUG
    /*{{{  variables*/
    private FILE *mf_dumb=0;
    private char *mf_name;

#    ifndef MALLOC_PAKET
#      define MALLOC_PAKET 0
#    endif
    private int m_paket=MALLOC_PAKET;
    private int m_lost_rate=0;
    private size_t m_limit=0;

    private size_t m_used=0;
    private size_t m_frag_lost=0;

    private boolean m_show=False;
    /*}}}  */

    /*{{{  mf_print*/
    private void mf_print(char*s)
    {
      if (mf_dumb) { fputs(s,mf_dumb);fflush(mf_dumb); }
    }
    /*}}}  */
    /*{{{  paket_malloc*/
    public void *paket_malloc(size_t n)
    { void *x;
      char report[256];
      static int no=0;

      mf_print("malloc-");
      mf_print((char*)i_to_ua(++no));
      mf_print(": ");
      /*{{{  maybe let paket grow*/
      if (m_paket) { n |= m_paket;n++; }
      /*}}}  */
      if (m_limit && m_used+n>m_limit)
       /*{{{  limit reached*/
       { strcpy(report,"limit reached, ");
         strcat(report,l_to_a((long)n));
         strcat(report," wanted\n");
         x=0;
       }
       /*}}}  */
      else if ((x=ORImalloc(n+sizeof(size_t))))
       /*{{{  malloc ok*/
       { m_used+=n;
         *((int*)x)=n;
         x=(void*)((char*)x+sizeof(size_t));
         strcpy(report,"got ");
         strcat(report,l_to_a((long)n));
         strcat(report," bytes ok (");
         strcat(report,l_to_a((long)m_used));
         strcat(report,")\n");
         title_op(CHGTITLE);
         *((char*)x)='\0';
       }
       /*}}}  */
      else
       /*{{{  no more memory*/
       { strcpy(report,"malloc ");
         strcat(report,l_to_a((long)n));
         strcat(report," wanted\n");
       }
       /*}}}  */

      mf_print(report);

      return(x);
    }
    /*}}}  */
    /*{{{  paket_free*/
    public void paket_free(void *p)
    { char report[256];
      static int no=0;

      mf_print("free---");
      mf_print((char*)i_to_ua(++no));
      mf_print(": ");
      if (!p)
       /*{{{  only report error*/
         strcpy(report,"free null!\n");
       /*}}}  */
      else
       /*{{{  free and report diff*/
       { size_t n;

         p=(void*)((char*)p-sizeof(size_t));
         n=*((int*)p);
         ORIfree(p);
         m_used-=n;
         m_frag_lost+=(n*m_lost_rate)/100;
         strcpy(report,"freed ");
         strcat(report,l_to_a((long)n));
         strcat(report," bytes (");
         strcat(report,l_to_a((long)m_used));
         strcat(report,")\n");
         title_op(CHGTITLE);
       }
       /*}}}  */

      mf_print(report);
    }
    /*}}}  */
    /*{{{  paket_realloc*/
    public void *paket_realloc(void *p,size_t n)
    { void *x;
      size_t cp_length;

      cp_length= *(((size_t*)p)-1);
      if (cp_length>n) cp_length=n;
      mf_print("realloc:\n");
      if ((x=paket_malloc(n)))
       { memcpy(x,p,cp_length);
         paket_free(p);
       }
      return(x);
    }
    /*}}}  */
    /*{{{  mf_arg_handle*/
    private void mf_arg_handle(char *a)
    {
      switch (*a++)
       { case 's':
            m_limit=atoi(a);
            break;
         case 'p':
          { int i;

            m_paket=atoi(a);
            for (i=0;i<m_paket;i=(i<<1)+1);
            m_paket=i;
            break;
          }
         case 'f':
            mf_name=a;
            break;
         case '%':
            m_lost_rate=atoi(a);
            if (m_lost_rate<0 || m_lost_rate>100)
               m_lost_rate=0;
            break;
         case '\0':
            m_show=True;
            break;
         default:
            break;
       }
    }
    /*}}}  */
    /*{{{  mf_info*/
    private boolean mf_info(unsigned char *t)
    {
      if (m_show)
       { ustrcat(t,(unsigned char*)"(m");
         ustrcat(t,(unsigned char*)l_to_a((long)m_used));
         if (m_limit)
          { ustrcat(t,(unsigned char*)"-");
            ustrcat(t,(unsigned char*)l_to_a((long)((m_limit-m_used-m_frag_lost)*100)/m_limit));
            ustrcat(t,(unsigned char*)"%");
          }
         ustrcat(t,(unsigned char*)")");
         return(True);
       }
      return(False);
    }
    /*}}}  */
#  else
#   define mf_info(x) 0
#  endif
  /*}}}  */
  /*{{{  vir debug*/
#  ifdef VIR_DEBUG
     public FILE *v_dumb=0;
     private char *v_name=0;
     private boolean v_display=False;
     private off_t v_off=0;

     /*{{{  vir_debug*/
     public void vir_debug(off_t off)
     { if (v_display)
        { if (off!=v_off) title_op(CHGTITLE);
          v_off=off;
        }
     }
     /*}}}  */
     /*{{{  vir_arg_handle*/
     private void vir_arg_handle(char *a)
     { switch (*a++)
        { case 'f':
             v_name=a;
             break;
          case '\0':
             v_display=True;
             break;
          default:
             break;
        }
     }
     /*}}}  */
     /*{{{  vir_info*/
     private boolean vir_info(unsigned char *t)
     { if (v_display)
        { ustrcat(t,(unsigned char*)"(N=");
          ustrcat(t,i_to_ua(vir_nodes));
          if (vir_nodes)
           { ustrcat(t,(unsigned char*)",");
             ustrcat(t,(unsigned char*)l_to_a((long)v_off/1024));
           }
          ustrcat(t,(unsigned char*)")");
          return(True);
        }
       return(False);
     }
     /*}}}  */
#  else
#    define vir_info(x) 0
#  endif
  /*}}}  */

  /*{{{  list of dumb files*/
  typedef struct { FILE *f;char *n; } D_F;
  private D_F d_f[]=
   {
#  ifdef OCL_DEBUG
       { 0,0 },
#  endif
#  ifdef VIR_DEBUG
       { 0,0 },
#  endif
#  ifdef MALLOC_DEBUG
       { 0,0 },
#  endif
    { 0,0 }
   };
  /*}}}  */
  /*{{{  open_dumb_file*/
  private FILE *open_dumb_file(char *name)
  { D_F *fn=d_f;

    if (!*name) name=DEBUG_FILE;
    while (fn->n)
       if (strcmp(fn->n,name))
          fn++;
       else
          break;
    if (!fn->n)
       fn->f=fopen(fn->n=name,"w");

    return(fn->f);
  }
  /*}}}  */

  /*{{{  title_debug*/
  public void title_debug(unsigned char *t)
  {
     if (mf_info(t) | o_info(t) | vir_info(t)) ustrcat(t,one_space);
  }
  /*}}}  */
  /*{{{  start_debug*/
  public void start_debug(void)
  {
#   ifdef MALLOC_DEBUG
       if (mf_name)
        /*{{{  open file and write limits*/
        { mf_dumb=open_dumb_file(mf_name);
          if (mf_dumb)
           { mf_print("using limit ");
             mf_print(l_to_a((long)m_limit));
             mf_print(", paketsize ");
             mf_print(l_to_a((long)(m_paket?(m_paket+1):0)));
             mf_print("\n");
             fflush(mf_dumb);
           }
        }
        /*}}}  */
#   endif
#   ifdef VIR_DEBUG
       if (v_name)
          v_dumb=open_dumb_file(v_name);
#   endif
#   ifdef OCL_DEBUG
       if (o_name)
          o_dumb=open_dumb_file(o_name);
       if (o_key_file)
          o_keyf=open_dumb_file(o_key_file);
#   endif
  }
  /*}}}  */
  /*{{{  end_debug*/
  public void end_debug(void)
  { D_F *fn=d_f;

    while (fn->f) fclose(fn++->f);
  }
  /*}}}  */
  /*{{{  arg_debug*/
  public void arg_debug(char *a)
  {
    switch (*a++)
     {
#      ifdef MALLOC_DEBUG
          case 'm':
             mf_arg_handle(a);
             break;
#      endif
#      ifdef OCL_DEBUG
          case 'o':
             o_arg_handle(a);
             break;
#      endif
#      ifdef VIR_DEBUG
          case 'v':
             vir_arg_handle(a);
             break;
#      endif
       default:
          break;
     }
  }
  /*}}}  */
#endif
/*}}}  */

/*{{{  ocl_screen_off*/
public void ocl_screen_off(void)
{
#ifdef OCL_DEBUG
  if (ocl_debug_scr)
   { ocl_scr_active++;
     ori_assert(bd.scr.cur_shift_w<=LINELEN,"shift-check");
     title_op(PRTTITLE);
   }
  else
#endif
   { if (!scr_off++) screen_off(); }
}
/*}}}  */
/*{{{  ocl_screen_on*/
public boolean ocl_screen_on(void)
{
#ifdef OCL_DEBUG
  if (ocl_debug_scr)
   { if (!ocl_scr_active) return(True);
     ocl_scr_active--;
     ori_assert(bd.scr.cur_shift_w<=LINELEN,"shift-check");
     title_op(PRTTITLE);

     return(False);
   }
  else
#endif
   { if (!scr_off) return(True);
     if (!--scr_off)
      { screen_on();
        check_time_event();
      }

     return(False);
   }
}
/*}}}  */
#ifndef OS_FILEPROMPT
  /*{{{  fileprompt*/
  public unsigned char *fileprompt(unsigned char * const n)
  {
    s_readprompt(n,get_msg(M_FILENAME),_POSIX_PATH_MAX,file_history);
    home_expand((char*)n);
    if (!*n && (bd.f.real_tail!=bd.f.real_head) && !aborted)
     { ustrcpy(n,get_data(bd.f.real_tail));
       if (*n) add_history(misc_history,n);
     }
    return(n);
  }
  /*}}}  */
#endif
/*{{{  environment-variables*/
public void put_vars(void)
{
# define STRLEN(x) (sizeof(x)-1)
  /*{{{  static vars, to hold the environment data*/
  static char envfile[_POSIX_PATH_MAX+STRLEN(ORIFILE)+2]=ORIFILE "=";
  static char envline[LINELEN+STRLEN(ORILINE)+2]=ORILINE "=";
  static char envposx[STRLEN(ORIXPOS)+12]=ORIXPOS "=";
  static char envword[LINELEN+STRLEN(ORIWORD)+2]=ORIWORD "=";
  static char envposy[STRLEN(ORIYPOS)+12]=ORIYPOS "=";
  static char envpid[STRLEN(ORIPID)+12]=ORIPID "=";
  /*}}}  */

  /*{{{  filename*/
  strcpy
   ( envfile+STRLEN(ORIFILE)+1,
     (char*)(bd.f.real_tail==bd.f.real_head
              ? empty_text
              : get_data(bd.f.real_tail))
   );
  putenv(envfile);
  /*}}}  */
  /*{{{  line,x,word*/
  { char *s,*w;
    int xoff;
#   define lb (envline+STRLEN(ORILINE)+1)

    /*{{{  line*/
    copyin((unsigned char*)lb,bd.f.current,True);
    putenv(envline);
    /*}}}  */
    /*{{{  positionx*/
    strcpy
     ( envposx+STRLEN(ORIXPOS)+1,
       (char*)i_to_ua
               (xoff=1+bd.f.enter_spaces+scr2txt
                                          ( bd.f.enter_spaces,
                                            bd.scr.cursor.w,
                                            (unsigned char*)lb
                                          )
               )
     );
    putenv(envposx);
    /*}}}  */
    /*{{{  word*/
    /*{{{  append the word*/
    /*{{{  s=&bd.f.current char in line*/
    xoff-=bd.f.enter_spaces+1;
    s=lb;
    while (*s && xoff--) s++;
    /*}}}  */
    /*{{{  maybe move to start of word*/
    if (isalnum(*(s))||(*(s)=='_'))
       while ((s!=lb)&&(isalnum(*(s-1))||(*(s-1)=='_'))) s--;
    /*}}}  */
    /*{{{  append the word*/
    w=envword+STRLEN(ORIWORD)+1;
    while (*s && (isalnum(*s)||*s=='_')) *w++=*s++;
    *w='\0';
    /*}}}  */
    /*}}}  */
    putenv(envword);
    /*}}}  */
  }
  /*}}}  */
  /*{{{  positiony*/
  strcpy(envposy+STRLEN(ORIYPOS)+1,(char*)i_to_ua(cur_line_no()));
  putenv(envposy);
  /*}}}  */
  /*{{{  pid*/
  strcpy(envpid+STRLEN(ORIPID)+1,(char*)i_to_ua(getpid()));
  putenv(envpid);
  /*}}}  */
}
/*}}}  */
