/*{{{}}}*/
/*{{{  #includes*/
#include <ctype.h>
#include <termios.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define PARSER_C
#define I_SET_C
#define I_RC_CHECK

#include <h/getmsg.h>
#include "keybind.h"
#include <h/rcformat.h>
#include <h/envvar_str.h>
#include <lib/ori_rc_lib.h>
#include <lib/ori_add_lib.h>
/*}}}  */

/*{{{  define _POSIX_VDISABLE to 0377, if bd os doesn't support*/
#ifndef _POSIX_VDISABLE
#  define _POSIX_VDISABLE 0377
#endif
/*}}}  */
/*{{{  variables*/
private unsigned char nullc='\0';
public int bind_const[RCC_SIZE];
public unsigned char ocl_opts[NO_OCL_CMD_OPTS*(2*BIND_NAME_LEN+2)];
public unsigned char *op_def_name = &nullc;
public int op_def_args = 0;
public TOKEN *m_def;
public unsigned char *modenames[MODE_COUNT];
public int used_modes=0;
public int vm_no=0;
public int bc_mac=0;
public boolean demand=False;
private unsigned char *ocl_arg_names[ocl_max_args];
/*}}}  */

/*{{{  check_length*/
public void check_length(TOKEN *b)
{
  if ((b-mac_ptr)>OCL_CODE_LEN)
     m_exit(M_LONG_MACRO);
}
/*}}}  */
/*{{{  mac_arg_parse*/
private int mac_arg_parse(void)
{ int no;

  for (no=0;no<ocl_max_args;)
   { switch (get_full_token())
      { case END:
           if (no==0)
              goto inv_arg_list;
           return(no);
        case VARIABLE:
        case NAME:
           def_ocl_args(no+1);
           creat_var(tk_string,1,ocl_loc_var(no));
           ocl_arg_names[no]=mstrcpy(tk_string);
           no++;
           continue;
        default:
        inv_arg_list:
           m_exit(M_INV_ARGS);
      }
   }
}
/*}}}  */
/*{{{  no_arg_vars*/
private void no_arg_vars(int args)
{
  while (args--)
   { free_var(ocl_arg_names[args]);
     kbd_free(ocl_arg_names[args]);
   }
}
/*}}}  */
/*{{{  parse the file*/
public boolean process_file(void)
{
  /*{{{  variables*/
  tokens token;
  boolean named=False;
  int dem_count=0;
  int n_dem_count=0;
  char *opt_end;
  /*}}}  */

  rc_put_w(RC_CHECK,rc.fp);
  general_abort_key=_POSIX_VDISABLE;
  mac_count= -1;
  put_consts();
  mac_count= 0;
  /*{{{  init ocl options*/
  opt_end=(char*)ocl_opts;
  *opt_end='\0';
  /*}}}  */
  init_vars(def_dummy_vars);
  /*{{{  parse the sources*/
  err_pos_out=True;
  for (;mac_count!=0x7fff;)
     if ((token=get_full_token())==ENDFILE)
        if (dem_count || n_dem_count)
           m_exit(F_MIS_DEM_END,dem_count+n_dem_count);
        else
           break;
     else
        switch (token)
         { case END:
            /*{{{  handle end of demand statements*/
              if (!dem_count && !n_dem_count)
                 m_exit(M_NO_DEM);
              if (n_dem_count)
                 demand=(--n_dem_count==0 && dem_count);
              else
                 demand= --dem_count;
              if (verbose_level>1)
                 fprintf(stderr,F_DEM_COMMENT,demand?"":"not ",dem_count,n_dem_count);
              goto get_end_token;
            /*}}}  */
           case BEGIN:
            /*{{{  ocl statement*/
            { token=get_full_token();
              switch (token) {
                /*{{{  demand*/
                case DEMAND:
                   switch(get_full_token())
                    { case NOT:
                         if (get_single_token()!=BEGIN)
                            goto demand_error;
                         n_dem_count++;
                         demand=False;
                         break;
                      case BEGIN:
                         if (n_dem_count)
                            n_dem_count++;
                         else
                            demand= ++dem_count;
                         break;
                      default:
                      demand_error:
                         m_exit(M_WANTBEGIN);
                    }
                   if (verbose_level>1)
                      fprintf(stderr,F_DEM_COMMENT,demand?"":"not ",dem_count,n_dem_count);
                   continue;
                /*}}}  */
                /*{{{  history_declare*/
                case HISTORY:
                   if (name_to_history(get_full_token(),True)==unknown_history)
                history_expected:
                      m_exit(M_NOHISTORY);
                   break;
                /*}}}  */
                /*{{{  shift-mark*/
                case LINESTART:
                   if (get_keycode_token()!=CHAR)
                      m_exit(M_WANTCHAR);
                   soln_str=tk_char;
                   break;
                /*}}}  */
                /*{{{  line-limiter*/
                case LINEEND:
                   if (get_keycode_token()!=CHAR)
                      m_exit(M_WANTCHAR);
                   eoln_str=tk_char;
                   break;
                /*}}}  */
                /*{{{  mode-limiter*/
                case MODE_COMMA:
                   if (get_keycode_token()!=CHAR)
                      m_exit(M_WANTCHAR);
                   comma=tk_char;
                   break;
                /*}}}  */
                /*{{{  termalias*/
                case TERMALIAS:
                 { unsigned char replace[NAME_LG+1];
                   tokens tn;

                   token=get_full_token();
                   if (token!=NAME && token!=NUMBER)
                      m_exit(M_T_NAME);
                   ustrcpy(replace,tk_string);
                   if (!dest_mac) rc_put_c(RC_TERMALIAS,rc.fp);
                   begin_parse();
                   while ((tn=get_full_token())!=END)
                    { if (tn!=NAME && tn!=NUMBER)
                         m_exit(M_T_NAME);
                      /*{{{  print alias to rc*/
                      if (!dest_mac)
                       { unsigned char *s;

                         rc_put_c(1,rc.fp);
                         s=tk_string;
                         do rc_put_c(*s,rc.fp); while (*s++);
                       }
                      /*}}}  */
                    }
                   /*{{{  print replace to rc*/
                   if (!dest_mac)
                    { unsigned char *s;

                      rc_put_c(0,rc.fp);
                      s=replace;
                      do rc_put_c(*s,rc.fp); while (*s++);
                    }
                   /*}}}  */
                   break;
                 }
                /*}}}  */
                /*{{{  os-extension*/
                case OS_EXT:
                   begin_parse();
                   if (verbose_level>0)
                      fprintf(stderr,M_ADD_OS_X);
                   /*{{{  read all strings*/
                   { if (!dest_mac)
                        rc_put_c(RC_OS_EXTENSION,rc.fp);
                     for (;;)
                      { switch(get_full_token())
                         { int no;

                           /*{{{  ) == end*/
                           case END:
                              break;
                           /*}}}  */
                           /*{{{  string*/
                           case MACRO:
                              if (!dest_mac)
                               { TOKEN *x=tk_macro;

                                 if (verbose_level>0)
                                    fprintf(stderr,(char*)tk_string);
                                 while (*x)
                                    rc_put_c(*x++,rc.fp);
                               }
                              continue;
                           /*}}}  */
                           /*{{{  defined function*/
                           case OPERATION:
                              if (!dest_mac)
                               { no=tk_operation->place;
                                 goto os_number;
                               }
                              continue;
                           /*}}}  */
                           /*{{{  mode*/
                           case KBD:
                              if ((no=token2mode(get_full_token()))<0)
                                 kbd_exit(1);
                              goto os_number;
                           /*}}}  */
                           /*{{{  history*/
                           case HISTORY:
                              no=name_to_history(get_full_token(),False);
                              if (no==unknown_history)
                                 goto history_expected;
                              if (!dest_mac)
                               { goto os_number;
                                 /*{{{  os_number*/
                                 { char *x;
                                   char id[16];

                                 os_number:
                                   sprintf(x=id,"%d",no);
                                   while (*x)
                                      rc_put_c(*x++,rc.fp);
                                   if (verbose_level>0)
                                      fprintf(stderr,(char*)id);
                                 }
                                 /*}}}  */
                               }
                              continue;
                           /*}}}  */
                           /*{{{  default error*/
                           default:
                              m_exit(M_WANTEND);
                           /*}}}  */
                         }
                        break;
                      }
                     if (!dest_mac)
                        rc_put_c(0,rc.fp);
                   }
                   /*}}}  */
                   if (verbose_level>0) fprintf(stderr,M_END_OS_X);
                   break;
                /*}}}  */
                /*{{{  terminal*/
                case TERMINAL:
                   terminal_cmd();
                   continue;
                /*}}}  */
                /*{{{  alias command*/
                case KEYALIAS:
                   set_alias();
                   break;
                /*}}}  */
                /*{{{  user-macros*/
                case DEFMYFIX:
                   if (mac_count || get_full_token()!=NUMBER)
                      m_exit(mac_count?M_MACGIV:M_NOADDCOUNTER);
                   mac_count=tk_val;
                   break;
                /*}}}  */
                /*{{{  keybind command*/
                case KEYDEF:
                   def_keybind_code();
                   continue;
                /*}}}  */
                /*{{{  overload prefix*/
                case OVER_PRE:
                   overload_prefix();
                   continue;
                /*}}}  */
                /*{{{  forward-declaration*/
                case FORWARD:
                 { int args;
                 
                   /*{{{  check name, and maybe arguments*/
                   switch (token=get_full_token())
                    { case BEGIN:
                         args=mac_arg_parse();
                         no_arg_vars(args);
                         if (get_full_token()!=NAME)
                            goto f_w_e;
                         else
                            break;
                      case NAME:
                         args=0;
                         break;
                      default:
                      f_w_e:
                         m_exit(M_MACNAME);
                    }
                   /*}}}  */
                   mac_ptr[0]=(TOKEN)((int)O_EXE_MACRO+ ++mac_count);
                   mac_ptr[1]=M_END_MACRO;
                   if (verbose_level>0) fprintf(stderr,F_FOR,mac_count);
                   creat_op(tk_string,False,1,mac_ptr,mac_count,False,args);
                   break;
                 }
                /*}}}  */
                /*{{{  automacro/abortmacro/promptmacros/knb/view*/
                case DEFAUTOSAVE:
                case DEFQUIT:
                case DEFU1:
                case DEFU2:
                case DEFAUTO:
                case DEFAB:
                case KNBM:
                case PRO_IN:
                case PRO_OUT:
                case B_CHG_MAC:
                case VMAC:
                 { int *x= token==DEFAUTOSAVE ? &salarm_macro:
                           token==DEFQUIT  ? &squit_macro:
                           token==DEFU1    ? &susr1_macro:
                           token==DEFU2    ? &susr2_macro:
                           token==PRO_IN   ? &pin_macro:
                           token==PRO_OUT  ? &pout_macro:
                           token==KNBM     ? &knb_macro:
                           token==DEFAB    ? &abort_macro:
                           token==VMAC     ? &view_macro:
                           token==B_CHG_MAC? &buff_macro:
                                             &auto_macro;

                  /*{{{  already given?*/
                  if (*x)
                     m_exit(M_DUPAUTO);
                  /*}}}  */
                  /*{{{  check if macroname given*/
                  if (get_full_token()!=OPERATION || tk_operation->place<0)
                     m_exit(M_INVMNAME);
                  /*}}}  */
                  *x=tk_operation->place;
                  break;
                 }
                /*}}}  */
                /*{{{  define a new operation*/
                case DEFOP:
                 { boolean ret_used=False;
                   TOKEN  *tl_ptr;
                   unsigned char name[NAME_LG];
                 
                   name_parse(name,M_DEFONAME,False);
                   /*{{{  prepare M_CALL-information*/
                   op_def_args=0;
                   op_def_name=name;
                   m_def=mac_ptr;
                   /*}}}  */
                   if (!(tl_ptr=opt_parse_macro(mac_ptr,0,&ret_used,False,n_dem_count,False)))
                     return(True);
                   /*{{{  reset M_CALL-information*/
                   op_def_name = &nullc;
                   m_def=0;
                   /*}}}  */
                   creat_op(name,True,tl_ptr-mac_ptr,mac_ptr,-1,ret_used,0);
                   break;
                 }
                /*}}}  */
                /*{{{  macro commands*/
                case DEFASM:
                case DEFMACRO:
                case INITMACRO:
                 { unsigned char name[NAME_LG];
                   boolean asm_used=(token==DEFASM);
                   boolean def_used=(asm_used || (token==DEFMACRO));
                   boolean ret_used=False;
                   boolean dest_found;
                   TOKEN  *tl_ptr;
                   int var_c=var_count;
                   readtags tag= def_used?RC_MAC_SET:RC_INITMACRO;
                   int po;
                 
                   /*{{{  maybe get args for deffun, get name*/
                   op_def_args=0;
                   switch (token)
                    { case DEFMACRO:
                         if ((token=get_full_token())!=BEGIN)
                            break;
                         op_def_args=mac_arg_parse();
                      default:
                         token=get_full_token();
                      }
                   /*}}}  */
                   if (token==NAME)
                    /*{{{  declaration & definition*/
                    { po = ++mac_count;
                      ustrcpy(name,tk_string);
                      op_def_name=name;
                      m_def=mac_ptr;
                    }
                    /*}}}  */
                   else if (token==OPERATION)
                    /*{{{  definition*/
                    { if (tk_operation->def || (tk_operation->args!=op_def_args && !asm_used))
                         m_exit(M_DUPDEF);
                      ustrcpy(name,tk_operation->op_name);
                      tk_operation->def=True;
                      po = (int)*(tk_operation->ops)-(int)O_EXE_MACRO;
                      op_def_name=name;
                    }
                    /*}}}  */
                   else
                      m_exit(M_INVMNAME);
                   dest_found= dest_mac && !(ustrcmp(dest_mac,name));
                   if (asm_used)
                    /*{{{  get assembler code*/
                    { if (!(tl_ptr=parse_asm(mac_ptr,name)))
                         return(True);
                    }
                    /*}}}  */
                   else
                    /*{{{  get normal OCL code*/
                    { if
                       ( !(tl_ptr=opt_parse_macro
                                   ( mac_ptr,
                                     op_def_args,
                                     &ret_used,
                                     def_used,
                                     !demand,
                                     False
                                   )
                          )
                       )
                         return(True);
                    }
                    /*}}}  */
                   /*{{{  store information and show verbose*/
                   if (token!=OPERATION)
                    /*{{{  macro as operation storing*/
                    { /* create op does the verbose handling! */
                      if (def_used)
                       { TOKEN dummy[1];
                      
                         dummy[0]=(TOKEN)((int)O_EXE_MACRO+po);
                         creat_op(name,True,1,dummy,po,False,op_def_args);
                       }
                      else
                         creat_op(name,True,tl_ptr-mac_ptr,mac_ptr,po,ret_used,op_def_args);
                      m_def=0;
                      op_def_name = &nullc;
                    }
                    /*}}}  */
                   else
                    /*{{{  verbose*/
                    { if (verbose_level>0)
                         fprintf(stderr,".\n");
                    }
                    /*}}}  */
                   /*}}}  */
                   if (!asm_used)
                    /*{{{  OCL code uses fixed argument commands*/
                    { *tl_ptr++=M_END_MACRO;
                      tl_ptr=opt_mac(mac_ptr,tl_ptr,False,False,True);
                      if (tl_ptr>mac_ptr && *(tl_ptr-1)==M_END_MACRO)
                         tl_ptr--;
                    }
                    /*}}}  */
                   /*{{{  write it to rc*/
                   if (dest_found && var_c==var_count)
                      write_dest_mac(mac_ptr,tl_ptr-mac_ptr);
                   write_macro_rc(tag,po,tl_ptr-mac_ptr,mac_ptr,name);
                   /*}}}  */
                   no_arg_vars(op_def_args);
                   break;
                 }
                /*}}}  */
                /*{{{  reference compress strings*/
                case REF_COMP:
                   token=get_full_token();
                   /*{{{  maybe set used*/
                   if (token==SET_COUNTER)
                    { ref_init_comp_data(0);
                      token=get_full_token();
                    }
                   /*}}}  */
                   /*{{{  test if ( used*/
                   if (token!=BEGIN)
                      m_exit(M_WANTBEGIN);
                   /*}}}  */
                   for (;;)
                    { switch(get_full_token())
                       { case END:
                            break;
                         case MACRO:
                            if (ref_add_word((char*)tk_string))
                               m_exit(M_NOMEMORY);
                            continue;
                         default:
                            m_exit(M_REFCOMP);
                       }
                      break;
                    }
                   break;
                /*}}}  */
                /*{{{  define modes*/
                case MULTIKBD:
                   define_k_modes();
                   continue;
                /*}}}  */
                /*{{{  mouse*/
                case MOUSEMAP:
                   mouse_code();
                   continue;
                /*}}}  */
                /*{{{  mode*/
                case KBD:
                   mode_code();
                   continue;
                /*}}}  */
                /*{{{  define the name of this binding*/
                case BINDNAME:
                 { unsigned char name[NAME_LG];

                   if (named)
                      m_exit(M_DUPKEY);
                   named=True;
                   name_parse(name,M_KEYNAME,True);
                   write_name_rc(tk_string);
                   for (;;)
                    { switch(get_full_token())
                       { case END:
                            break;
                         case BEGIN:
                          /*{{{  handle one ocl-option*/
                            if
                             /*{{{  correct option name given*/
                             (    get_full_token()==MACRO
                               && isalpha(tk_string[0])
                               && ustrlen(tk_string)<=BIND_NAME_LEN
                             )
                             /*}}}  */
                             /*{{{  check second string*/
                             { char b[BIND_NAME_LEN+1];

                               strcpy(b,(char*)tk_string);
                               if ( get_full_token()==MACRO )
                                /*{{{  define as ocl-options*/
                                { char *o;

                                  if
                                   /*{{{  space left in option string*/
                                   (   opt_end+strlen(b)+ustrlen(tk_string)+2
                                     < (char*)ocl_opts+NO_OCL_CMD_OPTS*(2*BIND_NAME_LEN+2)
                                   )
                                   /*}}}  */
                                   /*{{{  store it*/
                                   { for
                                      ( o=b
                                      ; (*opt_end++ = *o++)
                                      ;
                                      );
                                     for
                                      ( o=(char*)tk_string
                                      ; (*opt_end++ = *o++)
                                      ;
                                      );
                                     *opt_end='\0';
                                   }
                                   /*}}}  */
                                  else
                                     goto o_o_err;
                                  if (get_full_token()==END)
                                     continue;
                                }
                                /*}}}  */
                             }
                             /*}}}  */
                          /*}}}  */
                         default:
                         o_o_err:
                            m_exit(M_NOOCLOPT);
                       }
                      break;
                    }
                   continue;
                 }
                /*}}}  */
                /*{{{  defmark*/
                case DEFMARK:
                 { int i,j;
                   unsigned char marks[4][FOLD_TAG_LENGTH+1];
                   unsigned char mark_name[NAME_LG];
                 
                   name_parse(mark_name,M_MARKNAME,True);
                   /*{{{  get all mark_characters*/
                   begin_parse();
                   /*{{{  get all tags*/
                   for (i=0;i<4;i++) {
                     begin_parse();
                     /*{{{  get tag_length-1 chars*/
                     for (j=0;j<FOLD_TAG_LENGTH;j++)
                      { if (get_keycode_token()!=CHAR)
                           m_exit(M_WANTCHAR);
                        marks[i][j]=tk_char;
                      }
                     /*}}}  */
                     end_parse(M_WANTEND);
                   }
                   /*}}}  */
                   end_parse(M_WANTEND);
                   /*}}}  */
                   write_mark_rc(mark_name,marks);
                   break;
                 }
                /*}}}  */
                /*{{{  defset*/
                case DEFSET:
                   parse_set();
                   break;
                /*}}}  */
                /*{{{  defvar*/
                case DEFVAR:
                 { tokens to;
                   int size;

                   /*{{{  parse ( and maybe set dimension*/
                   switch (to=get_full_token())
                    { case BEGIN:
                         size=1;
                         break;
                      case NUMBER:
                         size=tk_val;
                         begin_parse();
                         if (size>0)
                            break;
                      default:
                         m_exit(M_WANTBEGIN);
                    }
                   /*}}}  */
                   while ((to=name_to_var(get_full_token(),size,True))!=END)
                      if (to!=VARIABLE)
                         m_exit(M_NOVAR);
                   break;
                 }
                /*}}}  */
                /*{{{  undeclare*/
                case UNDECLARE:
                 { tokens to;

                   begin_parse();
                   while ((to=get_full_token())!=END)
                    { if (verbose_level>1)
                         fprintf(stderr,"undeclaring %s\n",tk_string);
                      switch (to)
                       { case OPERATION:
                            if (tk_operation->def)
                               free_op(tk_operation->op_name);
                            else
                               m_exit(F_DNDMAC,tk_operation->op_name);
                            continue;
                         case VARIABLE:
                            free_var(tk_var->var_name);
                            break;
                         case OPCODE:
                            tk_key->use=False;
                            break;
                         default:
                            m_exit(M_NOVAR);
                       }
                    }
                   break;
                 }
                /*}}}  */
                /*{{{  defmodestring*/
                case DEFMODE:
                 { unsigned char name[NAME_LG];
                   unsigned char str[2][NAME_LG];
                   int i;

                   if (used_modes==MODE_COUNT)
                      m_exit(M_MANYMODES);
                   /*{{{  read name*/
                   name_parse(name,M_UMNAME,False);
                   for (i=0;i!=used_modes;i++)
                      if (!ustrcmp(tk_string,modenames[i]))
                         m_exit(M_UMNAME);
                   modenames[used_modes]=mstrcpy(tk_string);
                   /*}}}  */
                   /*{{{  scan the two string*/
                   for (i=0;i<2;i++) {
                     if (get_full_token()!=MACRO)
                        m_exit(M_MSTR);
                     ustrcpy(str[i],tk_string);
                   }
                   /*}}}  */
                   write_ums_rc(used_modes++,str[0],str[1]);
                   /*{{{  verbose?*/
                   if (verbose_level>0)
                      fprintf(stderr,F_DEFMOD,used_modes-1,str[0],str[1]);
                   /*}}}  */
                   break;
                 }
                /*}}}  */
                /*{{{  start-list-editing*/
                case START_LIST:
                   arg_list=True;
                   break;
                /*}}}  */
                /*{{{  opcodes or default error*/
                case OPCODE:
                   if (tk_key->num==O_TITLE_HIDE)
                    { var_notitle=1;
                      break;
                    }
                default:
                   goto pars_error;
                /*}}}  */
              }
            get_end_token:
              end_parse(M_WANTEND);
              break;
            }
            /*}}}  */
           /*{{{  default=error*/
           default:
           pars_error:
              m_exit(M_NOKCOMMAND);
           /*}}}  */
         }
  err_pos_out=False;
  /*}}}  */
  /*{{{  test number of defined functions*/
  if (mac_count==0x7fff)
     m_exit(M_MUCH_FUN);
  /*}}}  */
  if (verbose_level>0)
     fprintf(stderr,"-----------------\n");
  /*{{{  check forward-declarations*/
  { OP_LIST *op;

    op= &new_op;
    while (op)
       if (!op->def)
          m_exit(F_DNDMAC,op->op_name);
       else
          op=op->next;
  }
  /*}}}  */
  /*{{{  check mkbd-defines*/
  check_k_modes();
  /*}}}  */
  /*{{{  write sets*/
  if (!dest_mac)
     writesets(rc.fp);
  /*}}}  */
  /*{{{  write keyboards*/
  write_kbds();
  /*}}}  */
  put_consts();

  return(False);
}
/*}}}  */
