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

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

#define STRING_C
#define I_BUFFLOOP_C
#define I_DISPLAY_C
#define I_FINDS_C
#define I_FILEC_C
#define I_GETMSG_C
#define I_GETTK_C
#define I_MESSAGES_C
#define I_ORIEDT_C
#define I_PROMPT_C

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

#ifdef REGEXP
# include <lib/ori_re_lib.h>
  /*{{{  regular search/replace*/
  /*{{{  variables*/
#  define PAT_NUM (PAT_COUNT+1)
  private regmatch_t matches[PAT_NUM+1];
  private regex_t *ppreg;
  private int nmatches;
  private int r_c_e;
  public boolean preg_ok=False;
  /*}}}  */

  /*{{{  reg_comp*/
  private boolean reg_comp(boolean full_action,boolean inverse)
  {
    /*{{{  variables*/
#    ifdef REG_COMP_BUFF
       static struct pr_buff
        { unsigned char s[LINELEN+1];
          reg_types mode;
          boolean full;
          regex_t prog;
        } pr_list[REG_COMP_BUFF];
       static int pr_b_used=0;
       static int pr_b_free=0;
#    else
       static regex_t preg;
#    endif
    int i_t_l_f_offset;
    /*}}}  */

    /*{{{  check inverse search!*/
    inverted_search=False;
    i_t_l_f_offset=0;
    if
     (   (    reg_type==ext_reg
           && item_to_look_for[0]=='!'
         )
      || (    reg_type==basic_reg
           && item_to_look_for[0]=='\\'
           && item_to_look_for[1]=='!'
         )
     )
     { if (inverse)
        { inverted_search=True;
          i_t_l_f_offset=(reg_type==ext_reg)?1:2;
        }
       else
        { r_c_e=REG_BADRPT;
          return(True);
        }
     }
    /*}}}  */
    /*{{{  get pr_list*/
#    ifdef REG_COMP_BUFF
       { int i;

         /*{{{  return, if already defined*/
         for (i=0;i<pr_b_used;i++)
            if
             (    pr_list[i].mode==reg_type
               && (!full_action || pr_list[i].full)
               && !ustrcmp(item_to_look_for+i_t_l_f_offset,pr_list[i].s)
             )
             { preg_ok=True;
               ppreg= &pr_list[i].prog;
               return(False);
             }
         /*}}}  */
         /*{{{  delete entry*/
         if (pr_b_used==REG_COMP_BUFF)
          { if (pr_b_free==REG_COMP_BUFF)
               pr_b_free=0;
            regfree(&pr_list[pr_b_free].prog);
            pr_b_used--;
          }
         /*}}}  */
         preg_ok=False;
         ppreg= &pr_list[pr_b_free].prog;
       }
#    else
       ppreg= &preg;
       if (preg_ok)
        { regfree(ppreg);
          preg_ok=False;
        }
#    endif
    /*}}}  */
    nmatches=full_action?PAT_NUM:1;
    switch
     /*{{{  regcomp(item_to_look_for,...)*/
     ( r_c_e=regcomp
              ( ppreg,
                (char*)item_to_look_for+i_t_l_f_offset,
                  USE_NOSUB(full_action)
                | (reg_type==ext_reg?REG_EXTENDED:0)
                | USE_SUB_LIMIT(PAT_NUM,*ppreg)
              )
     )
     /*}}}  */
     {
       /*{{{  0 -> ok*/
       case 0:
          preg_ok=True;
          break;
       /*}}}  */
       /*{{{  REG_ESPACE -> ori_abort and error*/
       case REG_ESPACE:
          ocl_msg("REG_ESPACE error",0);
          ori_abort(~SIGINT);
          break;
       /*}}}  */
       /*{{{  else -> error*/
       default:
          regfree(ppreg);
          break;
       /*}}}  */
     }
    /*{{{  save program*/
#    ifdef REG_COMP_BUFF
       if (preg_ok)
        { pr_list[pr_b_free].mode=reg_type;
          pr_list[pr_b_free].full=full_action;
          ustrcpy(pr_list[pr_b_free].s,item_to_look_for+i_t_l_f_offset);
          pr_b_used++;
          pr_b_free++;
        }
#    endif
    /*}}}  */

    return(!preg_ok);
  }
  /*}}}  */
  /*{{{  reg_ustrstr*/
  public unsigned char *reg_ustrstr(unsigned char *s,boolean sl,boolean el)
  {
    if (preg_ok)
     { switch
        /*{{{  regexec(ppreg,s,..)*/
        ( regexec
           ( ppreg,
             (char*)s,
             (size_t)nmatches,
             matches,
             (sl?0:REG_NOTBOL)|(el?0:REG_NOTEOL)
           )
        )
        /*}}}  */
        { case 0:
             return((unsigned char*)matches[0].rm_sp);
          default:
             ocl_msg("strstr error",0);
             ori_abort(~SIGINT);
          case REG_NOMATCH:
             break;
        }
     }
    return(0);
  }
  /*}}}  */
  /*{{{  reg_repl*/
  /*{{{  variables*/
  static unsigned char const *r_r_exp_ptr;
  static unsigned char const *r_r_old_ptr;
  static unsigned char *r_r_buff_ptr;
  static unsigned char r_r_added;
  static int r_r_buflen;
  /*}}}  */
  /*{{{  rec_reg_repl*/
  /*{{{  add_repl_char*/
  private boolean add_repl_char(unsigned char c)
  {
    if ((r_r_added=c))
       if (r_r_buflen<=0)
          return(True);
       else
        { *r_r_buff_ptr++=r_r_added;
          r_r_buflen--;
        }
    return(False);
  }
  /*}}}  */
  private unsigned char *rec_reg_repl(int replen,boolean complex,boolean pad)
  {
    /*{{{  variables*/
    unsigned char *r_start_err;
    unsigned char *r_pat_start_err;
    /*}}}  */

    for (r_pat_start_err=(unsigned char*)r_r_exp_ptr;;)
     { unsigned char c;

       switch((c= *(r_start_err=(unsigned char*)r_r_exp_ptr)))
        { case '\0':
           /*{{{  end replace gen or error, if inside \{} expression*/
             if (complex)
              { r_start_err=r_pat_start_err-3;
                goto r_r_error;
              }
             *r_r_buff_ptr='\0';
             return(r_r_buff_ptr);
           /*}}}  */
          case '\\':
           /*{{{  handle pattern recall statements*/
           { boolean r_l_complex;
             boolean r_l_pad;
             boolean r_l_used;
             boolean r_l_upper;
             boolean r_l_lower;

             r_l_upper=r_l_lower=r_l_complex=False;
             switch(c= *++r_r_exp_ptr)
              {
                /*{{{  \\ or \} or \0*/
                case '0':
                   c='\0';
                case '}':
                   if (!complex)
                       goto r_r_error;
                case '\\':
                   break;
                   /* add in surrounding switch! */
                /*}}}  */
                /*{{{  \un  use uppercase from \n*/
                case 'u':
                   r_l_upper=True;
                   c= *++r_r_exp_ptr;
                   goto pattern_expr;
                /*}}}  */
                /*{{{  \ln  use lowercase from \n*/
                case 'l':
                   r_l_lower=True;
                   c= *++r_r_exp_ptr;
                   goto pattern_expr;
                /*}}}  */
                /*{{{  \{n  } expression, start complex and padding*/
                case '{':
                   r_l_complex=True;
                   r_l_pad=True;
                   r_l_used=False;
                   c= *++r_r_exp_ptr;
                   r_r_added=' ';
                   goto pattern_expr;
                /*}}}  */
                /*{{{  \?n  } expression, start complex , if n used!*/
                case '?':
                   r_l_complex=True;
                   r_l_pad=False;
                   r_l_used=True;
                   c= *++r_r_exp_ptr;
                   goto pattern_expr;
                /*}}}  */
                /*{{{  \!n  } expression, start complex , if n not used!*/
                case '!':
                   r_l_complex=True;
                   r_l_pad=False;
                   r_l_used=False;
                   c= *++r_r_exp_ptr;
                   goto pattern_expr;
                /*}}}  */
                /*{{{  a pattern expression \n*/
                pattern_expr:
                default:
                 { int n;

                   /*{{{  decode number to n, r_r_exp_ptr to last digit*/
                   { boolean ext_back_used;

                     for (n=0,ext_back_used=False;;c= *++r_r_exp_ptr)
                      { switch(c)
                         { case C_BACKREF:
                            /*{{{  mark \g usage*/
                              if (ext_back_used)
                                 goto r_r_error;
                              ext_back_used=True;
                              continue;
                            /*}}}  */
                           default:
                            /*{{{  handle current digit*/
                            { int i;

                              /*{{{  test for digit*/
                              for (i=9;;)
                                 if (hex_digits[i]==c)
                                    break;
                                 else if (i)
                                    i--;
                                 else
                                    goto r_r_error;
                              /*}}}  */
                              /*{{{  n=id for pattern*/
                              if (ext_back_used)
                               { ext_back_used=False;
                                 n+=i;
                                 n*=10;
                                 continue;
                               }
                              n+=i;
                              /*}}}  */
                              break;
                            }
                            /*}}}  */
                         }
                        break;
                      }
                   }
                   /*}}}  */
                   r_r_exp_ptr++;
                   if (r_l_complex)
                    /*{{{  gen sub string, cut at given length*/
                    { unsigned char *b;
                      int l;

                      b=r_r_buff_ptr;
                      if (r_l_pad)
                       { if (!matches[n].rm_ep || !matches[n].rm_sp)
                            goto r_r_error;
                         l=matches[n].rm_ep-matches[n].rm_sp;
                         if (l>replen)
                            l=replen;
                       }
                      else if (matches[n].rm_sp && matches[n].rm_ep)
                         l=r_l_used?replen:0;
                      else
                         l=r_l_used?0:replen;
                      if (!rec_reg_repl(l,True,r_l_pad))
                         return(0);
                      replen-=r_r_buff_ptr-b;
                    }
                    /*}}}  */
                   else if (replen>0)
                    /*{{{  copy pattern*/
                    { unsigned char *s;
                      int l;

                      if (!matches[n].rm_ep || !matches[n].rm_sp)
                         goto r_r_error;
                      s=(unsigned char*)matches[n].rm_sp;
                      l=matches[n].rm_ep-(char*)s;
                      s=(unsigned char*)r_r_old_ptr+(s-(unsigned char*)matches[0].rm_sp);
                      while (l-- && replen-->0)
                       { c= *s++;
                         if (r_l_upper) c=toupper(c);
                         if (r_l_lower) c=tolower(c);
                         if (add_repl_char(c))
                            goto r_r_error;
                       }
                    }
                    /*}}}  */
                   continue;
                 }
                /*}}}  */
              }
           }
           /*}}}  */
          default:
           /*{{{  in pattern replace, } marks the end of the string!*/
             if (c=='}' && complex)
              { r_r_exp_ptr++;
                /*{{{  maybe pad the string*/
                if (pad)
                   while (replen)
                      if (replen && (replen--,add_repl_char(r_r_added)))
                         goto r_r_error;
                /*}}}  */

                return(r_r_buff_ptr);
              }
           /*}}}  */
           /*{{{  add char*/
             if (replen && (replen--,add_repl_char(c)))
                goto r_r_error;
             r_r_exp_ptr++;
             continue;
           /*}}}  */
        }
       break;
     }
  r_r_error:
    message(get_msg(M_REG_PAT,r_start_err));
    return(0);
  }
  /*}}}  */

  private int reg_repl
   ( unsigned char const * const rep,
     unsigned char *buff,
     unsigned char const * const old
   )
  {
    r_r_exp_ptr=(unsigned char*)rep;
    r_r_old_ptr=old;
    r_r_buff_ptr=buff;
    r_r_buflen=LINELEN;
    buff=rec_reg_repl(LINELEN+1,False,False);

    return(buff?(matches[0].rm_ep-matches[0].rm_sp):-1);
  }
  /*}}}  */
  /*}}}  */
#endif
/*{{{  get_search*/
public int get_search(boolean init,boolean full_action,boolean inverse)
{
  unsigned char s[LINELEN+1];
  int result=0;

#ifdef REGEXP
  inverted_search=False;
  if (init)
     goto initialized;
#endif
  s_readprompt(s,get_msg(M_SEARCH),LINELEN,search_history);
  for (;;)
   { if (aborted)
      { *item_to_look_for='\0';break; }
     else
      { ustrcpy(item_to_look_for,s);
#       ifdef REGEXP
initialized:
           if (reg_type!=no_reg && reg_comp(full_action,inverse) && !aborted)
            { if (init)
                 result=r_c_e;
              else
               /*{{{  ask again and continue*/
               { readprompt
                  ( s,
                    get_msg(M_REG_EXP,REG_ERR_TAG(r_c_e)),
                    LINELEN,
                    search_history,
                    item_to_look_for,
                    REG_ERR_OFF(*ppreg)
                  );
                 continue;
               }
               /*}}}  */
            }
#       endif
      }
    break;
   }
  return(result);
}
/*}}}  */
/*{{{  strsub*/
public unsigned char *strsub(unsigned char const *s,int off)
{
  while (--off>0 && *s) s++;                 /* skip up to off leading chars */
  return((unsigned char*)s);
}
/*}}}  */
/*{{{  proc_replace*/
public int proc_replace
            ( int ls,
              unsigned char const * const replace,
              unsigned char *line,
              int position,
              int length
            )
{ unsigned char repl_string[LINELEN + 1];
  int lr,ll;

# ifdef REGEXP
     if (reg_type!=no_reg)
      { if ((ls=reg_repl(replace,repl_string,line+position))<0)
           return(-1);
      }
     else
# endif
        ustrcpy(repl_string,replace);
  ll=ustrlen(line);
  if (ll < position + ls - 1)
     return(0);
  lr=ustrlen(repl_string);
  if (length-ll-lr+ls>=0)
   { ustrcat(repl_string,line+(position+ls));
     ustrcpy(line+position,repl_string);
     trailing_spaces(line);
     return(lr);
   }
  msg_message(M_LONG_LINE);
  return(-1);
}
/*}}}  */
/*{{{  do_file_c*/
public char *do_file_c(char *f)
{
  if ((f=filec(f)))
     push_str_macro((unsigned char*)f);
  else
     f=(char*)empty_text;
  ocl_var[var_ocl_arg].v=strlen(f);

  return(f);
}
/*}}}  */
