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

#include <local/bool.h>

#define I_RC_CODE
#define PUTRC
#include "../h/rcformat.h"

#define KEYBIND_C

#include "keybind.h"
#include "../h/envvar_str.h"
/*}}}  */

/*{{{  variables*/
/*{{{  prototypes for getopt*/
int getopt(int,char**,char*);
extern char *optarg;
extern int optind;
/*}}}  */
private char *filebase=0;
public bool verbose=FALSE,
            do_opt=FALSE,
            sorting=FALSE;
public int  int_no=0;
public int t_counter=0,
           k_counter=0,
           m_counter=0;
public FILE *rc=0,*in=0,*ref=0;
public char *dest_mac=0;
#ifndef STD_FILTER
#   define STD_FILTER 0
#endif
private char *source_filter=STD_FILTER;
#ifndef STD_TOP_FILTER
#   define STD_TOP_FILTER 0
#endif
private char *top_filter=STD_TOP_FILTER;
private bool rcref=FALSE;
private FILE *bnd;
FILE *popen();
/*}}}  */

/*{{{  completebase*/
private char *completebase(char *s, char *t)
{
  char *h=getenv(HOME);

  if (!h || filebase)
    *s='\0';
  else {
    strcpy(s,h);
    strcat(s,PATH_SEP);
  }
  strcat(s,filebase?filebase:STD_BASENAME);
  strcat(s,t);
  return(s);
}
/*}}}  */

/*{{{  display usage*/
private void do_usage(void)
{
  fprintf(stderr,"Usage: keybind [-f filter][-m macro][-o namebase][-r file][-F filter][-M mouse][-U tag][-dhlsvOR] file\n");
  fprintf(stderr,M_MICE);
  show_mice(stderr);
  fprintf(stderr,"\nversion " VERSION "\n");
}
/*}}}  */
/*{{{  open source-file*/
public FILE *open_source(char *name,bool top)
{ FILE *x;
  char f[256];

  if ((top?top_filter:source_filter)==0)
     x=fopen(name,READ);
  else
     if (access(name,R_OK))
        x=0;
     else
      { strcpy(f,top?top_filter:source_filter);
        strcat(f," ");
        strcat(f,name);
        x=popen(f,READ);
      }
  return(x);
}
/*}}}  */
/*{{{  close source-file*/
public int close_source(FILE *f,bool top)
{ if (top?top_filter:source_filter) return(pclose(f)); else return(fclose(f));
}
/*}}}  */
/*{{{  write procedures*/
/*{{{  write mark-type to rc*/
public void write_mark_rc(char *name, char marks[4][tag_length-1])
{
  int i,j;
  char *n=name;

  if (dest_mac) return;
  rc_put_c(RC_MARKS,rc);
  for (i=1;i<mark_name_lg;i++)
    if (*n!='\0') rc_put_c(*n++,rc); else rc_put_c('\0',rc);
  for (i=0;i<4;i++)
    for (j=0;j<(tag_length-1);j++)
      rc_put_c(marks[i][j],rc);
  m_counter++;
  if (verbose) fprintf(stderr,F_MDECLARE,name);
}
/*}}}  */
/*{{{  write break*/
public void write_break_rc(char a)
{
  if (dest_mac) return;
  rc_put_c(RC_BREAK,rc);
  rc_put_c(a,rc);
  if (verbose)
    if (a<' ')
      fprintf(stderr,"ABORT: C-%c.\n",a+'@');
    else
      fprintf(stderr,"ABORT: %c.\n",a);
}
/*}}}  */
/*{{{  write automacro*/
public void write_auto_rc(tokens t,int x)
{
  char code;
  char *s;

  if (dest_mac) return;
  switch (t) {
    case DEFAB:   code=RC_ABORTM;   s="abort-";        break;
    case PRO_IN:  code=RC_PROMPTIN; s="begin-prompt-"; break;
    case PRO_OUT: code=RC_PROMPTOUT;s="end-prompt-";   break;
    case KNBM:    code=RC_KNBMAC;   s="key-not-bound-";break;
    case VMAC:    code=RC_VMAC;     s="view-";         break;
    default:      code=RC_AUTOM;    s="read-newfile-"; break;
  }
  rc_put_c(code,rc);
  rc_put_w(x,rc);
  if (verbose)
    fprintf(stderr,F_HOOK_MACRO,s,x);
}
/*}}}  */
/*{{{  write binding_name to rc file*/
public bool write_name_rc(char *name)
{
  int i=keyn_lg;

  if (dest_mac) return(FALSE);
  if (*name=='\0') {
    fprintf(stderr,M_NOBINDNAME);
    return(TRUE);
  }
  rc_put_c(RC_KEYNAME,rc);
  while (i--) {rc_put_c(*name<' ' ? '\0' : *name,rc);name++;}
  return(FALSE);
}
/*}}}  */
/*{{{  write one macro definition to rc file*/
public bool write_macro_rc(readtags tag, TOKEN op, int lg, TOKEN *code)
{
  if (dest_mac && op!=O_EXE_MACRO) return(FALSE);
  if (!(tag==RC_DEFMACRO || tag==RC_INITMACRO)) {
    fprintf(stderr,M_NOMACROTAG);
    return(TRUE);
  }
  /*{{{  verbose-mode*/
  if (verbose)
   { fprintf(stderr,tag==RC_DEFMACRO?F_STMAC:F_DEMAC,op,lg+1);
     t_counter += lg;
   }
  /*}}}  */
  rc_put_c(tag,rc);
  rc_put_w(op,rc);
  rc_put_w(lg,rc);
  while (lg--) rc_put_w(*code++,rc);
  return(FALSE);
}
/*}}}  */
/*{{{  write an usermodestring to rc*/
bool write_ums_rc(int no,char *lo,char *sh)
{
  int i;

  if (dest_mac) return(FALSE);
  rc_put_c(RC_MODE,rc);
  rc_put_c(no,rc);
  rc_put_w(i=strlen(lo),rc);
  for (;i--;) rc_put_c(*lo++,rc);
  rc_put_w(i=strlen(sh),rc);
  for (;i--;) rc_put_c(*sh++,rc);

  return(FALSE);
}
/*}}}  */
/*{{{  write one definition to bind file*/
public void write_bind(char *name,char *code,int kbd)
{
  if (dest_mac) return;
  bnd_wr(kbd,name,code);
}
/*}}}  */
/*{{{  write number of used macros*/
static off_t offset = -1;
void put_mac_count(int no)
{
  off_t current= -1;

  if (dest_mac) return;
  if (offset == -1)
     offset=ftell(rc);
  else
   { current=ftell(rc);
     fseek(rc,offset,SEEK_SET);
   }
  rc_put_c(RC_MACCOUNT,rc);
  rc_put_w(no,rc);
  if (current!= -1) fseek(rc,current,SEEK_SET);
}
/*}}}  */
/*{{{  write one macro coded to stdout*/
void write_dest_mac (TOKEN *b,int lg)
{ write_macro_rc(RC_INITMACRO,O_EXE_MACRO,lg,b);
  rc_put_c(RC_ENDE,rc);
}
/*}}}  */
/*{{{  write mousebutton-mapping to rc*/
public void write_buttons_rc(TOKEN*map,int buttons)
{
  int i;

  if (dest_mac) return;
  rc_put_c(RC_M_TAB,rc);
  rc_put_w(buttons,rc);
  for (i=0;i<buttons;i++) rc_put_w(map[i],rc);
  if (verbose) fprintf(stderr,F_M_USE,m_name,buttons);
}
/*}}}  */
/*{{{  rc_app_help*/
private bool rc_app_help(char *filename,bool add_tag,bool tmpfile)
{ FILE *f;
  char c;
  int lg;
  char pre[10];
  int pre_lg;
  int lines;

  if (!(f=fopen(filename,READ))) return(TRUE);
  if (add_tag) sprintf(pre,"%d|",ref_kbd); else pre[0]='\0';
  pre_lg=strlen(pre);
  for (lines=0;!feof(f);lines++)
   { lg=rc_help_lg-pre_lg;
     fputs(pre,rc);
     for(lg=rc_help_lg-pre_lg;;lg--)
      { c=fgetc(f);
        if (c=='\n')
         { fputc('\n',rc);
           break;
         }
        else if (c==EOF)
         { if (add_tag) fputc('\n',rc);
           break;
         }
        else if (lg>0)
           fputc(c,rc);
      }
   }
  if (verbose) fprintf(stderr,"appended %s %d lines as help.\n",filename,lines);
  if (fclose(f))
   { fprintf(stderr,F_CLOSE,filename);return(TRUE); }
  return(tmpfile && unlink(filename));
}
/*}}}  */
/*}}}  */

/*{{{  main*/
void main(int argc,char **argv)
{
  /*{{{  variable declarations*/
  char *home=getenv(HOME),
       *refname=0,
       rcfile[_POSIX_PATH_MAX],
       tmpbind[_POSIX_PATH_MAX],
       bindfile[_POSIX_PATH_MAX];
  static char r_n_tmp[]=REF_TMP_FILE;
  bool crashed;
  /*}}}  */

  /*{{{  init*/
  homecheck(home);
  init_alias();
  init_sets();
  /*}}}  */
  /*{{{  parse arguments*/
  { int c;

    /*{{{  Notes*/
    /* This is the first version which uses getopt, so the code is still suboptimal. */
    /*}}}  */
    while ((c=getopt(argc,argv,"f:h?lo:sdvr:F:ORm:M:U:"))!=EOF)
    switch (c)
    {
      /*{{{  f filter*/
      case 'f':
         top_filter=source_filter=(*optarg?optarg:0); break;
      /*}}}  */
      /*{{{  h, ?*/
      case 'h':
      case '?': do_usage();exit(0);
      /*}}}  */
      /*{{{  l*/
      case 'l':
      {
        sc_list_keys();
        exit(0);
        break;
      }
      /*}}}  */
      /*{{{  o name*/
      case 'o': filebase=optarg; break;
      /*}}}  */
      /*{{{  s*/
      case 's': sorting=TRUE; break;
      /*}}}  */
      /*{{{  d*/
      case 'd': new_vars_enabled=TRUE; break;
      /*}}}  */
      /*{{{  v*/
      case 'v': verbose=TRUE; break;
      /*}}}  */
      /*{{{  r name*/
      case 'r': refname=optarg; break;
      /*}}}  */
      /*{{{  F filter*/
      case 'F': top_filter= *optarg?optarg:0;source_filter=0; break;
      /*}}}  */
      /*{{{  O*/
      case 'O': do_opt=TRUE; break;
      /*}}}  */
      /*{{{  R*/
      case 'R': rcref=TRUE; break;
      /*}}}  */
      /*{{{  m macro*/
      case 'm': dest_mac=optarg; break;
      /*}}}  */
      /*{{{  M name*/
      case 'M': m_name=optarg; break;
      /*}}}  */
      /*{{{  U tag*/
      case 'U': use(optarg); break;
      /*}}}  */
    }
  }
  /*}}}  */
  /*{{{  init keytabs*/
  if (init_mouse()) { do_usage();exit(1); }
  default_using();
  if (!init_keytab(0,0)) { printf(M_NOMEMORY);exit(1); }
  /*}}}  */
  /*{{{  open files/pipe*/
  /*{{{  open reference file*/
  if (!dest_mac)
   { if (rcref && !refname) { refname=r_n_tmp;mktemp(refname); }
     if (refname && (ref=fopen(refname,WRITE))==(FILE*)0)
      { fprintf(stderr,M_NOREF);exit(1);}
   }
  /*}}}  */
  /*{{{  open input-file*/
  if (optind>=argc)
   { do_usage();exit(1); }
  if ((in=open_source(argv[optind],TRUE))==(FILE*)0)
   { fprintf(stderr,M_NOBFILE);exit(1); }
  source=argv[optind];
  init_scan();
  /*}}}  */
  /*{{{  open .origamirc*/
  if (dest_mac)
     rc=stdout;
  else if ((rc=fopen(completebase(rcfile,"rc"),WRITE_BIN))==(FILE*)0)
   { fprintf(stderr,F_NORC,home,filebase);exit(1); }
  /*}}}  */
  /*{{{  open .origamibind*/
  mktemp(strcpy(tmpbind,BND_TMP_FILE));
  if (!dest_mac)
   { if (!sorting)
      /*{{{  open file*/
      { if ((bnd=fopen(tmpbind,WRITE))==(FILE*)0)
         { fprintf(stderr,F_NOBIND,tmpbind);exit(1); }
      }
      /*}}}  */
     else
      /*{{{  open pipe*/
      { sprintf(bindfile,SORTCMD,tmpbind);
        if ((bnd=popen(bindfile,WRITE))==(FILE*)0)
         { fprintf(stderr,F_NOPIPE,bindfile);exit(1); }
      }
      /*}}}  */
   }
  /*}}}  */
  /*}}}  */
  if (dest_mac) verbose=FALSE;
  if (!(crashed=process_file()) && !dest_mac)
   /*{{{  handle vars and bind*/
   { if (int_no) { rc_put_c(RC_INTS,rc);rc_put_w(int_no,rc); }
     if (sorting ? pclose(bnd) : fclose(bnd))
      { fprintf(stderr,F_CLOSE,"bindfile");crashed=TRUE;unlink(tmpbind); }
     else
      { rc_put_c(RC_BIND,rc);
        if (rc_app_help(tmpbind,FALSE,TRUE))
         { fprintf(stderr,F_NOBIND,tmpbind);crashed=TRUE; }
      }
   }
   /*}}}  */
  else
   /*{{{  close bind*/
   { if (!dest_mac && !sorting) fclose(bnd); else pclose(bnd);
     unlink(tmpbind);
   }
   /*}}}  */
  /*{{{  close ref*/
  if (ref && fclose(ref))
   { crashed=TRUE;fprintf(stderr,F_CLOSE,refname); }
  /*}}}  */
  /*{{{  maybe copy ref to rc*/
  if (!crashed && rcref && !dest_mac)
     crashed=rc_app_help(refname,TRUE,refname==r_n_tmp);
  /*}}}  */
  /*{{{  close rc and in and unlink tmp*/
  if (in) close_source(in,TRUE);
  if (!dest_mac && fclose(rc))
   { fprintf(stderr,F_CLOSE,"rc-file");
     crashed=TRUE;
   }
  /*}}}  */
  /*{{{  maybe message*/
  if (crashed) {
    fprintf(stderr,M_INCORRECTFILES);
    exit(1);
  } else if (verbose) {
    fprintf(stderr,F_INTS,int_no);
    fprintf(stderr,F_BINDS,t_counter);
    fprintf(stderr,F_MARKT,m_counter);
    fprintf(stderr,F_MOUSE,m_name);
  }
  /*}}}  */
  exit(0);
}
/*}}}  */
