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

#include <sys/types.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#ifndef NO_GETOPT_H
#  include <getopt.h>
#endif
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <h/envvar_str.h>

#define MAIN_C
#define I_BUFFLOOP_C
#define I_DISPLAY_C
#define I_FOLDFILING_C
#define I_GETMSG_C
#define I_GETTK_C
#define I_INIT_C
#define I_KEYBOARD_C
#define I_LOOP_C
#define I_MESSAGES_C
#define I_ORIEDT_C
#define I_PROMPT_C
#define I_SCREEN_C
#define I_SHELL_C
#define I_SIGNALS_C
#define I_VIRTUAL_C

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

/*{{{  variables*/
public char kp_state='\0';
public char const *rc_name=0;
public unsigned char const *filearg;
public TOKEN sig_macro[HISTORY_SIZE+1];
public int sig_count=0;
public boolean auto_save=False;
public boolean interrupt_restore=False,
#           if defined(SIGWINCH) || defined(MGR)
               win_changed=False,
#           endif
            verbose=False,
            aborted;
public element null_element;
public boolean no_suspend=False;
public boolean no_pipe=False;
/*}}}  */

/*{{{  language data*/
#define LG_DAT(name,stxt,slg,etxt,elg) \
 { { stxt,etxt },{ slg,elg },(unsigned char*)name }
language_comment dialects[F_C_SIZE]=
 { LG_DAT("None",(unsigned char*)empty_text,0,(unsigned char*)empty_text,0),
   LANGDATA
   LG_DAT("User",bd.m.dialect.lg_u_s,0,bd.m.dialect.lg_u_e,0),
   LG_DAT("Inmos",(unsigned char*)"--",2,(unsigned char*)empty_text,0)
 };
/*}}}  */

/*{{{  main*/
/*{{{  ori_arg_parse*/
#define CMD_AL_LG (BIND_NAME_LEN+1)
/*{{{  get_rcopt*/
private char *get_rcopt(char *cmd,char *opts,char **val,boolean *set)
{
  int l;
  char *o;

  /*{{{  get length of name, maybe handle argument*/
  for (l=0;cmd[l];l++)
     if (cmd[l]=='=')
      { *set=True;
        *val=cmd+l+1;
        break;
      }
  /*}}}  */
  for (o=opts;*o;)
     if (!strncmp(o,cmd,l) && o[l]=='\0')
      { o=o+l+1;
        break;
      }
     else
      { while (*++o);
        while (*++o);
        o++;
      }

  return(o);
}
/*}}}  */
/*{{{  get_arg_mark*/
private boolean get_arg_mark(unsigned char*m,char **a)
{
  int i;

  for (i=FOLD_TAG_LENGTH;i;i--)
     if (!(*m++=(char)*(*a)++))
        return(True);
  return (*(m-=FOLD_TAG_LENGTH)==' ' || *m=='\t');
}
/*}}}  */

private RETURNS ori_arg_parse
 ( int argc,
   char *argv[],
   stackent * const ocl_args,
   char **mark_mode
 )
{
  char opts[NO_OCL_CMD_OPTS*2*CMD_AL_LG+2];
  char *oriarg;
  int env_oriargs;

  ori_name=get_prg_name(argv[0]);
  env_oriargs=0;
  if ((oriarg=getenv(ORIARG)))
   /*{{{  divide ORIARG in single options*/
   { for (;;oriarg++)
      { switch (*oriarg)
         { case ' ':
           case '\t':
              continue;
           default:
            { char *arg_buff;

              /* skipped leading spaces/tabs in ORIARG */
              if ((arg_buff=paket_malloc(strlen(oriarg)+1)))
               /*{{{  get new argc/argv, containing data from ORIARG*/
               { int n;
                 char **a;

                 strcpy(arg_buff,oriarg);
                 /*{{{  get number of additional opts*/
                 { char *s;

                   for (n=0,s=arg_buff;;)
                    { switch (*s)
                       { case ' ':
                         case '\t':
                            while (*++s==' ' || *s=='\t');
                         case'\0':
                            n++;
                            if (*s)
                               continue;
                            break;
                         default:
                            s++;
                            continue;
                       }
                      break;
                    }
                 }
                 /*}}}  */
                 if ((a=paket_malloc((argc+1+n)*sizeof(int*))))
                  /*{{{  new argc/argv*/
                  { char *s;

                    env_oriargs=n;
                    a[0]=argv[0];
                    memcpy(&(a[n+1]),&(argv[1]),argc*sizeof(char*));
                    argv=a;
                    argc+=n;
                    for (s=arg_buff,a++;n;n--,a++)
                     /*{{{  set pointer in argv, cut option at space/tab*/
                     { *a=s;
                       for (;;)
                        { switch(*s)
                           { default:
                                s++;
                                continue;
                             case ' ':
                             case '\t':
                                *s='\0';
                                while (*++s==' ' || *s=='\t');
                             case '\0':
                                break;
                           }
                          break;
                        }
                     }
                     /*}}}  */
                  }
                  /*}}}  */
                 else
                    goto arg_mem_full;
               }
               /*}}}  */
              else
                 goto arg_mem_full;
            }
           case '0':
              /* ORIARG contains only space and tabs! */
              break;
         }
        break;
      }
   }
   /*}}}  */
  /*{{{  options handling loop*/
  {
    /*{{{  variables*/
    int c;
    char const *nok;
    boolean arg_init;
    boolean rc_used;
    /*}}}  */

    for
     ( rc_used=arg_init=False,nok=0
     ;   !nok
      && (c=getopt(argc,argv,"a:d:ef:hik:l:m:nopt:vACDF:K:M:N:O:PR:S:T:VW:X:Z@:"))!=EOF
     ; env_oriargs--
     ) switch (c)
        {
          /*{{{  a delay,       alarmtime*/
          case 'a':
             alarm_time=atoi(optarg)*60;
             if (alarm_time)
              /*{{{  set auto-save and test time*/
              { auto_save=True;
                if (alarm_time<MIN_ALARMTIME*60 || alarm_time>MAX_ALARMTIME*60)
                   nok=(char*)get_msg
                               ( MSG_ARG_FORMAT,
                                 STR_F_ALARM,
                                 MIN_ALARMTIME,
                                 MAX_ALARMTIME
                               );
              }
              /*}}}  */
             else
              /*{{{  don't use autosave*/
              { auto_save=False;alarm_time=NORM_ALARMTIME*60; }
              /*}}}  */
             break;
          /*}}}  */
          /*{{{  d type,        displaytype*/
          case 'd':
             for (;;)
              { int code;

                switch ((code= *optarg++))
                 { case 't':
                      code= -atoi(optarg);
                      chg_dsp_type((code>0)?0:code);
                   case '\0':
                      break;
                   default:
                      chg_dsp_type(code);
                      continue;
                 }
                break;
              }
             break;
          /*}}}  */
          /*{{{  e,             echo disabled*/
          case 'e':
             echo_disabled=True;
             break;
          /*}}}  */
          /*{{{  f match,       match expression for diredit*/
          case 'f':
             add_history(match_history,(unsigned char*)optarg);
             break;
          /*}}}  */
          /*{{{  h,             help*/
          case 'h':
             aborted=True;
             break;
          /*}}}  */
          /*{{{  i,             inverse controls*/
          case 'i':
             chg_dsp_type('i');
             break;
          /*}}}  */
          /*{{{  k basename,    rc-basename*/
          case 'k':
             if (arg_init)
                nok=STR_M_NORCCHANGE;
             else
                rc_name=optarg;
             break;
          /*}}}  */
          /*{{{  l line,        first line to display*/
          case 'l':
             if
              (    startup_line
                || (startup_line=atoi(optarg))<=0
              )
                nok=STR_M_LINE;
             break;
          /*}}}  */
          /*{{{  m markname,    marktype*/
          case 'm':
             if (*mark_mode)
                nok=STR_M_MARK;
             else
                *mark_mode=optarg;
             break;
          /*}}}  */
          /*{{{  n,             no folding*/
          case 'n':
             init_buffer.e.fold_in=True;
             break;
          /*}}}  */
          /*{{{  o,             overwrite*/
          case 'o':
             init_buffer.m.overwrite=True;
             break;
          /*}}}  */
          /*{{{  p,             c-hash-mode*/
          case 'p':
             init_buffer.m.hash_shift=True;
             break;
          /*}}}  */
          /*{{{  t size,        tab-mode*/
          case 't':
           { int code;

             code= -atoi(optarg);
             chg_dsp_type((code>0)?0:code);
             break;
           }
          /*}}}  */
          /*{{{  v,             view-mode*/
          case 'v':
             init_buffer.m.read_only=init_buffer.m.user_view=True;
             break;
          /*}}}  */
          /*{{{  A,             open argument list*/
          case 'A':
             arg_list=True;
             break;
          /*}}}  */
          /*{{{  C,             copyright disabled*/
          case 'C':
#            ifdef SW_MSG
                copyright=False;
#            endif
             break;
          /*}}}  */
          /*{{{  D,             do_dump*/
          case 'D':
             do_dump=True;
             break;
          /*}}}  */
          /*{{{  F nomatch,     nomatch expression for diredit*/
          case 'F':
             add_history(nomatch_history,(unsigned char*)optarg);
             break;
          /*}}}  */
          /*{{{  K,             switch keypad*/
          case 'K':
             switch (kp_state= *optarg)
              { default:
                   nok=STR_M_KP;
                case 'A':
                case 'a':
                case 'N':
                case 'n':
                   break;
              }
             break;
          /*}}}  */
          /*{{{  M marks,       give foldmarks directly*/
          case 'M':
             if
              (    get_arg_mark(init_buffer.f.str.open_f,&optarg)
                || get_arg_mark(init_buffer.f.str.file_f,&optarg)
                || get_arg_mark(init_buffer.f.str.line_f,&optarg)
                || get_arg_mark(init_buffer.f.str.close_f,&optarg)
                || *optarg
              )
                nok=M_FM;
             break;
          /*}}}  */
          /*{{{  N buffsize,    tablesize*/
          case 'N':
#            ifdef VIRTUAL
                if ((vir_nodes=atoi(optarg))<0)
                   nok=STR_M_VN;
#            endif
             break;
          /*}}}  */
          /*{{{  O value,       set OCL-var*/
          case 'O':
             arg_init=True;
             if (*optarg)
              /*{{{  handle sequence of initializations*/
              { char *curr_opt;
                boolean set_rc_val;

                for (set_rc_val=False,curr_opt=optarg;;optarg=curr_opt)
                 {
                   /*{{{  vars*/
                   char *rc_val;
                   int off;
                   int v;
                   char c;
                   /*}}}  */

                   /*{{{  get command type, maybe expand alias*/
                   c= *curr_opt;
                   if (isalpha(c))
                    /*{{{  decode alias*/
                    { if
                       (    (!rc_used && (rc_used=True,rcfileparser(opts,(char*)0)))
                         || !(curr_opt=get_rcopt(curr_opt,opts,&rc_val,&set_rc_val))
                       )
                       { nok=M_INV_OCL;
                         break;
                       }
                      else
                         c=curr_opt[0];
                    }
                    /*}}}  */
                   /*}}}  */
                   /*{{{  get adress for setting*/
                   off=0;
                   if (c=='>')
                    /*{{{  use another member of field*/
                    { off=atoi(++curr_opt);
                      if (off<0 || off>=DIM_OCL_CMD_OPTS)
                       { nok=M_INV_OCL;
                         break;
                       }
                      while ((c= *curr_opt),isdigit(c))
                         curr_opt++;
                    }
                    /*}}}  */
                   /*}}}  */
                   if (c=='@')
                    { curr_opt++;
                      if (!set_rc_val)
                         nok=M_INV_OCL;
                      else
                         add_history
                          ( arg_history,
                            *rc_val
                             ? (unsigned char*)rc_val
                             : (unsigned char*)one_space
                          );
                    }
                   else
                    /*{{{  numerical option*/
                    {
                      /*{{{  get value for op*/
                      if (c=='-' || isdigit(c))
                         c='=';
                      else
                         curr_opt++;
                      if (*curr_opt=='?' && set_rc_val)
                       { v=atoi(rc_val);
                         curr_opt++;
                       }
                      else
                       { v=atoi(curr_opt);
                         while (isdigit(*curr_opt) || *curr_opt=='-')
                            curr_opt++;
                       }
                      /*}}}  */
                      /*{{{  handle op*/
                      switch (c)
                       { case '=': ocl_args[off].v =v;break;
                         case '|': ocl_args[off].v|=v;break;
                         case '&': ocl_args[off].v&=v;break;
                         case '^': ocl_args[off].v^=v;break;
                         case '+': ocl_args[off].v+=v;break;
                         case '<':
                            if (v>=0 && v<DIM_OCL_CMD_OPTS)
                             { ocl_args[off].v=ocl_args[v].v;
                               break;
                             }
                         default:  nok=M_INV_OCL;
                       }
                      /*}}}  */
                    }
                    /*}}}  */
                   if (nok || *curr_opt=='\0')
                      break;
                 }
              }
              /*}}}  */
             else
              /*{{{  init 0 with 0*/
                ocl_args[0].v=0;
              /*}}}  */
             /*{{{  if error, prepare detailed error message*/
             if (nok)
                nok=(char*)get_msg(MSG_ARG_FORMAT,nok,origami_name,optarg);
             /*}}}  */
             break;
          /*}}}  */
          /*{{{  P,             no open for /dev/tty*/
          case 'P':
             no_pipe=True;
             break;
          /*}}}  */
          /*{{{  R rcpath,      resource file path*/
          case 'R':
             ori_path=optarg;
             break;
          /*}}}  */
          /*{{{  S,             secure*/
          case 'S':
             secure=True;
             shell_name=optarg;
             break;
          /*}}}  */
          /*{{{  T title,       titlename*/
          case 'T':
#            ifdef WINDOW_TITLE_CHANGE
                 w_title=optarg;
#            endif
             break;
          /*}}}  */
          /*{{{  V,             verbose*/
          case 'V':
             verbose=True;
             break;
          /*}}}  */
          /*{{{  W,             statusline extension: window name*/
          case 'W':
             w_name=(unsigned char*)optarg;
             break;
          /*}}}  */
          /*{{{  X,             extensions*/
          case 'X':
#            ifdef OWN_ARG_HANDLE
                OWN_ARG_HANDLE
#            endif
             break;
          /*}}}  */
          /*{{{  Z,             no suspend*/
          case 'Z':
              no_suspend=True;
              break;
          /*}}}  */
          /*{{{  @,             debug arguments*/
          case '@':
             arg_debug(optarg);
             break;
          /*}}}  */
          /*{{{  else, error*/
          case '?':
          default:
             nok=(char*)empty_text;
             break;
          /*}}}  */
       }
    /*{{{  errorcases in args*/
    if (nok || aborted)
     { if (nok)
        { if (*nok)
           { if (env_oriargs>=0)
                eputs("origami ("ORIARG"):");
             else
                eputs("origami: ");
             eputs(nok);
             eputc('\n');
           }
          eputs(get_short_usage());
        }
       else
          eputs(get_long_usage());
       return(nok?r_arg_err:r_ok);
     }
    /*}}}  */
    if (!arg_init)
        ocl_args[0].v= -1;
  }
  /*}}}  */
  /*{{{  inputfiles*/
  { int c;

    if ((argc-optind)<=1)
       arg_list=False;
    for (c=optind,filearg=0;c!=argc;c++)
     /*{{{  expand ~ and set to filelist*/
     { unsigned char *f;

       f=(unsigned char*)(argv[c]);
       /*{{{  add to file-history*/
       add_history(file_history,f);
       /*}}}  */
       /*{{{  expand ~*/
       ustrcpy(print_buffer,f);
       home_expand((char*)print_buffer);
       /*}}}  */
       /*{{{  maybe copy to malloced space*/
       if (ustrcmp(print_buffer,f))
          if ((f=paket_malloc(ustrlen(print_buffer)+1)))
             ustrcpy(f,print_buffer);
          else
             goto arg_mem_full;
       else
          f=(unsigned char*)(argv[c]);
       /*}}}  */
       add_edit_file((char*)f,1,False);
       if (!filearg) filearg=f;
     }
     /*}}}  */
  }
  /*}}}  */
  /*{{{  maybe free malloced data from ORIARG handling*/
  if (oriarg)
     paket_free(argv);
  /*}}}  */

  return(exit_failure);

arg_mem_full:
  oputs((unsigned char*)M_NO_MEMORY);
  oputc('\n');
  return(r_mem_full);
}
/*}}}  */

int main(int argc, char *argv[])
{
  RETURNS new_exit;

  os_init();
  /*{{{  read arguments and rc, init the editor*/
  { stackent ocl_args[DIM_OCL_CMD_OPTS];
    int i;
    char *mark_mode=0;

    for (i=DIM_OCL_CMD_OPTS;i--;ocl_args[i].v=0);
    if ((new_exit=ori_arg_parse(argc,argv,ocl_args,&mark_mode))!=exit_failure)
       return(new_exit);
    start_debug();
    os_init_args_read();
    /*{{{  init the editor*/
    HostBegin(mark_mode);
#    ifdef VIRTUAL
       if (init_data_handle())
          exit_origami(r_vir_err,get_msg(F_VM_FAIL,"access",strerror(errno)));
#    endif
    initialise();
    /*}}}  */
    os_init_rc_read();
    memcpy(&(ocl_var[var_ocl_arg]),ocl_args,DIM_OCL_CMD_OPTS*sizeof(stackent));
  }
  /*}}}  */
  new_exit=main_editor_loop();
  /*{{{  try to do a pretty exit*/
  msg_message(M_BYE);
  exit_origami(new_exit,print_buffer[0]?print_buffer:(unsigned char*)0);
  /*}}}  */
  return exit_failure;
}
/*}}}  */
