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

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

#define PROMPT_C
#define I_LOOP_C
#define I_DISPLAY_C
#define I_GETMSG_C
#define I_GETTK_C
#define I_FIELDEDIT_C
#define I_FILEC_C
#define I_FOLDHELP_C
#define I_INIT_C
#define I_KEYBOARD_C
#define I_KEYTAB_C
#define I_MAIN_C
#define I_MESSAGES_C
#define I_MISC_C
#define I_ORIEDT_C
#define I_SCREEN_C
#define I_STRING_C
#define I_SET_C
#define I_VIRTUAL_C

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

/*{{{  variables*/
private boolean status = True;
public unsigned char const *menu_string=0;
public int menu_current=0;
public int prompt_shift_size;
/*}}}  */

/*{{{  history*/
/*{{{  history type*/
typedef struct History
 { struct History *next;
   histories id;
   int size;
   int used;
   unsigned char **text;
 } History;
/*}}}  */
/*{{{  variables*/
private History history_data[]=
 { { &(history_data[1]),search_history,0,0,0 },
   { &(history_data[2]),replace_history,0,0,0 },
   { &(history_data[3]),misc_history,0,0,0 },
   { &(history_data[4]),file_history,0,0,0 },
   { &(history_data[5]),error_history,0,0,0 },
   { &(history_data[6]),arg_history,0,0,0 },
   { &(history_data[7]),match_history,0,0,0 },
   { &(history_data[8]),nomatch_history,0,0,0 },
   { &(history_data[0]),shell_history,0,0,0 }
 };
private History *current_history= &(history_data[0]);
public histories overwrite_history_id=default_history;
/*}}}  */

/*{{{  switch_history        change current history, maybe create new one*/
public boolean switch_history(histories h)
{ History *x;

  /*{{{  maybe overwrite called history*/
  if (overwrite_history_id>default_history)
     h=overwrite_history_id;
  /*}}}  */
  if (h==no_history)
     return(False);
  /*{{{  return existing history*/
  for (x=current_history;;)
     if (x->id==h)
      { current_history=x;
        return(True);
      }
     else if (x->next==current_history)
        break;
     else
        x=x->next;
  /*}}}  */
  /*{{{  create new history*/
  if (!(x=paket_malloc(sizeof(History))))
     return(False);
  x->id=h;
  x->size=0;
  x->used=0;
  x->text=0;
  x->next=current_history->next;
  current_history->next=x;
  current_history=x;
  return(True);
  /*}}}  */
}
/*}}}  */
/*{{{  access_history        get correct index/text for a history*/
public void access_history
 ( int ind,
   histories h,
   boolean wrap_around,
   int * const p_ind,
   unsigned char ** const p_text
 )
{
  if (switch_history(h))
   /*{{{  set ind to value, inside of the used range*/
     if (wrap_around)
      { while (ind<START_HISTORY_INDEX) ind+=current_history->used+1;
        while (ind>=current_history->used) ind-=current_history->used+1;
      }
     else
      { if (ind<START_HISTORY_INDEX) ind=START_HISTORY_INDEX;
        if (ind>=current_history->used) ind=current_history->used-1;
      }
   /*}}}  */
  else
   /*{{{  use empty start*/
     ind=START_HISTORY_INDEX;
   /*}}}  */
  /*{{{  set return values*/
  if (p_ind)
     *p_ind=ind;
  if (p_text)
     *p_text=
        (ind==START_HISTORY_INDEX)
         ? (unsigned char*)empty_text
         : current_history->text[ind];
  /*}}}  */
}
/*}}}  */
/*{{{  grow_history          changes size of the given historylist*/
private void grow_history(histories h,int size)
{
  /*{{{  check arguments*/
  if (!switch_history(h)) return;
  if (size<=0 && current_history->size<=HISTORY_SIZE) return;
  if (size<HISTORY_SIZE) size=HISTORY_SIZE;
  if (size>HISTORY_MAX) size=HISTORY_MAX;
  if (size==current_history->size) return;
  /*}}}  */
  /*{{{  maybe free history entries*/
  { int i;

    if ((i=current_history->used-size)>0)
     { unsigned char **s;

       s=current_history->text+size;
       while (i>0)
        { paket_free(*s);current_history->used--;s++;i--; }
     }
  }
  /*}}}  */
  /*{{{  try to change the list*/
  { unsigned char **new_text;

    new_text=paket_malloc(size*sizeof(char*));
    if (new_text)
     { int new_used;
       unsigned char **s;
       unsigned char **d;
       int i;

       new_used=0;
       /*{{{  copy old to new*/
       if ((i=current_history->used)>0)
        { s=current_history->text;
          d=new_text;
          while (i-->0) { *d++= *s++;new_used++; }
        }
       /*}}}  */
       /*{{{  update history struct*/
       if (current_history->size)
          paket_free(current_history->text);
       current_history->text=new_text;
       current_history->size=size;
       current_history->used=new_used;
       /*}}}  */
     }
  }
  /*}}}  */
}
/*}}}  */
/*{{{  shrink_history        reduce size of list to minimum*/
public void shrink_history(void)
{ History *x,*y;

  x=y=current_history;
  do grow_history(x->id,-1); while ((x=x->next)!=y);
}
/*}}}  */
/*{{{  add_history           store text in a history list*/
public void add_history(histories const h,unsigned char const * const text)
{
  if (!switch_history(h)) return;
  /*{{{  maybe extend history*/
  if (current_history->size==0 || current_history->size==current_history->used)
     grow_history(h,HISTORY_SIZE+current_history->size);
  /*}}}  */
  if (current_history->size)
   { unsigned char *tp;

     if ((tp=ori_malloc(ustrlen(text)+1)))
      /*{{{  add the text*/
      { int i;

        /*{{{  maybe remove oldest entry*/
        if (current_history->size==current_history->used)
         { current_history->used-=1;
           paket_free(current_history->text[current_history->used]);
         }
        /*}}}  */
        /*{{{  shift old entries*/
        i=current_history->used;
        while (i>0)
         { current_history->text[i]=current_history->text[i-1];
           i--;
         }
        /*}}}  */
        ustrcpy((current_history->text[0]=tp),text);
        current_history->used+=1;
      }
      /*}}}  */
   }
}
/*}}}  */
/*}}}  */

/*{{{  message               put a message under statusline*/
public void message(unsigned char const * const s)
{
  enum dsp_size old_mode;

  ocl_msg((char*)s,0);
  old_mode=dsp.norm;
  dsp.norm=norm_dsp;
  moveclreol(message_line,1);
  move_cursor_to(message_line,1);
  if (prt_bin_text(0,screen.w-(am?1:0),False,(unsigned char*)s))
     status=True;
  if (*s)
     add_history(error_history,s);
  oflush;
  dsp.norm=old_mode;
}
/*}}}  */
/*{{{  msg_message*/
public void msg_message(msgtyp msg)
 {
   message(get_msg(msg));
 }
/*}}}  */
/*{{{  verbose_msg_message*/
public void verbose_msg_message(msgtyp msg)
 { unsigned char const *s;

   s=get_msg(msg);
   if (verbose)
      message(s);
 }
/*}}}  */
/*{{{  err_message           put a message under statusline with file and error*/
public void err_message(msgtyp m,unsigned char const * const p)
{
  message(get_msg(m,p,strerror(errno)));
  if (!scr_off)
     sleep(1);
}
/*}}}  */
/*{{{  warn_message          put a message and maybe switch on screen*/
public void warn_message(unsigned char const * const s)
{
  int i;

  for (i=0;scr_off;ocl_screen_on(),i++);
  if (verbose)
   { bell_audible();
     bell_visible();
   }
  message(s);
  warn_delay();
  for (;i;ocl_screen_off(),i--);
}
/*}}}  */
/*{{{  no_message            remove message*/
public void no_message(void)
{
  if (status)
   { status = False;
     moveclreol(message_line,1);
   }
}
/*}}}  */
/*{{{  readprompt            read a string with prompt, full editing*/
public void readprompt
 ( unsigned char * const Result,
   unsigned char const * const prompt,
   int const max,
   histories const history,
   unsigned char const * const start_pointer,
   int start_offset
 )
{
  /*{{{  variables*/
  /*{{{  edit buffer*/
#  define R_P_SIZE (2*LINELEN)
  unsigned char txt_buff[R_P_SIZE];
  unsigned char *txt;
  unsigned char *gap_start;
  unsigned char *gap_end;
  int length;
  /*}}}  */
  /*{{{  dsp data*/
  boolean dsp_full;
  boolean new_shift;
  int dsp_shift;
  int c_pos;
  /*}}}  */
  TOKEN ch;
  int hist_index=START_HISTORY_INDEX;
  /*}}}  */

  status=True;
  /*{{{  init the loop*/
  /*{{{  txt_buffer*/
  ustrcpy(txt_buff,prompt);
  ustrcat(txt_buff,question);
  for (txt=txt_buff;*txt;txt++);
  /*}}}  */
  /*{{{  edit data*/
  *(gap_start=txt)='\0';
  *(gap_end=txt_buff+R_P_SIZE-1)=0;
  if (start_pointer)
   { length=ustrlen(start_pointer);
     gap_end-=length;
     ustrcpy(gap_end,start_pointer);
     while (start_offset-->=0 && (*gap_start= *gap_end))
      { gap_end++;gap_start++; }
     *gap_start='\0';
   }
  else
     length=0;
  /*}}}  */
  /*{{{  dsp data*/
  dsp_full=True;
  new_shift=True;
  /*}}}  */
  /*}}}  */
  do
   /*{{{  edit loop*/
   {
     /*{{{  show line and cursor*/
     if (dsp_full)
      /*{{{  show whole prompt/string*/
      { enum dsp_size old_mode;

        old_mode=dsp.norm;
        dsp.norm=norm_dsp;
        if (new_shift)
         /*{{{  get new shift offset*/
         { ocl_screen_off();
           for (dsp_shift=0,c_pos=0;;)
            { c_pos=prt_bin_text(0,screen.w,False,txt_buff+dsp_shift)+1;
              if (c_pos>=screen.w && c_pos>1)
               { int i=prompt_shift_size;

                 do
                  { i--;
                    dsp_shift++;
                  }
                 while (i && txt_buff[dsp_shift]);
               }
              else
                 break;
            }
           ocl_screen_on();
         }
         /*}}}  */
        /*{{{  clear line and set cursor*/
        moveclreol(message_line,1);
        move_cursor_to(message_line,1);
        c_pos=prt_bin_text(0,screen.w,True,txt_buff+dsp_shift)+1;
        /*}}}  */
        if (*gap_end) prt_bin_text(c_pos-1,screen.w-c_pos,True,gap_end);
        dsp_full=new_shift=False;
        dsp.norm=old_mode;
      }
      /*}}}  */
     move_cursor_to(message_line,c_pos);
     /*}}}  */
     ch=hide_key();
     if (ch<O_NOP)
      /*{{{  normal character*/
      { length++;
        *gap_start++=(unsigned char)ch;
        *gap_start='\0';
        switch (char_dsp_size((unsigned char)ch))
         { case norm_dsp:
           case ictrl_dsp:
            /*{{{  print, if not at right margin*/
              if (c_pos<screen.w-1)
               /*{{{  prt the char, is single char and fits on line*/
               { enum dsp_size old_mode;

                 old_mode=dsp.norm;
                 dsp.norm=norm_dsp;
                 prt_bin_text(c_pos,1,False,gap_start-1);
                 c_pos++;
                 if (*gap_end)
                    prt_bin_text(c_pos-1,screen.w-c_pos,True,gap_end);
                 dsp.norm=old_mode;
                 break;
               }
               /*}}}  */
            /*}}}  */
           default:
            /*{{{  mark dsp_full, maybe dsp_shift+*/
            { new_shift=(c_pos>=screen.w);
              dsp_full=True;
            }
            /*}}}  */
         }
        continue;
      }
      /*}}}  */
     else
      /*{{{  handle special commands*/
        switch(ch)
         {
           /*{{{  O_DELETE*/
           case O_DELETE:
              if (gap_start!=txt)
               { length--;
                 deleted_ch= *--gap_start;
                 *gap_start='\0';
                 break;
               }
              continue;
           /*}}}  */
           /*{{{  O_LEFT*/
           case O_LEFT:
              if (gap_start!=txt)
               { *--gap_end= *--gap_start;
                 *gap_start='\0';
                 break;
               }
              continue;
           /*}}}  */
           /*{{{  O_START_OF_LINE*/
           case O_START_OF_LINE:
              if (gap_start!=txt)
               { do *--gap_end= *--gap_start; while (gap_start!=txt);
                 *gap_start='\0';
                 break;
               }
              continue;
           /*}}}  */
           /*{{{  O_RIGHT*/
           case O_RIGHT:
              if (*gap_end)
               { *gap_start++= *gap_end++;
                 *gap_start='\0';
                 break;
               }
              continue;
           /*}}}  */
           /*{{{  O_FILEC*/
           case O_FILE_C:
              switch (*gap_end)
               { case '\0':
                 case ' ':
                 case '\t':
                  { char *f;

                    /*{{{  look for space or tab*/
                    { char *x;

                      for (f=x=(char*)txt;*x && x<(char*)gap_start;)
                         if (*x==' ' || *x=='\t')
                            f= ++x;
                         else
                            x++;
                    }
                    /*}}}  */
                    do_file_c(f);
                  }
                 default:
                    continue;
               }
              break;
           /*}}}  */
           /*{{{  O_END_OF_LINE*/
           case O_END_OF_LINE:
            { unsigned char c;

              if ((c= *gap_end))
               { do *gap_start++=c; while ((c= *++gap_end));
                 *gap_start='\0';
                 break;
               }
              continue;
            }
           /*}}}  */
           /*{{{  M_END_OF_LINE*/
           case M_END_OF_LINE:
              macro_tag=(*gap_end=='\0');
              continue;
           /*}}}  */
           /*{{{  M_BEGIN_OF_LINE*/
           case M_BEGIN_OF_LINE:
              macro_tag=(gap_start==txt);
              continue;
           /*}}}  */
           /*{{{  O_UP,O_DOWN*/
           case O_UP:
           case O_DOWN:
            { unsigned char *h_text;

              access_history
               ( hist_index+((ch==O_UP)?HISTORY_ADD_OLD:HISTORY_ADD_NEW),
                 history,True,&hist_index,&h_text
               );
              ustrcpy(txt,h_text);
              txt[max-1]='\0';
              length=ustrlen(txt);
              gap_start=txt+length;
              gap_end=txt_buff+R_P_SIZE-1;
              break;
            }
           /*}}}  */
           /*{{{  M_TEST_CHAR (LOW/HIGH/ ) _C*/
           case M_TEST_CHAR_SET:
           case M_TEST_CC:
           case M_TEST_L_CC:
           case M_TEST_H_CC:
           case M_TEST_CHAR_LOW:
           case M_TEST_CHAR_HIGH:
           case M_TEST_CHAR:
              do_cc_compare(*gap_end,ch);
              continue;
           /*}}}  */
           /*{{{  M_TEST_STR*/
           case M_TEST_STR:
              do_str_compare(gap_end);
              continue;
           /*}}}  */
           /*{{{  M_STORE_C*/
           case M_STORE_C:
             ocl_var[get_arg("store-char-var")].v= *gap_end;
             continue;
           /*}}}  */
           /*{{{  O_TOGGLE_CASE*/
           case O_TOGGLE_CASE:
            { unsigned char c;

              if ((c = *gap_end) && isalpha(c))
                 *gap_end=isupper(c)?tolower(c):toupper(c);
              break;
            }
           /*}}}  */
           default:
              continue;
         }
      /*}}}  */
     dsp_full=new_shift=True;
   }
   /*}}}  */
  while (length<max && ch!=O_RETURN && !aborted);
  if (aborted)
     *Result='\0';
  else
   /*{{{  return string and store in history*/
   { ustrcpy(Result,txt);
     ustrcat(Result,gap_end);
     if (*Result || history==replace_history)
        add_history(history,Result);
   }
   /*}}}  */
}
/*}}}  */
/*{{{  yes                   ask for boolean*/
public boolean yes(unsigned char const * const q)
{
# define YES_LG 8
  unsigned char temp[YES_LG+1];

  do
   { s_readprompt(temp,q,YES_LG,no_history);
     temp[0]=toupper(temp[0]);
     if (temp[0]==YES)
        return(True);
     else if (temp[0]==NO)
        break;
   }
  while (!aborted);

  return(False);
}
/*}}}  */
/*{{{  menu_item             display a menu string and get item number*/
public int menu_item
 ( unsigned char const * prompt,
   int start,
   boolean const direct,
   boolean const move_current
 )
{ while (*prompt==' ') prompt++;
  ori_assert(bd.scr.cur_shift_w<=LINELEN*max_dsp,"shift-check");
  title_op(UPDTITLE);
  status=True;
  for (;;)
   /*{{{  display and get input*/
   {
     /*{{{  variables*/
     int i;
     int max;
     int m_shift;
     TOKEN cmd;
     unsigned char *s;
     /*}}}  */

     /*{{{  print menu and get code*/
     /*{{{  check, if start to low*/
     if (start<0) start=0;
     /*}}}  */
     /*{{{  print menu line*/
     { int lg;

       for (m_shift=0;;m_shift++)
        { unsigned char c;

          moveclreol(message_line,1);
          move_cursor_to(message_line,1);
          s=(unsigned char*)prompt;
          /*{{{  skip leading entries, if to long*/
          if (m_shift)
           { i=m_shift;
             for (;i;i--)
              { while (*s && *s!=' ')
                   s++;
                while (*s==' ')
                   s++;
              }
           }
          /*}}}  */
          menu_string=s;
          if (m_shift && soln_str)
           { oputc(soln_str);
             menu_string--;
           }
          i=0;
          lg=screen.w-5;
          while ((c= *s++))
             if (c==' ')
              /*{{{  print spaces*/
              { if (lg)
                 { oputc(' ');
                   lg--;
                 }
              }
              /*}}}  */
             else
              /*{{{  print mark*/
              { boolean cur;

                cur=(i==start-m_shift) && lg;
                /*{{{  maybe mark current*/
                if (cur)
                   if (move_current && so && !sg)
                      do_standout();
                /*}}}  */
                /*{{{  print leading space decode*/
                if (c==(unsigned char)SPACE_CHAR)
                 { unsigned char *sp=(unsigned char*)SPACE;

                   while (lg && *sp)
                    { oputc(*sp);
                      sp++;
                      lg--;
                    }
                   c= *s++;
                 }
                /*}}}  */
                /*{{{  print body of tag*/
                while (c!='\0' && c!=' ')
                 { if (lg)
                    { oputc(c);
                      lg--;
                    }
                   c= *s++;
                 }
                /*}}}  */
                /*{{{  maybe end mark current*/
                if (move_current)
                   if (so && !sg)
                      do_standend();
                   else if (lg)
                    { oputc(CURR_MARK[0]);
                      lg--;
                    }
                /*}}}  */
                s-=1;
                i++;
              }
              /*}}}  */
          if (lg==0)
             oputc(eoln_str);
          oputs((unsigned char*)question);
          if (m_shift==0)
             max=i;
          if (lg || (start==m_shift) || !move_current)
             break;
        }
     }
     /*}}}  */
     /*{{{  check, if start to high*/
     if (start>=max)
      { start=max-1;
        continue;
      }
     /*}}}  */
     i=start;
     /*{{{  set current adress for mouse handling*/
     if (move_current && !(so && !sg))
        menu_current=start-m_shift;
     else
        menu_current= -1;
     /*}}}  */
     if (direct)
      /*{{{  read the key direct from keyboard*/
      { disable_abort();
        enable_echo(False);
        /* if aborted is true before get_key is called, then ... :-( */
        cmd=get_key();
        enable_echo(True);
        enable_abort();
      }
      /*}}}  */
     else
      /*{{{  hide_key*/
        cmd=hide_key();
      /*}}}  */
     menu_string=0;
     /*}}}  */
     /*{{{  abort returns -1*/
     if (aborted)
      { start= -1;
        break;
      }
     /*}}}  */
     /*{{{  maybe handle left/right*/
     if (move_current)
        if (cmd==O_LEFT)
         { start=--i;
           continue;
         }
        else if (cmd==O_RIGHT)
         { if (i<max-1)
              start=++i;
           continue;
         }
     /*}}}  */
     if (cmd<O_NOP)
      /*{{{  look in list for corresponding entry*/
      { unsigned char ch;

        for(ch=toupper((unsigned char)cmd),s=(unsigned char*)prompt,i=0;*s;)
         /*{{{  handle items*/
         { if (*s==' ')
            /*{{{  skip gap*/
              while (*(++s)==' ');
            /*}}}  */
           else if (*s==(unsigned char)SPACE_CHAR && ch==' ')
            /*{{{  hit correct space -> ret*/
              return(i);
            /*}}}  */
           else if (ch==toupper(*s))
            /*{{{  hit correct non-space -> ret*/
              return(i);
            /*}}}  */
           else
            /*{{{  skip item*/
            { while (*(++s) && *s!=' ');
              i++;
            }
            /*}}}  */
           continue;
         }
         /*}}}  */
        continue;
      }
      /*}}}  */
     else if (cmd==O_RETURN)
        break;
   }
   /*}}}  */
  return(start);
}
/*}}}  */
/*{{{  show_string           display string (with \n) on screen*/
public boolean show_string(unsigned char const *s,boolean const clr)
{
  if (s && *s)
   /*{{{  display it, using the whole screen*/
   { int x,y;
     unsigned char c;
     enum dsp_size old_mode;

     old_mode=dsp.norm;
     dsp.norm=norm_dsp;
     for (y=0,x=INT_MAX;;)
      /*{{{  show whole string*/
      {
        /*{{{  maybe line break*/
        if (x>screen.w)
           if (++y==screen.h)
              break;
           else
              move_cursor_to(y,x=1);
        /*}}}  */
        switch ((c=(s?*s++:' ')))
         {
           /*{{{  \n -> line break*/
           case '\n':
              moveclreol(y,x);
              x=INT_MAX;
              continue;
           /*}}}  */
           /*{{{  print*/
           case '\0':
              if (!clr || y>screen.h)
                 break;
              s=0;
           case '\t':
              c=' ';
           default:
              oputc(c);
              x++;
              continue;
           /*}}}  */
         }
        break;
      }
      /*}}}  */
     oflush;
     dsp.norm=old_mode;
     return(True);
   }
   /*}}}  */
  return(False);
}
/*}}}  */
/*{{{  help                  display help or binding*/
/*{{{  mark strings for rc-help*/
static char alias[REF_TAG_LG+1];
static char numb[REF_TAG_LG+1];
static char ab[REF_TAG_LG+1];
#ifdef MOUSY
   static char mouse[REF_TAG_LG+1];
#endif
static char const * const mark_field[]=
{ alias,
  numb,
  ab,
#ifdef MOUSY
  mouse,
#endif
  0
};
#define marks (mark_field+1)
#ifdef MOUSY
#  define end_marks (mark_field+4)
#else
#  define end_marks (mark_field+3)
#endif
/*}}}  */
/*{{{  buffer for line wrap*/
private char help_cut_buff[RC_HELP_LEN+1];
/*}}}  */
/*{{{  get_help_line*/
private char *get_help_line
 ( char * const buff,
   FILE * const f,
   const boolean tag_test,
   const unsigned char *pattern,
   const unsigned char *skip,
   int * const skipped
 )
 {
   /*{{{  get line*/
   if (help_cut_buff[0])
    /*{{{  use tail of last line*/
      strcpy(buff,help_cut_buff);
    /*}}}  */
   else
    /*{{{  get new line*/
    { char const * const *tags;

      for (tags=(char const*const*)(tag_test?marks:0);;)
       { char const * const *cmp_tag_list;
         char *b;

         if (!fgets((b=help_cut_buff),RC_HELP_LEN,f))
          /*{{{  nothing found, return 0*/
          { help_cut_buff[0]='\0';
            return(0);
          }
          /*}}}  */
         if ((cmp_tag_list=tags))
          /*{{{  next one, if tags do not match this line*/
          { while (*cmp_tag_list)
             { const char *cur_cmp_tag= *(cmp_tag_list++);

               if (*cur_cmp_tag)
                /*{{{  compare line and not empty tag*/
                { char *d=b;
#                 ifdef MOUSY
                     boolean change=(cur_cmp_tag==mouse);
#                 endif

                  while (*cur_cmp_tag && *cur_cmp_tag==*d)
                   { cur_cmp_tag++;
                     d++;
                   }
                  if (*cur_cmp_tag=='\0')
                     if (*d==REF_NODE_LIMITER)
                      /*{{{  prepare string for return*/
                      { b=d+1;
                        ref_uncompress_text(b,buff);
#                       ifdef MOUSY
                           if (change)
                            /*{{{  decode the mouse buttonnumber to a name*/
                            { char *m_s;

                              /*{{{  move to end of string*/
                              for (m_s=buff;*m_s>=' ';m_s++);
                              /*}}}  */
                              /*{{{  decode mouse button*/
                              { unsigned int c;

                                c=*--m_s-REF_COUNT_BASE;
                                strcpy
                                 ( m_s,
                                   (mouse_b_count[use_mouse]>c)
                                     ? mouse_b_names[use_mouse][c]
                                     : (char*)i_to_ua(c)
                                 );
                              }
                              /*}}}  */
                            }
                            /*}}}  */
#                       endif
                        b=buff;
                        cmp_tag_list--;
                        break;
                      }
                      /*}}}  */
                     else if (*d==REF_NODE_ALIAS)
                      /*{{{  use alias field too and set to pointed value*/
                      { unsigned char *s;

                        /*{{{  cp alias to alias-part in marks (without \r \n)*/
                        for
                         ( s=(unsigned char*)d+1,
                           d=alias
                         ; (*((unsigned char*)d++)= *s++)>=REF_NODE_LIMITER
                         ;
                         );
                        d[-1]='\0';
                        /*}}}  */
                        tags=(char const*const*)mark_field;
                        cmp_tag_list=end_marks;
                      }
                      /*}}}  */
                }
                /*}}}  */
             }
            if (!*cmp_tag_list)
               continue;
          }
          /*}}}  */
         if (pattern && !ustrstr((unsigned char*)b,pattern))
            goto next_try;
         if (skip && !ustrstr((unsigned char*)b,skip))
          { if (skipped)
               (*skipped)++;
         next_try:
            if (tags==(char const*const*)mark_field)
               tags++;
            continue;
          }
         if (!cmp_tag_list)
            strcpy(buff,help_cut_buff);
         break;
       }
      /*{{{  cut at newline/cr*/
      { char *d;

        for (d=buff;;d++)
         { switch (*d)
            { case '\r':
              case '\n':
                 *d='\0';
              case '\0':
                 break;
              default:
                 continue;
            }
           break;
         }
      }
      /*}}}  */
    }
    /*}}}  */
   /*}}}  */
   /*{{{  maybe store tail of long line/splitted line part*/
   if (strlen(buff)>(bd.scr.txt_size.w-1))
    { strcpy(help_cut_buff,buff+(bd.scr.txt_size.w-1));
      buff[bd.scr.txt_size.w-1]='\0';
    }
   else
      help_cut_buff[0]='\0';
   /*}}}  */

   return(buff);
 }
/*}}}  */
/*{{{  help_seek*/
#define help_seek(f,o) (help_cut_buff[0]='\0',fseek((f),(o),SEEK_SET))
/*}}}  */

public void help(boolean show_help)
{
  /*{{{  local variables*/
  FILE *fp;
  boolean help_as_rc=False;
  boolean displayed=False;
  boolean complain=True;
  off_t offset;
  unsigned char pat_buff[LINELEN+1];
  const unsigned char *pattern=0;
  unsigned char skip_buff[LINELEN+1];
  const unsigned char *skip=0;
  int shown_lines=0;
  boolean killing=False;
  unsigned char help_prompt[MSG_LENGTH+1];
  /*}}}  */

  /*{{{  get the prompt*/
  ustrcpy(help_prompt,get_msg(M_ANY_Q));
  last_message=M_BASE_FORMAT;
  /*}}}  */
  /*{{{  open the file*/
  if (show_help)
   { char *sysfile;

     if (ocl_var[var_mod_beh].v<0)
        ocl_var[var_mod_beh].v=0;
     sysfile=ocl_var[var_mod_beh].v?0:open_sysfile(M_HELPSTR,(char*)0);
     if (!sysfile ||  !*sysfile || !(fp=fopen(sysfile,(char*)"rb")))
      { int err_no;

        err_no=errno;
        fp=rcfile
            ? rcfile
            : (rcfile=fopen(open_sysfile(M_RCSTR,(char*)0),(char*)"rb"));
        errno=err_no;
        help_as_rc=True;
      }
   }
  else
     fp=fopen(open_sysfile(M_RCSTR,(char*)0),(char*)"rb");
  /*}}}  */
  /*{{{  set filter marks*/
  if (!show_help)
   /*{{{  set leading marks for bindinglist*/
   { ref_code_tag(numb,bind_mark[0]);
     ref_code_tag(ab,abort_kbd);
#    ifdef MOUSY
        if (use_mouse==no_mouse)
           mouse[0]='\0';
        else
           ref_code_tag(mouse,mouse_kbd);
#    endif
     offset=helpofs;
   }
   /*}}}  */
  else
   /*{{{  clear or set marks for help*/
     if (help_as_rc)
      { show_help=False;
#   ifdef MOUSY
        mouse[0]=
#   endif
        ab[0]='\0';
        /*{{{  get correct seek offset*/
        { static off_t ref_class_ofs=0;
          static off_t ref_file_ofs=0;
          off_t x;
          int id;

          /*{{{  get limiter help id, maybe set no-complain, set offset*/
          if (ocl_var[var_mod_beh].v)
           { id=ref_class_id;
             complain=False;
             x=ref_class_ofs;
             offset=ref_file_ofs?ref_file_ofs:helpofs;
           }
          else
           { id=ref_file_id;
             x=ref_file_ofs;
             offset=helpofs;
           }
          /*}}}  */
          if (!x)
           /*{{{  try to get seek offset, using kbd-0 tag field*/
           { if (-1!=help_seek(fp,(off_t)offset))
              { char line[RC_HELP_LEN+1];

                ref_code_tag(numb,ref_kbd-id);
                if
                 (    !get_help_line(line,fp,True,pattern,(const unsigned char *)0,(int*)0)
                   || !(x=ftell(fp))
                 )
                   x=helpofs;
                if (complain)
                   ref_file_ofs=x;
                else
                   ref_class_ofs=x;
              }
           }
           /*}}}  */
          offset=x;
        }
        /*}}}  */
        ref_code_tag(numb,ref_kbd-ocl_var[var_mod_beh].v);
      }
     else
        offset=ftell(fp);
   /*}}}  */
  /*}}}  */
  /*{{{  init compression data*/
  if (!show_help && !(fseek(fp,compofs,SEEK_SET)))
     ref_read_comp_data(fp);
  /*}}}  */
  /*{{{  show-loop*/
  foo:
  if (fp)
   {
     /*{{{  variables*/
     char line[RC_HELP_LEN+1];
     int li;
     int cmd;
     /*}}}  */

     if (-1!=help_seek(fp,(off_t)offset))
      { if (killing)
         /*{{{  kill whole view*/
         { warn_message(get_msg(M_KILLING));
           bd.scr.txt_size.w+=RC_HELP_LEN;
           for
            (
            ; get_help_line(line,fp,!show_help,pattern,(const unsigned char*)0,(int*)0)
            ;
            )
            { element *p;

              p=proc_new_element();
              set_data(p,(unsigned char const *)line,True);
              append_to_pick(p);
            }
           bd.scr.txt_size.w-=RC_HELP_LEN;
           killing=False;
           goto foo;
         }
         /*}}}  */
        /*{{{  overread leading text*/
        for
        (
          li=shown_lines;
          li && get_help_line(line,fp,!show_help,pattern,(const unsigned char *)0,(int*)0);
          li--
        );
        /*}}}  */
        for (;!aborted;)
         /*{{{  show screen and prompt*/
         { li=0;
           /*{{{  show one screen*/
           { enum dsp_size old_mode;

             old_mode=dsp.norm;
             dsp.norm=norm_dsp;
             /*{{{  show lines*/
             { char *s;

               while
                (    (li<bd.scr.txt_size.h)
                  && (s=get_help_line
                         ( line,
                           fp,
                           !show_help,
                           pattern,
                           skip,skip?&shown_lines:(int*)0)
                         )
                )
                { skip=0;
                  displayed=True;
                  shown_lines++;
                  li++;
                  move_prt_clreol(li,1,0,bd.scr.txt_size.w-1,True,(unsigned char*)s);
                  oflush;
                }
             }
             /*}}}  */
             /*{{{  maybe clear to end of screen*/
             { int x;

               if ((x=li)) while (x<bd.scr.txt_size.h) moveclreol(++x,1);
             }
             /*}}}  */
             dsp.norm=old_mode;
           }
           /*}}}  */
           if (li)
            /*{{{  prompt for key*/
            { ori_assert(bd.scr.cur_shift_w<=LINELEN*max_dsp,"shift-check");
              title_op(PRTTITLE);
              last_message=M_ANY_Q;
              cmd=1;
              cmd=menu_item(help_prompt,cmd,False,False);
              switch (cmd)
               {
                 case 0:
                  /*{{{  next page*/
                    break;
                  /*}}}  */
                 case 1:
                  /*{{{  next line*/
                    shown_lines-=bd.scr.txt_size.h;
                    shown_lines++;
                    if (shown_lines<0) shown_lines=0;
                    goto foo;
                  /*}}}  */
                 case 2:
                  /*{{{  page up*/
                    shown_lines-=(bd.scr.txt_size.h)<<1;
                    if (shown_lines<0) shown_lines=0;
                    goto foo;
                  /*}}}  */
                 case 3:
                  /*{{{  quit*/
                    goto bar;
                  /*}}}  */
                 case 4:
                  /*{{{  prompt for the filter*/
                    s_readprompt(pat_buff,(unsigned char*)"filter",LINELEN,misc_history);
                    if (aborted)
                       goto bar;
                    pattern=pat_buff[0]?(const unsigned char*)pat_buff:0;
                    shown_lines=0;
                    goto foo;
                  /*}}}  */
                 case 5:
                  /*{{{  prompt for search pattern*/
                    s_readprompt(skip_buff,get_msg(M_SEARCH),LINELEN,misc_history);
                    if (aborted)
                       goto bar;
                    shown_lines-=(bd.scr.txt_size.h-1);
                    if (shown_lines<0)
                       shown_lines=1;
                    skip=skip_buff[0]?(const unsigned char*)skip_buff:0;
                    goto foo;
                  /*}}}  */
                 case 6:
                  /*{{{  kill all text*/
                    killing=True;
                    shown_lines-=bd.scr.txt_size.h;
                    goto foo;
                  /*}}}  */
               }

            }
            /*}}}  */
           else
              goto bar;
         }
         /*}}}  */
      }
     bar:
     /*{{{  check closing the helpfile*/
     if (fp!=rcfile && fclose(fp)<0)
        err_message(M_CLOSE_FAILED,empty_text);
     /*}}}  */
   }
  no_message();
  /*{{{  maybe there was no ref in the rc-file, so complain then*/
  if (!displayed && complain)
   { show_string(get_copyright(True),True);
     move_cursor_to(screen.h,1);
     hide_key();
   }
  /*}}}  */
  /*}}}  */
  /*{{{  free internal data*/
  ref_free_comp_data();
  /*}}}  */
}
/*}}}  */
