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

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

#define DISPLAY_C
#define I_BUFFLOOP_C
#define I_FIELDEDIT_C
#define I_FINDS_C
#define I_FOLDHELP_C
#define I_FOLDING_C
#define I_GETTK_C
#define I_GETMSG_C
#define I_KEYBOARD_C
#define I_LOOP_C
#define I_MISC_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 <h/envvar_str.h>
/*}}}  */

/*{{{  variables*/
public unsigned char *u_modes[MODE_COUNT*2];
public boolean um_active[MODE_COUNT];
public int n_modes[MODE_COUNT];
public unsigned char print_buffer[BUFFERLEN+1];
public int area_used=0;
public win_data area_line;
public int message_line;
public dsp_data dsp=
 { 8,        /* tab size: default is standard width 8 */
   hex_dsp,  /* base: base for number-display */
   mark_dsp, /* ctrl: marked display */
   norm_dsp, /* norm: ascii output for normal chars */
   norm_dsp, /* tilde: as normal char */
   norm_dsp, /* high: -bit as ascii */
   0,        /* ctrl_add: 0 -> marked display ^A */
 };
public boolean dirty;
public unsigned char *w_name=0;
public boolean xy_show=False;
public boolean allow_text_so=True;
private unsigned char **area_lines=0;
private boolean highlighted;
/*}}}  */

/*{{{  on_screen*/
public int on_screen(element const * const p)
{
  element *q;
  int line;

  for (q=bd.scr.start,line=0;;)
   { if (++line>bd.scr.txt_size.h)
        break;
     else if (p==q)
        return(line);
     else if (q==bd.scr.end)
        break;
     else
        q=q->next;
   }
  return(0);
}
/*}}}  */
/*{{{  chg_dsp_type*/
public void chg_dsp_type(int code)
 {
   if (code<=0)
    { dsp.tab_size= -code;
      if (dsp.tab_size>LINELEN/8)
         dsp.tab_size=0;
    }
   else
      switch (code)
       {
         /*{{{  switch bases*/
         case 'o':
            dsp.base=oct_dsp;
            goto switch_base;
         case 'd':
            dsp.base=dec_dsp;
            goto switch_base;
         case 'h':
            dsp.base=hex_dsp;
         switch_base:
            if (numb_dsp(dsp.ctrl))
               dsp.ctrl=dsp.base;
            if (numb_dsp(dsp.norm))
               dsp.norm=dsp.base;
            if (numb_dsp(dsp.tilde))
               dsp.tilde=dsp.base;
            if (numb_dsp(dsp.high))
               dsp.high=dsp.base;
            break;
         /*}}}  */
         /*{{{  control handling*/
         case 'm':
            dsp.ctrl=mark_dsp;
            dsp.ctrl_add=0;
            break;
         case 'i':
            if (allow_text_so)
             { dsp.ctrl=ictrl_dsp;
               dsp.ctrl_add= -1;
             }
            break;
         case 'n':
            dsp.ctrl=dsp.base;
            dsp.ctrl_add= -1;
            break;
         /*}}}  */
         /*{{{  number select*/
         case 'p':
            dsp.norm=dsp.tilde=dsp.high=norm_dsp;
         case '8':
            dsp.norm=dsp.tilde=norm_dsp;
            dsp.high=dsp.base;
            break;
         case 'a':
            dsp.ctrl=dsp.norm=dsp.tilde=dsp.high=dsp.base;
            break;
         /*}}}  */
         default:
            break;
       }
   if (hz)
      dsp.tilde=dsp.ctrl;
   /*{{{  check missing features*/
   if (!eight_bits && dsp.high==norm_dsp)
      dsp.high=dsp.base;
   if (!allow_text_so && dsp.ctrl==ictrl_dsp)
    { dsp.ctrl=mark_dsp;
      dsp.ctrl_add=0;
    }
   /*}}}  */
   set_fold_mark_dsp_length(&(init_buffer.f.str));
 }
/*}}}  */
/*{{{  char_dsp_size*/
enum dsp_size char_dsp_size(unsigned char const c)
 {
   if (c==(unsigned char)'~')
      return(dsp.tilde);
   if ((unsigned int)c<(unsigned int)' ')
      if (c==(unsigned char)'\t' && dsp.tab_size && !numb_dsp(dsp.norm))
         return(tab_dsp);
      else
         return(dsp.ctrl);
   if ((unsigned int)c<(unsigned int)127)
      return(dsp.norm);
   else if (c==(unsigned char)127)
      return(dsp.ctrl);
   return(dsp.high);
 }
/*}}}  */
/*{{{  txt2scr*/
public int txt2scr(int offset,int x,unsigned char const *s)
{ int i;

  for (i=1,s--;x;x--)
   { unsigned char c;

     switch ((c= *++s))
      { case '\0':
         /*{{{  string empty, add the missing moves and break from loop*/
           i+=x;
           break;
         /*}}}  */
        /*{{{  case [ a-zA-Z0-9]*/
        case ' ':
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
        case 'G':
        case 'H':
        case 'I':
        case 'J':
        case 'K':
        case 'L':
        case 'M':
        case 'N':
        case 'O':
        case 'P':
        case 'Q':
        case 'R':
        case 'S':
        case 'T':
        case 'U':
        case 'V':
        case 'W':
        case 'X':
        case 'Y':
        case 'Z':
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'g':
        case 'h':
        case 'i':
        case 'j':
        case 'k':
        case 'l':
        case 'm':
        case 'n':
        case 'o':
        case 'p':
        case 'q':
        case 'r':
        case 's':
        case 't':
        case 'u':
        case 'v':
        case 'w':
        case 'x':
        case 'y':
        case 'z':
        /*}}}  */
         /*{{{  handle some norm here*/
           i+=dsp.norm;
           continue;
         /*}}}  */
        default:
         /*{{{  add number of needed chars*/
         { enum dsp_size lg;

           switch ((lg=CHAR_DSP_SIZE(c)))
            { case tab_dsp:
                 i=((offset+i+dsp.tab_size-1)/dsp.tab_size)*dsp.tab_size-offset+1;
                 break;
              case ictrl_dsp:
                 lg=1;
              default:
                 i+=lg;
                 break;
            }
           continue;
         }
         /*}}}  */
      }
     break;
   }
  return(i);
}
/*}}}  */
/*{{{  scr2txt*/
public int scr2txt(int offset,int x,unsigned char const *s)
{ int i,is;

  for (is=1,i=0,s-=1;is<x;i++)
   { unsigned char c;

     switch ((c= *++s))
      { case '\0':
         /*{{{  string empty, add missing moves and break from loop*/
           i+=(x-is)/dsp.norm;
           break;
         /*}}}  */
        /*{{{  case [ a-zA-Z0-9]*/
        case ' ':
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
        case 'G':
        case 'H':
        case 'I':
        case 'J':
        case 'K':
        case 'L':
        case 'M':
        case 'N':
        case 'O':
        case 'P':
        case 'Q':
        case 'R':
        case 'S':
        case 'T':
        case 'U':
        case 'V':
        case 'W':
        case 'X':
        case 'Y':
        case 'Z':
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'g':
        case 'h':
        case 'i':
        case 'j':
        case 'k':
        case 'l':
        case 'm':
        case 'n':
        case 'o':
        case 'p':
        case 'q':
        case 'r':
        case 's':
        case 't':
        case 'u':
        case 'v':
        case 'w':
        case 'x':
        case 'y':
        case 'z':
        /*}}}  */
         /*{{{  handle some norm here*/
           is+=dsp.norm;
           continue;
         /*}}}  */
        default:
         /*{{{  handle display length*/
         { enum dsp_size lg;

           switch ((lg=CHAR_DSP_SIZE(c)))
            { case tab_dsp:
                 is=((offset+is+dsp.tab_size-1)/dsp.tab_size)*dsp.tab_size-offset+1;
                 break;
              case ictrl_dsp:
                 lg=1;
              default:
                 is+=lg;
                 break;
            }
           continue;
         }
         /*}}}  */
      }
     break;
   }
  return(i);
}
/*}}}  */
/*{{{  x_shift*/
public void  x_shift
 ( int * const p_off,
   int * const p_txt,
   int old,
   int po,
   int off,
   unsigned char const *t
 )
{
  int s;
  int v_txt;

  while (po<=old+(soln_str?1:0))
     old-=bd.scr.shift_size;
  for
   ( s=0,v_txt=0
   ; po>=bd.scr.txt_size.w
     || old>=bd.scr.shift_size
   ;
   )
   /*{{{  do a shift (width is bd.scr.shift_size)*/
   { int x;

     for (x=bd.scr.shift_size;x>0;)
      { unsigned char c;

        if (!(c = *t))
         /*{{{  ready, so break from loop*/
         { s+=x;
           x=0;
           break;
         }
         /*}}}  */
        else
         /*{{{  handle length of display*/
         { enum dsp_size lg;

           switch ((lg=CHAR_DSP_SIZE(c)))
            { case tab_dsp:
                 do { s++;x--; } while ((off+s)%dsp.tab_size);
                 break;
              case ictrl_dsp:
                 lg=1;
              default:
                 s+=lg;
                 x-=lg;
                 break;
            }
           t++;
           v_txt++;
         }
         /*}}}  */
      }
     old-=bd.scr.shift_size-x;
     po-=bd.scr.shift_size-x;
   }
   /*}}}  */
  *p_off=s;
  *p_txt=v_txt;
}
/*}}}  */
/*{{{  invers*/
#define invers() (highlighted?do_standend():do_standout())
/*}}}  */
/*{{{  noinvers*/
#define noinvers() (highlighted?do_standout():do_standend())
/*}}}  */
/*{{{  prtendwin*/
private void prtendwin(unsigned char const c)
{
  if (!bd.scr.clreol && !var_notitle)
     if (allow_text_so)
      { invers();
        oputc(c);
        noinvers();
      }
     else
        oputc(c!=' '?c:'|');
  else
     oputc(c);
  }
/*}}}  */
/*{{{  clrbuffline*/
public void clrbuffline(int y,int x)
{
  y+=bd.scr.off.h;
  if (bd.scr.clreol)
     moveclreol(y,x+bd.scr.off.w);
  else
   { int l;

     move_cursor_to(y,x+bd.scr.off.w);
     for (l=bd.scr.win.w-x;l--;) oputc(' ');
     prtendwin(' ');
   }
}
/*}}}  */
/*{{{  highlight*/
private void highlight(boolean on,boolean title)
{
  if (!allow_text_so && !title) return;
  if (on) on=True;
  if (highlighted!=on)
   { highlighted=on;
     if (highlighted) do_standout(); else do_standend();
   }
}
/*}}}  */
/*{{{  prt_highlight_bin_text*/
/*{{{  chg_highlight_type*/
private int chg_highlight_type(int limit,int m2)
{
  if (limit==m2)
   { highlight(False,False);
     limit=bd.scr.txt_size.w+2;
   }
  else
   { highlight(True,False);
     limit=m2;
   }

  return(limit);
}
/*}}}  */

public int prt_highlight_bin_text
 ( int m1,
   int m2,
   int off,
   int len,
   boolean mark,
   unsigned char *s
 )
{ unsigned char *s1,*s2;
  unsigned char c;
  int x;
  int limit;

  /*{{{  set limit for highlight switch*/
  limit=m1;
  if (m1<=0)
     limit=chg_highlight_type(m1,m2);
  if (m2<=0)
     limit=chg_highlight_type(m2,m2);
  /*}}}  */
  for (s1=s2=s,x=0;(c = *s2) && len;)
   /*{{{  search special char and print leading text followed by special*/
   {
     if (x==limit)
      /*{{{  prt old part and change highlight type*/
      { *s2='\0';
        oputs(s1);
        *(s1=s2)=c;
        limit=chg_highlight_type(limit,m2);
      }
      /*}}}  */
     { enum dsp_size lg;

       if ((lg=CHAR_DSP_SIZE(c))==norm_dsp)
        /*{{{  scan over normal text*/
        { s2++;
          off++;
          len--;
          x++;
        }
        /*}}}  */
       else
        /*{{{  print old text and the speciell handled char*/
        { *s2='\0';
          oputs(s1);
          *s2=c;
          switch (lg)
           { unsigned char ohx[max_dsp];
             unsigned char *xp;

             case tab_dsp:
              /*{{{  print as tabs*/
              { do
                 { oputc(' ');
                   off++;
                   len--;
                   x++;
                   if (x==limit) limit=chg_highlight_type(limit,m2);
                 }
                while (off%dsp.tab_size && len);
                break;
              }
              /*}}}  */
             case ictrl_dsp:
             case mark_dsp:
              /*{{{  print as ctrl*/
              { if (dsp.ctrl_add)
                   invers();
                else
                 { oputc(CTRL_MARK);
                   off++;
                   len--;
                   x++;
                   if (x==limit) limit=chg_highlight_type(limit,m2);
                 }
                if (len)
                 { if (hz&&c==(unsigned char)'~')
                      oputc(HZ_TILDE);
                   else if (c==127)
                      oputc('?');
                   else
                      oputc(ctrl_decode[c]);
                   len--;
                   off++;
                   x++;
                   if (x==limit) limit=chg_highlight_type(limit,m2);
                 }
                else
                   s2--;
                if (dsp.ctrl_add)
                   noinvers();
                break;
              }
              /*}}}  */
             case hex_dsp:
              /*{{{  gen hex*/
                ohx[1]=hex_digits[(unsigned)c>>4];
                ohx[2]=hex_digits[(unsigned)c&15];
                ohx[3]='\0';
                goto do_numb_dsp;
              /*}}}  */
             case dec_dsp:
              /*{{{  gen decimal*/
                ohx[1]=DECIMAL_MARK;
                ohx[2]=hex_digits[(unsigned)c/100];
                ohx[3]=hex_digits[((unsigned)c/10)%10];
                ohx[4]=hex_digits[(unsigned)c%10];
                ohx[5]='\0';
                goto do_numb_dsp;
              /*}}}  */
             default:
              /*{{{  error*/
                ori_assert(False,"incorrect dsp-base");
              /*}}}  */
             case oct_dsp:
              /*{{{  gen octal*/
                ohx[1]=hex_digits[(unsigned char)c>>6];
                ohx[2]=hex_digits[((unsigned char)c>>3)&7];
                ohx[3]=hex_digits[(unsigned char)c&7];
                ohx[4]='\0';
                goto do_numb_dsp;
              /*}}}  */
             do_numb_dsp:
              /*{{{  prt number*/
                ohx[0]=NUMB_MARK;
                /*{{{  print all chars of number*/
                for (xp=ohx;(c = *xp);xp++)
                   if (len)
                    { oputc(c);
                      off++;
                      len--;
                      x++;
                      if (x==limit)
                         limit=chg_highlight_type(limit,m2);
                    }
                   else
                    { s2--;
                      break;
                    }
                /*}}}  */
                break;
              /*}}}  */
           }
          s1= ++s2;
        }
        /*}}}  */
     }
   }
   /*}}}  */
  if (*s1)
   /*{{{  print missing part of line*/
     if (*s2)
      /*{{{  poke 0 into the text and print*/
      { c= *s2;
        *s2='\0';
        oputs(s1);
        *s2=c;
      }
      /*}}}  */
     else
        oputs(s1);
   /*}}}  */
  ori_assert(len>=0,"prt_highlight len crash");
  if (mark && !len)
   /*{{{  maybe put overflow mark*/
   { x++;
     while (*s2==' ' || *s2=='\t') s2++;
     prtendwin(*s2?eoln_str:' ');
   }
   /*}}}  */
  highlight(False,False);

  return(x);        /* number of printed characters */
}
/*}}}  */
/*{{{  prt_bin_text*/
public int prt_bin_text(int off,int len,boolean mark,unsigned char *s)
{
  return(prt_highlight_bin_text((LINELEN+2)*max_dsp,(LINELEN+3)*max_dsp,off,len,mark,s));
}
/*}}}  */
/*{{{  move_prt_clreol*/
public void move_prt_clreol(int y,int x,int off,int len,boolean mark,
                          unsigned char *s)
{ move_cursor_to(y+bd.scr.off.h,x+bd.scr.off.w);
  x+=prt_bin_text(off,len,mark,s);
  if (x<bd.scr.txt_size.w) clrbuffline(y,x);
}
/*}}}  */
/*{{{  write_dsp_line*/
public void write_dsp_line(element const * const ptr,int level)
{
  if (!scr_off)
   { int m1,m2,off,gap,s_off,x;
     unsigned char *wrt_ptr;

     copyin(print_buffer, ptr, False);
     /*{{{  set positions for higlight region*/
     switch (get_highlight(*ptr))
      { case no_highlight:
           m2=(LINELEN+3)*max_dsp;
           m1=(LINELEN+2)*max_dsp;
           break;
        case full_highlight:
           m1=0;
           m2=(LINELEN+2)*max_dsp;
           break;
        case c_f_highlight:
        case m_f_highlight:
           m1=bd.e.makefold_indent;
           m2=(LINELEN+2)*max_dsp;
           break;
        case f_m_highlight:
           m1=0;
           m2=bd.e.makefold_indent+1;
           break;
        case c_m_highlight:
           if (bd.e.makefold_indent<bd.scr.cursor.w)
            { m1=bd.e.makefold_indent;
              m2=bd.scr.cursor.w;
            }
           else
            { m2=bd.e.makefold_indent+1;
              m1=bd.scr.cursor.w-1;
            }
           break;
      }
     /*}}}  */
     /*{{{  handle offsets for full shifting screen*/
     gap=0;
     if ((s_off=bd.scr.full_shift_w))
      { off=scr2txt(bd.f.enter_spaces,s_off+1,print_buffer);
        ori_assert(off>0,"wrt-dsp-line: screen-shift without text-shift");
        if (ustrlen(print_buffer)>off)
         { gap=txt2scr(bd.f.enter_spaces,off,print_buffer)-s_off-1;
           wrt_ptr=print_buffer+off;
         }
        else
           wrt_ptr=(unsigned char*)empty_text;
      }
     else
      { off=0;
        wrt_ptr=print_buffer;
      }
     ori_assert(gap>=0,"wrt-dsp-line: invalid gap size");
     /*}}}  */
     move_cursor_to(level+bd.scr.off.h,1+bd.scr.off.w);
     /*{{{  print leading gap for incomplete chars, init no of printd chars*/
     x=0;
     if (gap)
      { if (soln_str)
         { oputc(soln_str);x=1; }
        while (x<gap)
         { x++;
           oputc(' ');
         }
      }
     /*}}}  */
     /*{{{  print text*/
     x+=prt_highlight_bin_text
         ( m1-off,
           m2-off,
           bd.f.enter_spaces+s_off+gap,
           bd.scr.txt_size.w-1-gap,
           True,
           wrt_ptr
         );
     /*}}}  */
     /*{{{  maybe clear rest of line*/
     if (x<bd.scr.txt_size.w)
        clrbuffline(level,x+1);
     /*}}}  */
     /*{{{  maybe mark left-shift*/
     if (off && soln_str && !gap)
      { move_cursor_to(level+bd.scr.off.h,1+bd.scr.off.w);
        oputc(soln_str);
      }
     /*}}}  */
   }
}
/*}}}  */

/*{{{  forward: restore_element*/
public void restore_element(int close_line);
/*}}}  */

/*{{{  title_op*/
/*{{{  statusline print data*/
/*{{{  mode braces*/
#define MODE_BEGIN "("
#define MODE_END ") "
/*}}}  */
/*{{{  buffer id/change*/
#define stat_buff " %s%s%c "
#define no_char ' '
#define buff_txt (unsigned char*)": "
#define chg_char '*'
/*}}}  */
/*{{{  version string*/
#define s_version (unsigned char*)" " MAJOR_VERSION "." MINOR_VERSION "." REVISION DEBUG_FEATURE " " MODE_BEGIN
/*}}}  */
/*{{{  window modes*/
#ifdef WINDOW_TITLE_CHANGE
#  ifdef DIREDT
#     define full_win_stat " " MODE_BEGIN "%s%c%s" MODE_END
#  endif
#  define win_stat         " " MODE_BEGIN "%s" MODE_END
#  define no_win_stat      " - "
#endif
/*}}}  */
private unsigned char const d_e_tag[]=DV_TAG;
/*}}}  */
/*{{{  pathcut*/
/*{{{  comment*/
/*
   pathcut: input  char **name
                   (adress of a pointer to filename)
            output int
                   number of cut chars
            sideeffect:
                   if there is a '/'( not at start of the filename), then
                   *name is set to the text behind '/'
*/
/*}}}  */
private boolean pathcut(unsigned char **name)
{
  unsigned char *s= *name;

  if (*s==PATH_C)
     s++;
  while (*s && *s!=PATH_C)
     s++;
  if (*s)
   { *name=s;
     return(True);
   }
  return(False);
}
/*}}}  */
/*{{{  p_modes*/
/*{{{  p_mode*/
private void p_mode(unsigned char *s,unsigned char const * const m)
{
  if (*m)
   { s+=ustrlen(s);
     *s++=comma;
     ustrcpy(s,m);
   }
}
/*}}}  */

private void p_modes(unsigned char *r,boolean sh)
{
  /*{{{  view*/
  if (bd.m.read_only)
     p_mode(r,(unsigned char*)(sh?sVIEW_TAG:VIEW_TAG));
  /*}}}  */
  /*{{{  dirview*/
  if (bd.m.dir_edit)
     p_mode(r,sh?(unsigned char*)sDV_TAG:d_e_tag);
  /*}}}  */
# ifdef REGEXP
   /*{{{  regexp*/
     if (reg_type!=no_reg)
        p_mode
         ( r,
           (unsigned char*)((reg_type==basic_reg)
                              ? (sh?sBREGEXP_TAG:BREGEXP_TAG)
                              : (sh?sXREGEXP_TAG:XREGEXP_TAG)
           )
         );
   /*}}}  */
# endif
  /*{{{  defmac*/
  if (defining_macro)
     p_mode(r,(unsigned char*)(sh?sDM_TAG:DM_TAG));
  /*}}}  */
  /*{{{  folding*/
  if (bd.m.select_mode==fold_selection || bd.m.select_mode==unmark_fold_selection)
     p_mode(r,(unsigned char*)(sh?sFOLD_TAG:FOLD_TAG));
  /*}}}  */
  /*{{{  over*/
  if (bd.m.overwrite)
     p_mode(r,(unsigned char*)(sh?sOVER_TAG:OVER_TAG));
  /*}}}  */
  /*{{{  auto*/
  if (auto_save)
     p_mode(r,(unsigned char*)(sh?sAUTO_TAG:AUTO_TAG));
  /*}}}  */
  /*{{{  nopar*/
  if (bd.e.fold_in)
     p_mode(r,(unsigned char*)(sh?sNOPAR_TAG:NOPAR_TAG));
  /*}}}  */
  /*{{{  hash*/
  if (bd.m.hash_shift)
     p_mode(r,(unsigned char*)H_SHIFT_TAG);
  /*}}}  */
  /*{{{  append user modes*/
  { int i;
    unsigned char **um;

    for
     ( i=MODE_COUNT-1,um=u_modes+(sh?(MODE_COUNT-1):(2*MODE_COUNT-1))
     ; i>=0
     ; i--,um--
     )
       if (um_active[i])
        { p_mode(r,*um);
          if (n_modes[i])
             ustrcat(r,i_to_ua(n_modes[i]));
        }
  }
  /*}}}  */
  ustrcat(r,(unsigned char*)MODE_END);
  /*{{{  maybe w_name*/
  if (w_name)
   { ustrcat(r,w_name);
     ustrcat(r,one_space);
   }
  /*}}}  */
}
/*}}}  */
#ifdef WINDOW_TITLE_CHANGE
   public unsigned char statline[BUFFERLEN+1];
#endif

public int title_op(TITLE_OP op)
{
  /*{{{  variables*/
  static boolean title_changed=True;
  enum dsp_size old_numb_mode;
  /*}}}  */

  old_numb_mode=dsp.norm;
  dsp.norm=norm_dsp;
  switch(op)
   {
     /*{{{  LINE_NO*/
     case LINE_NO:
        break;
     /*}}}  */
     /*{{{  SMALLTITLE,UPDTITLE,PRTTITLE,WINTITLE*/
     case UPDTITLE:
        if (!title_changed)
           break;
     case PRTTITLE:
     case SMALLTITLE:
        if (!scr_off && !var_notitle && !dirty)
#     ifdef WINDOW_TITLE_CHANGE
     case WINTITLE:
#     endif
         /*{{{  do the print*/
         {
           /*{{{  variables*/
           unsigned char filename[_POSIX_PATH_MAX+1];
#           ifndef WINDOW_TITLE_CHANGE
              unsigned char statline[LINELEN+1];
#           endif
           unsigned char *fn_start;
           int max;
           int time_len=0;
           boolean to_long;
           msgtyp old_last;
           /*}}}  */

           old_last=last_message;
           /*{{{  get filename*/
           if (bd.f.real_tail == bd.f.real_head)
             *filename = '\0';
           else
             ustrcpy(filename,get_data(bd.f.real_tail));
           /*}}}  */
#         ifdef WINDOW_TITLE_CHANGE
           if (op==WINTITLE)
            /*{{{  buildt title for window*/
            { max=WIN_TITLE_LENGTH(screen.w);
              ustrcpy(statline,origami_name);
              /*{{{  modes*/
              { unsigned char *bn;

                bn=(buff_used<=1)?0:i_to_ua(buff_used);
                ustrcat
                 ( statline,
                   get_msg
                    ( MSG_ARG_FORMAT,
#                ifdef DIREDT
                        (bn && bd.m.dir_edit)   ? full_win_stat :
#                endif
                        (bn
#                ifdef DIREDT
                            ||  bd.m.dir_edit
#                endif
                                              ) ? win_stat :
                                                  no_win_stat,
#                ifdef DIREDT
                        bd.m.dir_edit ? d_e_tag :
#                endif
                                                 bn,
                      comma,
                      bn
                    )
                 );
                if (w_name)
                 { ustrcat(statline,w_name);
                   ustrcat(statline,one_space);
                 }
              }
              /*}}}  */
            }
            /*}}}  */
           else
#         endif
            /*{{{  data for origami's title line*/
            { max=bd.scr.win.w-(bd.scr.bold_stat?(2*sg):0);
              /*{{{  buffer id and chg*/
              ustrcpy
               ( statline,
                 get_msg
                  ( MSG_ARG_FORMAT,
                    stat_buff,
                    ((buff_used>1)?i_to_ua(bd.scr.list_no):empty_text),
                    ((buff_used>1)?buff_txt:empty_text),
                    (bd.m.file_changed_status?chg_char:no_char)
                  )
               );
              /*}}}  */
              /*{{{  get statusline data*/
              { unsigned char *user_c_string;

                if (op!=SMALLTITLE)
                 { max-=(time_len=strlen(time_str));
                   /*{{{  additional data*/
                   ustrcat(statline,origami_name);
                   ustrcat(statline,s_version);
                   title_debug(statline);
                   /*{{{  get correct language-mark, fn_start at end of the current string*/
                   ustrcat(statline,dialects[bd.m.dialect.typ].name);
                   fn_start=statline;
                   while (*fn_start) fn_start++;
                   if (bd.m.dialect.typ == F_C_USER)
                    /*{{{  append user-comments*/
                    { *fn_start++=' ';
                      user_c_string=fn_start;
                      ustrcpy(fn_start,dialects[F_C_USER].txt.start);
                      ustrcat(fn_start,one_space);
                      ustrcat(fn_start,dialects[F_C_USER].txt.end);
                      while (*fn_start) fn_start++;
                      if
                       (    (  dialects[F_C_USER].lg.start
                             + dialects[F_C_USER].lg.end
                            )
                         <= SHORT_F_C_LENGTH
                       )
                         user_c_string=0;
                    }
                    /*}}}  */
                   else
                      user_c_string=0;
                   *fn_start='\0';
                   /*}}}  */
                   /*{{{  modes at end of line*/
                   p_modes(fn_start,False);
                   /*}}}  */
                   /*}}}  */
                 }
                else
                 /*{{{  fn_start to end of string!*/
                   fn_start=statline+ustrlen(statline);
                 /*}}}  */
                /*{{{  position*/
                if (xy_show)
                 { if (!bd.S.y_pos)
                      bd.S.y_pos=line_no(bd.f.current,bd.f.real_head);
                   ori_assert(bd.scr.cur_shift_w>=0 && bd.scr.cur_shift_w<=LINELEN*max_dsp,"shift-check");
                   ustrcat
                    ( fn_start,
                      get_msg
                       ( (bd.scr.cur_shift_w>0)?M_XYS_LONG:M_XY_LONG,
                         bd.S.y_pos,
                         bd.S.last_x_pos=bd.scr.cursor.w+bd.f.enter_spaces,
                         bd.scr.cur_shift_w
                       )
                    );
                   ustrcat(fn_start,one_space);
                 }
                /*}}}  */
                if (ustrlen(filename)+ustrlen(statline)>max)
                 /*{{{  try short version and full pathname*/
                 { if (op!=SMALLTITLE)
                    {
                      /*{{{  maybe short user comment*/
                      if (user_c_string)
                       { int i;
                         unsigned char *s,*t;

                         t=user_c_string;
                         /*{{{  copy start*/
                         for
                          ( i=SHORT_F_C_LENGTH/2,
                            s=dialects[F_C_USER].txt.start
                          ; *s && i
                          ; i--,*t++ = *s++
                          );
                         /*}}}  */
                         if (*s) *t++=eoln_str;
                         *t++=' ';
                         /*{{{  copy end*/
                         for
                          ( i=SHORT_F_C_LENGTH/2,
                            s=dialects[F_C_USER].txt.end
                          ; *s && i
                          ; i--,*t++ = *s++
                          );
                         /*}}}  */
                         if (*s) *t++=eoln_str;
                         *t='\0';
                         fn_start=t;
                       }
                      /*}}}  */
                      /*{{{  modes*/
                      *fn_start='\0';
                      p_modes(fn_start,True);
                      /*}}}  */
                    }
                   else
                      *fn_start='\0';
                   /*{{{  position*/
                   ori_assert(bd.scr.cur_shift_w<=LINELEN*max_dsp,"shift-check");
                   if (xy_show)
                    { ustrcat
                       ( fn_start,
                         get_msg
                          ( (bd.scr.cur_shift_w>0)?M_XYS_SHORT:M_XY_SHORT,
                            bd.S.y_pos,
                            bd.S.last_x_pos,
                            bd.scr.cur_shift_w
                          )
                       );
                      ustrcat(fn_start,one_space);
                    }
                   /*}}}  */
                 }
                 /*}}}  */
              }
              /*}}}  */
            }
            /*}}}  */
           /*{{{  fn_start to end of string!*/
           fn_start=statline+ustrlen(statline);
           /*}}}  */
           ustrcpy(fn_start,filename);
           if (ustrlen(statline)>max)
            /*{{{  cut filename*/
            { unsigned char *fn;
              boolean loop;
              boolean cut;

              for (fn=filename,loop=True,cut=False;loop;)
               {
                 /*{{{  cat filename to statusline*/
                 *fn_start='\0';
                 if (cut)
                    ustrcpy(fn_start,M_CUT_PATH);
                 ustrcat(fn_start,fn);
                 /*}}}  */
                 if ((to_long=(ustrlen(statline)>max)) && pathcut(&fn))
                    cut=True;
                 else
                    loop=False;
               }
            }
            /*}}}  */
           else
              to_long=False;
           /*{{{  maybe handle time*/
           if (time_len)
            { ustrcat(fn_start,(unsigned char*)time_str);
              to_long=(ustrlen(statline)>(max+=time_len));
            }
           /*}}}  */
           /*{{{  handle end of line*/
           if (to_long)
            { statline[max-1]=eoln_str;
              statline[max]='\0';
            }
           else
#           ifdef WINDOW_TITLE_CHANGE
                if (op!=WINTITLE)
#           endif
            { int l;

              l=ustrlen(statline);
              spaces(statline+l,max-l);
            }
           /*}}}  */
#          ifdef WINDOW_TITLE_CHANGE
              if (op==WINTITLE)
                 break;
#          endif
           /*{{{  print the line*/
           move_cursor_to(bd.scr.win.h+bd.scr.off.h,1+bd.scr.off.w);
           if (bd.scr.bold_stat) highlight(True,True);
           prt_bin_text(0,max,False,statline);
           if (bd.scr.bold_stat) highlight(False,True);
           /*}}}  */
           title_changed=False;
           last_message=old_last;
         }
         /*}}}  */
        break;
     /*}}}  */
     /*{{{  CHGXY*/
     case CHGXY:
        bd.S.last_x_pos=0;
        bd.S.y_pos=0;
     chg_line:
        if (xy_show) goto chg_title;
        break;
     /*}}}  */
     /*{{{  CHGTITLE*/
     case CHGTITLE:
     chg_title:
       title_changed=True;
       break;
     /*}}}  */
     /*{{{  CHGX*/
     case CHGX:
        if (bd.S.last_x_pos!=bd.scr.cursor.w+bd.f.enter_spaces)
           goto chg_line;
        break;
     /*}}}  */
     /*{{{  INCY*/
     case INCY:
        if (bd.S.y_pos) bd.S.y_pos++;
        goto chg_line;
     /*}}}  */
     /*{{{  DECY*/
     case DECY:
        if (bd.S.y_pos) bd.S.y_pos--;
        goto chg_line;
     /*}}}  */
   }
  dsp.norm=old_numb_mode;

  return(bd.S.y_pos);
}
/*}}}  */

/*{{{  restore_area_line*/
private void restore_area_line(int const y)
{ int dsp;

  if (scr_off || !area_used) return;
  /*{{{  print area_line*/
  if ((dsp=y+area_line.h)<message_line)
   { moveclreol(dsp,1);
     move_cursor_to(dsp,1);
     { boolean wsg=!var_notitle && (dsp==(message_line-1));

       if (wsg) highlight(True,True);
       prt_bin_text(0,area_line.w-(wsg?sg+sg:0),False,area_lines[y]);
       if (wsg) highlight(False,True);
     }
   }
  /*}}}  */
}
/*}}}  */
/*{{{  store_area_line*/
private void store_area_line(unsigned char const * const x,int const no)
{
  /*{{{  maybe free old data*/
  if (area_lines[no]!=empty_text)
     paket_free(area_lines[no]);
  /*}}}  */
  /*{{{  store data*/
  area_lines[no]=(unsigned char*)empty_text;
  if (*x)
     if ((area_lines[no]=ori_malloc(ustrlen(x)+1)))
        ustrcpy(area_lines[no],x);
     else
        warn_message(M_NO_MALLOC);
  /*}}}  */
}
/*}}}  */
/*{{{  restore_area*/
public void restore_area(void)
{ int y;

  for (y=area_line.h;y<message_line;restore_area_line(y++-area_line.h));
}
/*}}}  */
/*{{{  set_area*/
public void set_area (int new_size)
{ int i;

  /*{{{  remove old area*/
  if (area_used)
   { for
      ( i=area_used;
        i--;
        store_area_line(empty_text,i)
      );
     paket_free(area_lines);
   }
  /*}}}  */
  /*{{{  get new area-size*/
  i=area_used=new_size;
  if (screen.h-4<area_used || area_used<=0)
   { area_used=0;
     if (i)
        msg_message(M_NO_AREA);
   }
  /*}}}  */
  /*{{{  get place for new arealines*/
  if (!(area_lines=ori_malloc(area_used*sizeof(char*))))
     exit_origami(r_mem_full,M_NO_MEMORY);
  /*}}}  */
  /*{{{  clear the buffers*/
  while (i--)
     area_lines[i]=(unsigned char*)empty_text;
  /*}}}  */
}
/*}}}  */
/*{{{  write_area*/
void write_area (int x,int y,unsigned char const *pr)
{
  /*{{{  variables*/
  int ex;
  unsigned char *p=print_buffer;
  /*}}}  */

  /*{{{  get position to write to*/
  ex= --x;
  --y;
  /*}}}  */
  /*{{{  check position*/
  if (!area_used || x<0 || y<0 || y>=area_used)
   { msg_message(M_NO_AREA);
     return;
   }
  /*}}}  */
  /*{{{  copy line to printbuffer and set p to correct x-position for writing*/
  ustrcpy(print_buffer,area_lines[y]);
  while (x--) if (*p) p++; else { *p=' ';*(++p)='\0'; }
  /*}}}  */
  /*{{{  copy string to correct position*/
  ustrcpy(p,pr);
  /*}}}  */
  /*{{{  restore rest of old line*/
  ex+=ustrlen(p);
  if (ex<ustrlen(area_lines[y]))
     ustrcat(p,area_lines[y]+ex);
  /*}}}  */
  /*{{{  move new line to storage and show it*/
  store_area_line(print_buffer,y);
  restore_area_line(y);
  /*}}}  */
}
/*}}}  */

/*{{{  restore*/
public void restore(int from)
{
  element *p=bd.scr.start;
  int h;

  for (h = 1; h <= bd.scr.end_level; h++)
   {
     /*{{{  display if under from*/
     if (h >= from)
        write_dsp_line(p, h);
     /*}}}  */
     p = p->next;
   }
  for (h=bd.scr.end_level+1;h<=bd.scr.txt_size.h;h++)
     clrbuffline(h,1);
  title_op(CHGTITLE);
}
/*}}}  */
/*{{{  restore_to_end*/
public void restore_to_end(int from)
{  /*if select_on then set select separately*/
  bd.scr.end = bd.f.current;
  bd.scr.end_level = bd.scr.cursor.h;
  while (bd.scr.end_level < bd.scr.txt_size.h && bd.scr.end != bd.f.tail)
   { bd.scr.end_level++;
     bd.scr.end = bd.scr.end->next;
   }
  restore(from);
}
/*}}}  */
/*{{{  restore_element*/
public void restore_element(int close_line)
{  /*if select_on then set select separately*/
  int level, dist_to_end;
  int dist_to_start;

  dist_to_start = to_start((int)(bd.scr.txt_size.h - 1));
  dist_to_end = to_bottom((int)(bd.scr.txt_size.h - 2));
  if ((bd.scr.cursor.h=close_line)<2)
     bd.scr.cursor.h=2;
  else if (close_line>=bd.scr.txt_size.h)
     bd.scr.cursor.h=bd.scr.txt_size.h-1;
  if (dist_to_start<bd.scr.cursor.h)
     bd.scr.cursor.h = dist_to_start;
  while
   (    bd.scr.cursor.h+dist_to_end < bd.scr.txt_size.h
     && dist_to_start > bd.scr.cursor.h
   ) bd.scr.cursor.h++;
  for (level=bd.scr.cursor.h-1,bd.scr.start=bd.f.current;level>0;level--)
     bd.scr.start = bd.scr.start->prec;
  restore_to_end(1);
}
/*}}}  */
/*{{{  restore_or_restore_to_end*/
public void restore_or_restore_to_end(void)
{ element *p;

  if (bd.scr.start != bd.f.head->next) {
    restore_element(bd.scr.txt_size.h/2);
    return;
  }
  p = bd.f.head->next;
  bd.scr.cursor.h = 1;
  while (p != bd.f.current) {
    bd.scr.cursor.h++;
    p = p->next;
  }
  restore_to_end(bd.scr.cursor.h);
}
/*}}}  */
/*{{{  restore_shift*/
public boolean restore_shift(int off)
{
  if
   (    ( bd.m.full_shift && bd.scr.full_shift_w!=off )
     || (!bd.m.full_shift && bd.scr.full_shift_w )
   )
   { bd.scr.full_shift_w=bd.m.full_shift?off:0;
     copyout(cur_dsp_line,bd.f.current);
     restore(1);
     return(True);
   }
  return(False);
}
/*}}}  */

/*{{{  up_a_bit*/
public void up_a_bit(int from)
{ int dirty_line=0;

  if (from!=bd.scr.txt_size.h)
     if
      /*{{{  cannot scroll*/
      ( B_scroll_region
         ( from+bd.scr.off.h,1+bd.scr.off.w,
           bd.scr.txt_size.h+bd.scr.off.h,bd.scr.off.w+bd.scr.txt_size.w,
           True
         )
      )
      /*}}}  */
      /*{{{  simulate by add/delete line or redraw*/
      { move_cursor_to(from+bd.scr.off.h,1);
        if (bd.scr.win.w!=screen.w||DelLine(1))
           dirty_line=from;
        else
         { move_cursor_to(bd.scr.txt_size.h+bd.scr.off.h,1);
           if (insLine(1))
              dirty_line=bd.scr.txt_size.h;
         }
        if (dirty_line)
           restore_to_end(dirty_line);
      }
      /*}}}  */
     else if (bd.scr.win.w+bd.scr.off.w!=screen.w)
      /*{{{  print right margin of buffer again*/
      { move_cursor_to(bd.scr.txt_size.h+bd.scr.off.h,bd.scr.txt_size.w+bd.scr.off.w);
        prtendwin(' ');
      }
      /*}}}  */
  move_cursor_to(from+bd.scr.off.h,1+bd.scr.off.w);
}
/*}}}  */
/*{{{  down_a_bit*/
public void down_a_bit(int from)
{ int dirty_line=0;

  if (from!=bd.scr.txt_size.h)
     if
      /*{{{  cannot scroll*/
      ( B_scroll_region
         ( from+bd.scr.off.h,1+bd.scr.off.w,
           bd.scr.txt_size.h+bd.scr.off.h,bd.scr.off.w+bd.scr.txt_size.w,
           False
         )
      )
      /*}}}  */
      /*{{{  simulate by add/delete line or redraw*/
      { move_cursor_to(from+bd.scr.off.h,1);
        if (bd.scr.win.w!=screen.w||insLine(1))
           dirty_line=from;
        else
         { move_cursor_to(bd.scr.txt_size.h+1+bd.scr.off.h,1);
           if (DelLine(1))
            { dirty_line=bd.scr.txt_size.h+1;
              message(empty_text);
            }
         }
        if (dirty_line)
           restore_to_end(dirty_line);
      }
      /*}}}  */
   move_cursor_to(from+bd.scr.off.h,1+bd.scr.off.w);
}
/*}}}  */
/*{{{  whole_screen_up*/
public void whole_screen_up(void)
{
  if (bd.scr.start == bd.f.tail) return;
  bd.scr.start = bd.scr.start->next;
  bd.scr.cursor.h--;
  up_a_bit(1);
  if (bd.scr.end != bd.f.tail) {
    bd.scr.end = bd.scr.end->next;
    write_dsp_line(bd.scr.end, bd.scr.txt_size.h);
  } else
    bd.scr.end_level--;
}
/*}}}  */
/*{{{  whole_screen_down*/
public void whole_screen_down(void)
{
  if (bd.scr.start == bd.f.head->next) return;
  bd.scr.start = bd.scr.start->prec;
  bd.scr.cursor.h++;
  if (bd.scr.end_level == bd.scr.txt_size.h)
    bd.scr.end = bd.scr.end->prec;
  else
    bd.scr.end_level++;
  down_a_bit(1);
  write_dsp_line(bd.scr.start,1);
}
/*}}}  */

/*{{{  upd_highlight*/
public boolean upd_highlight(element *hd, element *tl, boolean full)
{
  element *m1,*m2;

  if (!allow_text_so)
     return(False);
  /*{{{  handle delay for screen-off*/
  if (scr_off)
   { bd.m.upd_sel_delayed=True;
     return(False);
   }
  else
     bd.m.upd_sel_delayed=False;
  /*}}}  */
  /*{{{  set pointers to selections*/
  if (bd.m.select_mode==no_selection)
     if (bd.m.last_no_sel)
        return(False);
     else
      { m1=m2=0;
        bd.m.last_no_sel=True;
      }
  else
   { m1=bd.f.select_ptr;
     m2=bd.f.current;
     bd.m.last_no_sel=False;
   }
  /*}}}  */
  for (;;)
   /*{{{  do one loop*/
   {
     /*{{{  variables*/
     highlight_typ msingle,mstart,min,mend;
     element *p;
     boolean inside;
     boolean use_next;
     int match;
     /*}}}  */

     /*{{{  set highlight types and move-mode*/
     use_next=False;
     switch (bd.m.select_mode)
      { case fold_selection:
           use_next=True;
           mstart=min=m_f_highlight;
           msingle=mend=no_highlight;
           break;
        case lim_pseudo_fold_selection:
           use_next=True;
        case pseudo_fold_selection:
           msingle=mstart=min=m_f_highlight;
           mend=no_highlight;
           break;
        case lim_reg_selection:
           use_next=True;
        case reg_selection:
           msingle=c_m_highlight;
           mstart=c_f_highlight;
           min=full_highlight;
           mend=f_m_highlight;
           break;
        case lim_block_selection:
           use_next=True;
        case block_selection:
           msingle=mstart=min=mend=c_m_highlight;
           break;
        case no_selection:
        case nodrawn_selection:
        case unmark_fold_selection:
        case lim_nodrawn_selection:
        default:
           msingle=mstart=min=mend=no_highlight;
           break;
      }
     /*}}}  */
     /*{{{  set highlight flags in range*/
     /*{{{  maybe shift hd/tl, set initial value for inside*/
     inside=False;
     if (!full)
      { if (hd!=bd.f.real_head)
         { hd=hd->prec;
           inside=get_highlight(*hd);
         }
        if (tl!=bd.f.real_tail) tl=tl->next;
      }
     /*}}}  */
     for (p=hd,match=0;;)
      { highlight_typ h;

        /*{{{  maybe change m1<->m2, if m2 < m1*/
        if (!inside && p==m2) { m2=m1;m1=p; }
        /*}}}  */
        /*{{{  get highlight flag for this line*/
        if (p==m1)
         /*{{{  handle start of selection*/
         { match++;
           if (p==m2)
            { match++;
              h=msingle;
            }
           else
            { inside=True;
              h=mstart;
            }
         }
         /*}}}  */
        else if (p==m2)
         /*{{{  handle end of selection*/
         { match++;
           inside=False;
           h=mend;
         }
         /*}}}  */
        else if (inside)
           h=min;
        else
           h=no_highlight;
        /*}}}  */
        /*{{{  set flags*/
        { linetyp t;

          t=get_linetyp(*p);
          set_lflags(*p,t,h);
        }
        /*}}}  */
        if (p==tl)
           break;
        if (use_next)
           p=p->next;
        else
           p=move_on(p);
      }
     /*}}}  */
     if (full && bd.m.select_mode!=no_selection && match!=2)
      { bd.m.select_mode=no_selection;
        warn_message(get_msg(M_FOLDERR));
        ocl_msg("highlight crash",0);
        ori_abort(~SIGINT);
        /* now loop a second time and reset flags :-) */
      }
     else
        return(True);
   }
   /*}}}  */
}
/*}}}  */
