/*
*  gcal_tty.c:  Screen support functions and major output function.
*
*
*  Copyright (C) 1994, 1995, 1996 Thomas Esken
*
*  This software doesn't claim completeness, correctness or usability.
*  On principle I will not be liable for ANY damages or losses (implicit
*  or explicit), which result from using or handling my software.
*  If you use this software, you agree without any exception to this
*  agreement, which binds you LEGALLY !!
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the `GNU General Public License' as published by
*  the `Free Software Foundation'; either version 2, or (at your option)
*  any later version.
*
*  You should have received a copy of the `GNU General Public License'
*  along with this program; if not, write to the:
*
*    Free Software Foundation, Inc.
*    59 Temple Place - Suite 330
*    Boston, MA 02111-1307,  USA
*/



#ifdef RCSID
static char rcsid[]="$Id: gcal_tty.c 2.01 1996/05/20 02:00:01 tom Exp $";
#endif



/*
*  Include header files.
*/
#include "gcal_tai.h"
#if HAVE_CTYPE_H
#  include <ctype.h>
#endif
#if HAVE_SYS_TYPES_H
#  include <sys/types.h>
#endif
#if USE_PAGER || USE_HLS
#  if defined(UNIX) && !defined(DJG)
#    if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
#      include <termios.h>
#      if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
#        include <sys/ioctl.h>
#      endif
#    else /* !HAVE_TERMIOS_H || !HAVE_TERMIOS_FUNCS */
#      if HAVE_TERMIO_H
#        include <termio.h>
#      else /* !HAVE_TERMIO_H */
#        include <sgtty.h>
#        if HAVE_SYS_IOCTL_H && (defined(WIOCGETD) || defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP))
#          include <sys/ioctl.h>
#        endif
#      endif /* !HAVE_TERMIO_H */
#    endif /* !HAVE_TERMIOS_H || !HAVE_TERMIOS_FUNCS */
/*
*  For the Unix PC (ATT 7300 & 3B1):  (taken from less-278 by Mark Nudelman)
*  Since WIOCGETD is defined in sys/window.h, we can't use that to decide
*  whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
*/
#    ifndef TIOCGWINSZ
#      include <sys/signal.h>
#      ifdef SIGPHONE
#        include <sys/window.h>
#      endif
#    endif
#    if HAVE_SYS_STREAM_H
#      include <sys/stream.h>
#    endif
#    if HAVE_SYS_PTEM_H
#      include <sys/ptem.h>
#    endif
#  endif /* UNIX && !DJG */
#  ifdef DJG
#    include <pc.h>
#  else /* !DJG */
#    if defined(UNIX) || (defined(OS2) && defined(__GNUC__))
#      if HAVE_TERMCAP_H && (HAVE_LIBTERMCAP || HAVE_LIBTERMLIB)
#        if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS && defined(OS2) && defined(__GNUC__)
#          include <termios.h>
#        endif
#        include <termcap.h>
#      else /* !HAVE_TERMCAP_H || (!HAVE_LIBTERMCAP && !HAVE_LIBTERMLIB) */
#        if HAVE_LIBTERMCAP || HAVE_LIBTERMLIB
IMPORT int   tgetent __P_((char *buffer, char *termtype));
IMPORT int   tgetnum __P_((char *name));
#          if USE_HLS
IMPORT char *tgetstr __P_((char *name, char **area));
IMPORT char *tputs __P_((char *string, int nlines, int (*outfunc)()));
#          endif
#          if USE_PAGER
IMPORT int   tgetflag __P_((char *name));
#          endif /* USE_PAGER */
#        endif /* HAVE_LIBTERMCAP || HAVE_LIBTERMLIB */
#      endif /* !HAVE_TERMCAP_H || (!HAVE_LIBTERMCAP && !HAVE_LIBTERMLIB) */
#      if MUST_DCL_OSPEED && USE_HLS
IMPORT short ospeed;   /* Terminal output baud rate */
/*
   On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC.
   Unfortunately, PC is a global variable used by the Termcap library.
*/
#        ifdef PC
#          undef PC
#        endif
IMPORT char  PC;       /* Padding character */
#      endif /* MUST_DCL_OSPEED && USE_HLS */
#    endif /* UNIX || (OS2 && __GNUC__) */
#  endif /* !DJG */
#endif /* USE_PAGER || USE_HLS */
#include "gcal.h"



/*
*  Function prototypes.
*/
#if __cplusplus
extern "C"
{
#endif
/*
************************************************** Defined in `gcal_utl.c'.
*/
IMPORT VOID_PTR
my_malloc __P_((const int   amount,
                const int   exit_status,
                const char *module_name,
                const long  module_line,
                const char *var_name,
                const int   var_contents));
IMPORT void
resize_all_strings __P_((const int   amount,
                         const char *module_name,
                         const long  module_line));
IMPORT int
my_atoi __P_((const char *string));
/*
************************************************** Defined in `gcal_tty.c'.
*/
EXPORT void
print_text __P_((FILE *fp,
                 char *text_line));
EXPORT void
get_tty_hls __P_((const char *sequence_str));
#if USE_PAGER
EXPORT void
get_tty_scr_size __P_((int *rows,
                       int *cols));
#endif
#if defined(GCAL_TCAP) && USE_HLS
LOCAL char *
skip_leading_padding_info __P_((char *sequence_str));
#endif
#if USE_HLS || USE_PAGER
#  ifdef GCAL_TCAP
LOCAL Bool
open_termcap __P_((void));
#    if USE_HLS
LOCAL void
get_ospeed __P_((void));
LOCAL int
outchar __P_((int ch));
LOCAL Bool
get_termcap_hls __P_((Bool *hls1_set,
                      Bool *hls2_set));
#    endif /* USE_HLS */
#    if USE_PAGER
LOCAL Bool
get_termcap_scr_attrib __P_((int *rows,
                             int *cols));
#    endif /* USE_PAGER */
#  else /* !GCAL_TCAP */
#    if defined(MSDOS) && USE_PAGER
LOCAL Uchar
peek_byte __P_((Uint segment,
                Uint offset));
#    endif
#  endif /* GCAL_TCAP */
#endif /* USE_HLS || USE_PAGER */
LOCAL void
get_hl_seq __P_((const char *sequence_str,
                       Bool *hls1_set,
                       Bool *hls2_set));
#if !HAVE_STRTOL
LOCAL int
sbyte2int __P_((const char *string,
                const int   base));
#endif
#if !HAVE_STRSTR
LOCAL char *
my_strstr __P_((const char *text,
                const char *pattern));
#endif /* !HAVE_STRSTR */
#if __cplusplus
}
#endif



/*
*  Declare public(extern) variables.
*/
#ifdef GCAL_EMAIL
IMPORT FILE       *tfp;              /* Temporary file, which is send by mailer */
#endif
IMPORT Hls_struct  ehls1s;           /* Effective hls 1 start (current day) */
IMPORT Hls_struct  ehls1e;           /* Effective hls 1 end (current day) */
IMPORT Hls_struct  ehls2s;           /* Effective hls 2 start (holiday) */
IMPORT Hls_struct  ehls2e;           /* Effective hls 2 end (holiday) */
IMPORT Uint        maxlen_max;       /* Actual size of all string vectors */
IMPORT int         warning_level;    /* --debug[=0...WARN_LVL_MAX] */
IMPORT int         is_tty;           /* Is output displayed on a terminal? */
IMPORT int         is_tty1;          /* Is output directed to channel 1? */
IMPORT int         is_tty2;          /* Is output directed to channel 2? */
IMPORT int         tty_rows;         /* Number of tty rows */
IMPORT int         tty_cols;         /* Number of tty columns */
IMPORT char       *s1;               /* General purpose text buffer */
#ifdef GCAL_EPAGER
IMPORT char       *ext_pager;        /* Name of external pager program */
#endif
IMPORT Bool        emu_hls;          /* Must we emulate the highlighting sequences? */
IMPORT Bool        highlight_flag;   /* -H<yes> or -H<no> */
#if USE_PAGER
IMPORT Bool        pager_flag;       /* -p */
#endif



/*
   Define local(static) variables.
*/
#if USE_PAGER || USE_HLS
#  ifdef GCAL_TCAP
#    if USE_HLS
LOCAL FILE  *fp_outchar;         /* Module global file which is used by the `tputs()' function */
LOCAL Bool   is_padding=FALSE;   /* Stores whether padding is used or not */
#    endif
#    define  TC_BUFLEN  4096
LOCAL char   tc_buf[TC_BUFLEN];  /* Module global Termcap buffer */
LOCAL Bool   tc_no_error=TRUE;   /* Termcap access error occurred */
#    if MUST_DCL_OSPEED && USE_HLS
extern short ospeed;             /* Terminal output baud rate */
extern char  PC;                 /* Padding character */
#    endif
#  endif /* GCAL_TCAP */
#  if USE_PAGER
LOCAL Bool   tty_am=TRUE;        /* Terminal has automatic margins */
LOCAL Bool   tty_xn=FALSE;       /* Terminal ignores newline after wrap */
#  endif /* USE_PAGER */
#endif /* USE_PAGER || USE_HLS */



   PUBLIC void
print_text (fp, text_line)
   FILE *fp;
   char *text_line;
/*
   This is the central tty output function, which works according to
     actual display mode.  It prints a line of text given in `text_line' with
     newline to file `fp' with paging option (very poor and simple paging,
     only used if preprocessor symbol USE_PAGER is defined) and deletes ALWAYS
     the delivered `text_line' automatically after printing (*text_line = '\0').
*/
{
#if USE_PAGER || (defined(GCAL_TCAP) && USE_HLS)
   if (   is_tty
#  if USE_PAGER && (!defined(GCAL_TCAP) || !USE_HLS)
       && pager_flag
#    endif
       && is_tty1
       && is_tty2)
    {
      register int    hls_pos=-1;
      register int    hls1_pos=-1;
      register int    hls2_pos=-1;
      register int    hls_chars=0;
      register int    i=0;
      register int    nl;
      register int    j;
#  if USE_PAGER
      register int    k;
      static   int    lines_printed=0;
#  endif
      auto     char  *ptr_1hls=(char *)NULL;
      auto     char  *ptr_2hls=(char *)NULL;
      auto     Bool   hls_start;
#  if USE_PAGER
      auto     Bool   print_hls=(Bool)(is_tty&&highlight_flag&&!emu_hls);
#  endif
      auto     Bool   nl_found=FALSE;
      auto     Bool   buf_highlight_flag=highlight_flag;


#  if defined(GCAL_TCAP) && USE_HLS
      fp_outchar = fp;
#  endif
      LOOP
       {
         hls_chars=nl=j = 0;
         if (   highlight_flag
             && !emu_hls)
          {
            /*
               Try to detect a highlighting sequence,
                 store its position and point to it...
            */
            hls_pos=hls1_pos=hls2_pos = -1;
            if (*(text_line + i))
             {
               ptr_1hls = strstr(text_line+i, ehls1s.seq);
               if (ptr_1hls != (char *)NULL)
                 hls1_pos = i + (int)(ptr_1hls - (text_line + i));
               ptr_2hls = strstr(text_line+i, ehls2s.seq);
               if (ptr_2hls != (char *)NULL)
                 hls2_pos = i + (int)(ptr_2hls - (text_line + i));
               if (   (hls1_pos != -1)
                   && (hls2_pos != -1))
                {
                  if (hls1_pos > hls2_pos)
                    hls_pos = hls2_pos;
                  else
                    hls_pos = hls1_pos;
                }
               else
                 if (hls1_pos != -1)
                   hls_pos = hls1_pos;
                 else
                   if (hls2_pos != -1)
                     hls_pos = hls2_pos;
             }
            if (hls_pos == -1)
              highlight_flag = FALSE;
          }
         /*
            No real highlighting sequence found AND
              either: no '\n'-NEWLINE character found
                  or: no "pager_flag" given
                  or: "pager_flag" given but we use an external paging program
              --> Let's print the complete line at once!
         */
         if (   (   strchr(text_line+i, '\n') == (char *)NULL
#  if USE_PAGER
                 || !pager_flag 
#    ifdef GCAL_EPAGER
                 || (   pager_flag
                     && (ext_pager != (char *)NULL))
#    endif
#  endif
                )
             && (hls_pos == -1))
          {
            fprintf(fp, "%s\n", text_line+i);
            i = (int)strlen(text_line);
          }
         else
          {
            /*
               Otherwise we have to inspect each character of the line
                 whether it's part of a real highlighting sequence or not
                 (because of managing a potential padding) and to count
                 the NEWLINES so we are able to page the output internally.
            */
            hls_start = TRUE;
            while (*(text_line + i))
             {
               /*
                  `\n'-NEWLINE character found (actual line must be a RC
                  fixed date => TILDE-character expansion was performed):
                    Print this NEWLINE character.
               */
               if (*(text_line + i) == '\n')
                {
                  i++;
                  if (j)
                    nl++;
                  nl_found = TRUE;
                  break;
                }
               /*
                  Look for a real highlighting sequence;
                    if found, store its length in `hls_chars'
                    and print it explicitly by means of tputs().
               */
               if (   highlight_flag
                   && !emu_hls
                   && (i == hls_pos))
                {
                  if (hls_pos == hls1_pos)
                   {
                     if (hls_start)
                      {
#  if defined(GCAL_TCAP) && USE_HLS
                        tputs((char *)ehls1s.seq, 1, outchar);
#  else /* !GCAL_TCAP || !USE_HLS */
                        fputs(ehls1s.seq, fp);
#  endif /* !GCAL_TCAP || !USE_HLS */
                        hls_chars += ehls1s.len;
                        i += ehls1s.len;
                        ptr_1hls = strstr(text_line+i, ehls1e.seq);
                        if (ptr_1hls != (char *)NULL)
                          hls_pos=hls1_pos = i + (int)(ptr_1hls - (text_line + i));
                        else
                          hls_pos = hls2_pos;
                        hls_start = FALSE;
                      }
                     else
                      {
#  if defined(GCAL_TCAP) && USE_HLS
                        tputs((char *)ehls1e.seq, 1, outchar);
#  else /* !GCAL_TCAP || !USE_HLS */
                        fputs(ehls1e.seq, fp);
#  endif /* !GCAL_TCAP || !USE_HLS */
                        hls_chars += ehls1e.len;
                        i += ehls1e.len;
                        ptr_1hls = strstr(text_line+i, ehls1s.seq);
                        if (ptr_1hls != (char *)NULL)
                          hls_pos=hls1_pos = i + (int)(ptr_1hls - (text_line + i));
                        else
                          hls_pos = hls2_pos;
                        hls_start = TRUE;
                      }
                     if (*(text_line + i))
                       fputc(*(text_line+i), fp);
                     else
                       break;
                   }
                  else
                    if (hls_pos == hls2_pos)
                     {
                       if (hls_start)
                        {
#  if defined(GCAL_TCAP) && USE_HLS
                          tputs((char *)ehls2s.seq, 1, outchar);
#  else /* !GCAL_TCAP || !USE_HLS */
                          fputs(ehls2s.seq, fp);
#  endif /* !GCAL_TCAP || !USE_HLS */
                          hls_chars += ehls2s.len;
                          i += ehls2s.len;
                          ptr_2hls = strstr(text_line+i, ehls2e.seq);
                          if (ptr_2hls != (char *)NULL)
                            hls_pos=hls2_pos = i + (int)(ptr_2hls - (text_line + i));
                          else
                            hls_pos = hls1_pos;
                          hls_start = FALSE;
                        }
                       else
                        {
#  if defined(GCAL_TCAP) && USE_HLS
                          tputs((char *)ehls2e.seq, 1, outchar);
#  else /* !GCAL_TCAP || !USE_HLS */
                          fputs(ehls2e.seq, fp);
#  endif /* !GCAL_TCAP || !USE_HLS */
                          hls_chars += ehls2e.len;
                          i += ehls2e.len;
                          ptr_2hls = strstr(text_line+i, ehls2s.seq);
                          if (ptr_2hls != (char *)NULL)
                            hls_pos=hls2_pos = i + (int)(ptr_2hls - (text_line + i));
                          else
                            hls_pos = hls1_pos;
                          hls_start = TRUE;
                        }
                       if (*(text_line + i))
                         fputc(*(text_line+i), fp);
                       else
                         break;
                     }
                }
               else
                 /*
                    Otherwise, print the actual character in the string.
                 */
                 fputc(*(text_line+i), fp);
               j++;
               i++;
             }
            S_NEWLINE(fp);
          }
#  if USE_PAGER
         if (   pager_flag
#    ifdef GCAL_EPAGER
             && (ext_pager == (char *)NULL)
#    endif
             )
          {
            /*
               Must we prompt the user?
            */
            if (!tty_am)
              j = 0;
            else
             {
               k = ((nl_found) ? j : i) - nl - hls_chars;
               j = k / tty_cols;
               if (   j
                   && tty_xn
                   && !(k % tty_cols))
                 j--;
             }
            if (lines_printed+j >= tty_rows-1)
             {
               if (print_hls)
#    if defined(GCAL_TCAP) && USE_HLS
                 tputs((char *)ehls1s.seq, 1, outchar);
#    else /* !GCAL_TCAP || !USE_HLS */
                 fputs(ehls1s.seq, fp);
#    endif /* !GCAL_TCAP || !USE_HLS */
#    if USE_GER
               fprintf(fp, "Weiter mit <Return> , <%s> zum Beenden...", PAGER_QUIT);
#    else /* !USE_GER */
               fprintf(fp, _("<Return> for more , <%s> to quit..."), PAGER_QUIT);
#    endif /* !USE_GER */
               if (print_hls)
#    if defined(GCAL_TCAP) && USE_HLS
                 tputs((char *)ehls1e.seq, 1, outchar);
#    else /* !GCAL_TCAP || !USE_HLS */
                 fputs(ehls1e.seq, fp);
#    endif /* !GCAL_TCAP || !USE_HLS */
               k = fgetc(stdin);
               /*
                  Quit the pager by the quit "key" command.
               */
               if (tolower(k) == (int)*PAGER_QUIT)
                {
                  /*
                     In case a leading "quit" character and other text is in the stdin buffer:
                       Clean the whole buffer.
                  */
                  while (   ((k=fgetc(stdin)) != '\n')
                         && (k != EOF))
                    ;   /* Void */
                  k = EOF;
                }
               else
                 /*
                    In case a "quit" character is not leading (anywhere) in the stdin buffer:
                      Clean the buffer until the "quit" character isn't found.
                 */
                 if (k != '\n')
                   while (   ((k=fgetc(stdin)) != '\n')
                          && (tolower(k) != (int)*PAGER_QUIT)
                          && (k != EOF))
                     ;   /* Void */
               /*
                  In case the "quit" character is found in the stdin buffer now:
                    Clean up the rest of buffer (until its end).
               */
               if (tolower(k) == (int)*PAGER_QUIT)
                {
                  while (   ((k=fgetc(stdin)) != '\n')
                         && (k != EOF))
                    ;   /* Void */
                  k = EOF;
                }
               /*
                  Exit the program with success if the "quit" character was found in the stdin buffer.
               */
               if (k == EOF)
                 exit(0);
               /*
                  Begin scrolling of the next page.
               */
               lines_printed = 0;
             }
            else
              lines_printed += (j + 1);
            if (!*(text_line + i))
              break;
          }
         else
#  endif /* USE_PAGER */
           if (!*(text_line + i))
             break;
       }
      highlight_flag = buf_highlight_flag;
    }
   else
#endif /* USE_PAGER || (GCAL_TCAP && USE_HLS) */
    {
      /*
         If the mailing option is selected, print the output to the temporary file.
      */
      if (   (tfp != (FILE *)NULL)
          && (fp != (FILE *)stderr))
        fprintf(tfp, "%s\n", text_line);
      else
        fprintf(fp, "%s\n", text_line);
    }
   *text_line = '\0';
}



   PUBLIC void
get_tty_hls (sequence_str)
   const char *sequence_str;
/*
   Reads the colours/highlighting sequences from Termcap and assigns them
     to the according variables.  If Termcap isn't present, defaults are used.
*/
{
#if USE_HLS
   auto char  *ptr_env=getenv(ENV_VAR_GCALANSI);
#  ifdef GCAL_TCAP
   auto Bool   check_again=FALSE;  
#  endif
#endif
   auto Bool   hls1_set=FALSE;
   auto Bool   hls2_set=FALSE;


   /*
      Check whether highlighting must be disabled (-H<no> given in command line).
   */
   if (!highlight_flag)
    {
      emu_hls = TRUE;
      ehls1s.seq = NO_HLS;
      ehls1e.seq = NO_HLS;
      ehls2s.seq = NO_HLS;
      ehls2e.seq = NO_HLS;
    }
   else
    {
      /*
         If output is not directed to a tty, emulate highlighting sequences
           by marking characters.
      */
      if (   !is_tty
          && !emu_hls
          && highlight_flag)
        emu_hls = TRUE;
      /*
         Check whether user defined highlighting sequences are given
           in command line (-H<def>).
      */
      if (sequence_str != (char *)NULL)
       {
         get_hl_seq (sequence_str, &hls1_set, &hls2_set);
#if USE_HLS && defined(GCAL_TCAP)
         if (   (   hls1_set
                 && (  ehls1s.len > 1
                     || ehls1e.len > 1))
             || (   hls2_set
                 && (   ehls2s.len > 1
                     || ehls2e.len > 1)))
           check_again = TRUE;
#endif
       }
      /*
         No or partitial highlighting sequences are given in command line
           -> Complete them (or if a padding was given, init the necessary
              functions for managing it).
      */
      if (   !hls1_set
#if USE_HLS && defined(GCAL_TCAP)
          || check_again
#endif
          || !hls2_set)
       {
#if USE_HLS
#  ifdef GCAL_TCAP
         if (!emu_hls)
          {
            /*
               Try to open the [/etc/]termcap file.
            */
            tc_no_error = open_termcap ();
            if (tc_no_error)
              get_termcap_hls (&hls1_set, &hls2_set);
            if (check_again)
             {
               get_hl_seq (sequence_str, &hls1_set, &hls2_set);
               if (tc_no_error)
                 get_termcap_hls (&hls1_set, &hls2_set);
             }
            if (ptr_env != (char *)NULL)
             {
               /*
                  Some or all Termcap highlighting sequences are missing:
                    Use according "default" ANSI highlighting sequences
                    if the environment variable $GCALANSI is set.
               */
               if (!hls1_set)
                {
                  ehls1s.seq = HLS1S;
                  ehls1e.seq = HLS1E;
                }
               if (!hls2_set)
                {
                  ehls2s.seq = HLS2S;
                  ehls2e.seq = HLS2E;
                }
             }
            else
              if (   !hls1_set
                  || !hls2_set)
               {
                 /*
                    Some or all Termcap highlighting sequences are missing:
                      Emulate ALL highlighting sequences by marking character
                      pairs if the environment variable $GCALANSI isn't set.
                 */
                 emu_hls = TRUE;
                 ehls1s.seq = BUF_HLS1S;
                 ehls1e.seq = BUF_HLS1E;
                 ehls2s.seq = BUF_HLS2S;
                 ehls2e.seq = BUF_HLS2E;
               }
          }
         else
#  endif /* GCAL_TCAP */
          {
            if (emu_hls)
             {
               /*
                  Use emulation of highlighting sequences
                    in case output is not directed to a tty and the highlighting
                    sequences are not explicit disabled in command line by -H<no>.
               */
               if (!hls1_set)
                {
                  ehls1s.seq = BUF_HLS1S;
                  ehls1e.seq = BUF_HLS1E;
                }
               if (!hls2_set)
                {
                  ehls2s.seq = BUF_HLS2S;
                  ehls2e.seq = BUF_HLS2E;
                }
             }
            else
             {
               /*
                  Use highlighting sequences directly in all other cases.
               */
               if (ptr_env != (char *)NULL)
                {
                  /*
                     If environment variable $GCALANSI is set:
                       Use the according "default" ANSI highlighting sequences.
                  */
                  if (!hls1_set)
                   {
                     ehls1s.seq = HLS1S;
                     ehls1e.seq = HLS1E;
                   }
                  if (!hls2_set)
                   {
                     ehls2s.seq = HLS2S;
                     ehls2e.seq = HLS2E;
                   }
                }
               else
                 if (   !hls1_set
                     || !hls2_set)
                  {
                    /*
                       If environment variable $GCALANSI is not set:
                         Emulate ALL highlighting sequences by marking character pairs.
                    */
                    emu_hls = TRUE;
                    ehls1s.seq = BUF_HLS1S;
                    ehls1e.seq = BUF_HLS1E;
                    ehls2s.seq = BUF_HLS2S;
                    ehls2e.seq = BUF_HLS2E;
                  }
             }
          }
#else /* !USE_HLS */
         /*
            Use the default highlighting sequences.
         */
         if (!hls1_set)
          {
            ehls1s.seq = HLS1S;
            ehls1e.seq = HLS1E;
          }
         if (!hls2_set)
          {
            ehls2s.seq = HLS2S;
            ehls2e.seq = HLS2E;
          }
#endif /* !USE_HLS */
       }
    }
   /*
      Detect and store length of the highlighting sequences.
   */
#if defined(GCAL_TCAP) && USE_HLS
   /*
       If padding is specified in Termcap and output is not connected to a tty,
         avoid to emit the padding information itself in the output, because
         it's senseless to redirect/pipe such an information to the consumser,
         who doesn't interpret it!
   */
   if (   is_padding
       && (   !is_tty
           || !is_tty1
           || !is_tty2))
    {
      ehls1s.seq = (const char *)skip_leading_padding_info ((char *)ehls1s.seq);
      ehls1e.seq = (const char *)skip_leading_padding_info ((char *)ehls1e.seq);
      ehls2s.seq = (const char *)skip_leading_padding_info ((char *)ehls2s.seq);
      ehls2e.seq = (const char *)skip_leading_padding_info ((char *)ehls2e.seq);
    }
#endif /* !GCAL_TCAP || !USE_HLS */
   ehls1s.len = (int)strlen(ehls1s.seq);
   ehls1e.len = (int)strlen(ehls1e.seq);
   ehls2s.len = (int)strlen(ehls2s.seq);
   ehls2e.len = (int)strlen(ehls2e.seq);
}



#if USE_PAGER
   PUBLIC void
get_tty_scr_size (rows, cols)
   int *rows;
   int *cols;
/*
   Detects the number of rows and columns of a tty
     and stores the values found in `rows' and `cols'.
*/
{
#  if !defined(AMIGA) || defined(__GNUC__)
   register int    li=0;
   register int    co=0;
   auto     char  *ptr_env;


   /*
      First look into the environment variable pair $LINES and $COLUMNS
      resp., $LI and $CO in case these are defined and have valid settings:
        Use these settings.
   */
   ptr_env = getenv(ENV_VAR_LI);
   if (ptr_env != (char *)NULL)
     if (*ptr_env)
      {
        li = my_atoi (ptr_env);
        ptr_env = getenv(ENV_VAR_CO);
        if (ptr_env != (char *)NULL)
          if (*ptr_env)
            co = my_atoi (ptr_env);
      }
   if (   (li > 0)
       && (co > 0))
    {
      *rows = li;
      *cols = co;
    }
   else
    {
      ptr_env = getenv(ENV_VAR_LI2);
      if (ptr_env != (char *)NULL)
        if (*ptr_env)
         {
           li = my_atoi (ptr_env);
           ptr_env = getenv(ENV_VAR_CO2);
           if (ptr_env != (char *)NULL)
             if (*ptr_env)
               co = my_atoi (ptr_env);
         }
      if (   (li > 0)
          && (co > 0))
       {
         *rows = li;
         *cols = co;
       }
      else
       {
#    if defined(OS2) && defined(__GNUC__)
         auto int  info[2];
#    endif /* OS2 && __GNUC__ */
#    if defined(UNIX) && !defined(DJG)
#      ifdef TIOCGWINSZ
         auto struct winsize  wsz;
#      else /* !TIOCGWINSZ */
#        ifdef WIOCGETD
         auto struct uwdata   wsz;
#        endif
#      endif /* !TIOCGWINSZ */
#    endif /* UNIX && !DJG */


#    if !defined(DJG) && !defined(MSDOS) && !defined(OS2) && !defined(UNIX)
         /*
            For these machines: Defaults only!
         */
         *rows = SCREEN_ROWS;
         *cols = SCREEN_COLS;
#    else /* DJG || MSDOS || OS2 || UNIX */
#      ifdef DJG
         /*
            Get the actual number of lines and columns of the video
              by calling the DJGPP-GCC ScreenRows() and ScreenCols() functions.
         */
         *rows = ScreenRows();
         *cols = ScreenCols();
#      else /* !DJG */
#        if defined(MSDOS)
         /*
            Look directly into the PC-BIOS and get the actual number
              of lines and columns of the video.
         */
         *rows = peek_byte (0x40, 0x84) + 1;
         /*
            Get lower part of 2-byte word.
         */
         *cols = peek_byte (0x40, 0x4b);
         *cols <<= (Ulint)0x08;
         /*
            Add higher part of 2-byte word.
         */
         *cols += peek_byte (0x40, 0x4a);
#        else /* !MSDOS */
#          if defined(OS2) && defined(__GNUC__)
         /*
            Get the actual number of lines and columns of the
              video by calling the EMX-GCC _scrsize() function.
         */
         _scrsize(info);
         *cols = s1[0];
         *rows = s1[1];
#          else /* !OS2 || !__GNUC__ */
#            if defined(UNIX)
         /*
            Get the actual number of lines and columns of the
              video by calling the ioctl() function.
         */
#              ifdef TIOCGWINSZ
         if (   !ioctl(1, TIOCGWINSZ, &wsz)
             && (wsz.ws_row > 0))
          {
            *rows = wsz.ws_row;
            if (   !ioctl(1, TIOCGWINSZ, &wsz)
                && (wsz.ws_col > 0))
              *cols = wsz.ws_col;
            else
              *rows = -1;
          }
#              else /* !TIOCGWINSZ */
#                ifdef WIOCGETD
         if (   !ioctl(1, WIOCGETD, &wsz)
             && (wsz.uw_height > 0))
          {
            *rows = wsz.uw_height / wsz.uw_vs;
            if (   !ioctl(1, WIOCGETD, &wsz)
                && (wsz.uw_width > 0))
              *cols = wsz.uw_width / wsz.uw_hs;
            else
              *rows = -1;
          }
#                endif
#              endif /* !TIOCGWINSZ */
#            endif /* UNIX */
#          endif /* !OS2 || !__GNUC__ */
#        endif /* !MSDOS */
#      endif /* !DJG */
         if (   (*rows == -1)
             && (*cols == -1))
          {
#      if HAVE_LIBTERMCAP || HAVE_LIBTERMLIB
            /*
               If the previous actions have failed, try to open the  [/etc/]termcap file.
            */
            tc_no_error = open_termcap ();
            if (tc_no_error)
             {
               if (!get_termcap_scr_attrib (rows, cols))
                {
                  /*
                     No valid Termcap entries: Defaults only!
                  */
                  *rows = SCREEN_ROWS;
                  *cols = SCREEN_COLS;
                }
             }
            else
             {
               /*
                  Access to [/etc/]termcap file has failed: Defaults only!
               */
               *rows = SCREEN_ROWS;
               *cols = SCREEN_COLS;
             }
#      else /* !HAVE_LIBTERMCAP && !HAVE_LIBTERMLIB */
            /*
               No [/etc/]termcap file available: Defaults only!
            */
            *rows = SCREEN_ROWS;
            *cols = SCREEN_COLS;
#      endif /* !HAVE_LIBTERMCAP && !HAVE_LIBTERMLIB */
          }
#    endif /* DJG || MSDOS || OS2 || UNIX */
       }
    }
#  else /* AMIGA && !__GNUC__ */
#    ifdef AMIGA
   /*
      Amiga gets the window size by asking the console.device.
   */
   {
     auto long  len;
     auto char  buf[30];


     Write(Output(), "\2330 q", 4);
     len = Read(Input(), buf, 29);
     buf[len] = '\0';
     sscanf(&buf[5], "%d;%d", rows, cols);
   }
#    else /* !AMIGA */
   /*
      All other systems: Defaults only.
   */
   *rows = SCREEN_ROWS;
   *cols = SCREEN_COLS;
#    endif /* !AMIGA */
#  endif /* AMIGA && !__GNUC__ */
   if (*rows > 1)
     (*rows)--;
}
#endif /* USE_PAGER */



#if defined(GCAL_TCAP) && USE_HLS
   LOCAL char *
skip_leading_padding_info (sequence_str)
   char *sequence_str;
/*
   Skip the padding information part of a sequence.
*/
{
   if (isdigit(*sequence_str))
    {
      is_padding = TRUE;
      while (isdigit(*sequence_str))
        sequence_str++;
      if (*sequence_str == '.')
       {
         sequence_str++;
         if (isdigit(*sequence_str))
           sequence_str++;
       }
      if (*sequence_str == '*')
        sequence_str++;
    }

   return sequence_str;
}
#endif /* GCAL_TCAP && USE_HLS */



#if USE_PAGER || USE_HLS
#  ifdef GCAL_TCAP
   LOCAL Bool
open_termcap ()
/*
   Tries to open the Termcap library and returns the terminal entry found
     in the module global vector `tc_buf[]'.  I cannot rely that we use the
     access functions of the GNU Termcap library; which allow to pass
     a NULL pointer to `tgetent()', so this function can check itself, how
     large `tc_buf[]' must be and allocates it automatically; so i set
     `tc_buf' to a size of 4096 (TC_BUFLEN) bytes hoping, that this will be
     enough for save program operation...
     May be called only once.
     Returns FALSE if an error has occurred, otherwise TRUE.
*/
{
#    if defined(OS2) && defined(__GNUC__)
   auto   char  *ptr_env=getenv(ENV_VAR_TCAP);
   auto   char  *ptr_tc;
#    endif /* OS2 && __GNUC__ */
   auto   char  *term=getenv(ENV_VAR_TERM);
   static Bool   func_accessed=FALSE;
   auto   Bool   is_error=FALSE;


   if (!func_accessed)
    {
      func_accessed = TRUE;
#    if defined(OS2) && defined(__GNUC__)
      /*
         Under OS/2 with GNU-C, we use the default terminal type (ANSI)
           and access the Termcap library instead of printing an informational
           message and using burned-in defaults if the $TERM environment
           variable isn't set.
      */
      if (   term == (char *)NULL
          || !*term)
        term = DFLT_TERM;
#    else /* !OS2 || !__GNUC__ */
      if (term == (char *)NULL)
       {
#      if USE_GER
         if (warning_level >= 0)
           fprintf(stderr, "\nUmgebungsvariable `%s' nicht vorhanden", ENV_VAR_TERM);
#      else /* !USE_GER */
         if (warning_level >= 0)
           fprintf(stderr, _("\nEnvironment variable `%s' not found"), ENV_VAR_TERM);
#      endif /* !USE_GER */
         is_error = TRUE;
       }
      else
        if (!*term)
         {
#      if USE_GER
           if (warning_level >= 0)
             fprintf(stderr, "\nUmgebungsvariable `%s' nicht gesetzt", ENV_VAR_TERM);
#      else /* !USE_GER */
           if (warning_level >= 0)
             fprintf(stderr, _("\nEnvironment variable `%s' not set"), ENV_VAR_TERM);
#      endif /* !USE_GER */
           is_error = TRUE;
         }
        else
#    endif /* !OS2 || !__GNUC__ */
         {
#    if defined(OS2) && defined(__GNUC__)
           /*
              Make sure the Termcap database is available, i.e. store its access path
                in the environment explicitly so we are able to refer it.
           */
           if (   ptr_env == NULL
               || !*ptr_env)
            {
              ptr_env = (char *)my_malloc (256,
                                           124, __FILE__, ((long)__LINE__)-1,
                                           "ptr_env", 0);
              _searchenv(FNAME_TCAP, "INIT", ptr_env);
              if (!*ptr_env)
                _searchenv(FNAME_TCAP, ENV_VAR_PATH, ptr_env);
              if (!*ptr_env)
                _searchenv(FNAME_TCAP, ENV_VAR_DPATH, ptr_env);
              if (*ptr_env)
               {
                 ptr_tc = (char *)my_malloc (strlen(ptr_env)+9,
                                             124, __FILE__, ((long)__LINE__)-1,
                                             "ptr_tc", 0);
                 sprintf(ptr_tc, ENV_VAR_TCAP"=%s", ptr_env);
                 putenv(ptr_tc);
               }
              free(ptr_env);
            }
#    endif /* OS2 && __GNUC__ */
           switch (tgetent(tc_buf, term))
            {
              case -1:
#    if USE_GER
                if (warning_level >= 0)
                  fputs("\n`termcap' Datei wurde nicht vorgefunden", stderr);
#    else /* !USE_GER */
                if (warning_level >= 0)
                  fputs(_("\n`termcap' file not found"), stderr);
#    endif /* !USE_GER */
                is_error = TRUE;
                break;
              case 0:
#    if USE_GER
                if (warning_level >= 0)
                  fprintf(stderr, "\nUnbekannter Terminaltyp in `%s' eingetragen", ENV_VAR_TERM);
#    else /* !USE_GER */
                if (warning_level >= 0)
                  fprintf(stderr, _("\nUnknown terminal type defined in `%s'"), ENV_VAR_TERM);
#    endif /* !USE_GER */
                is_error = TRUE;
                break;
              default:
                ;   /* Void, Termcap access ok */
            }
         }
      if (   is_error
          && (warning_level >= 0))
        fputs(".\n\n", stderr);
      return (Bool)!is_error;
    }

   return tc_no_error;
}



#    if USE_HLS
   LOCAL void
get_ospeed ()
/*
   Try to detect the terminal speed and store its value to
     Termcap's global `ospeed' variable.
*/
{
#      if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
   auto struct termios  buf;


   /*
      Get the terminal mode.
   */
   tcgetattr(1, &buf);
   /*
      Get ospeed!
   */
#        if HAVE_OSPEED
   switch (cfgetospeed(&buf))
    {
#          ifdef B0
      case B0:
        ospeed = 0;
        break;
#          endif
#          ifdef B50
      case B50:
        ospeed = 1;
        break;
#          endif
#          ifdef B75
      case B75:
        ospeed = 2;
        break;
#          endif
#          ifdef B110
      case B110:
        ospeed = 3;
        break;
#          endif
#          ifdef B134
      case B134:
        ospeed = 4;
        break;
#          endif
#          ifdef B150
      case B150:
        ospeed = 5;
        break;
#          endif
#          ifdef B200
      case B200:
        ospeed = 6;
        break;
#          endif
#          ifdef B300
      case B300:
        ospeed = 7;
        break;
#          endif
#          ifdef B600
      case B600:
        ospeed = 8;
        break;
#          endif
#          ifdef B1200
      case B1200:
        ospeed = 9;
        break;
#          endif
#          ifdef B1800
      case B1800:
        ospeed = 10;
        break;
#          endif
#          ifdef B2400
      case B2400:
        ospeed = 11;
        break;
#          endif
#          ifdef B4800
      case B4800:
        ospeed = 12;
        break;
#          endif
#          ifdef B9600
      case B9600:
        ospeed = 13;
        break;
#          endif
#          ifdef EXTA
      case EXTA:
        ospeed = 14;
        break;
#          endif
#          ifdef EXTB
      case EXTB:
        ospeed = 15;
        break;
#          endif
#          ifdef B57600
      case B57600:
        ospeed = 16;
        break;
#          endif
#          ifdef B115200
      case B115200:
        ospeed = 17;
        break;
#          endif
      default:
        ;   /* Void */
    }
#        endif /* HAVE_OSPEED */
#      else /* !HAVE_TERMIOS_H || !HAVE_TERMIOS_FUNC */
#        if TCGETA
   auto struct termio  buf;


   /*
      Get the terminal mode.
   */
   ioctl(1, TCGETA, &buf);
   /*
      Get ospeed!
   */
#          if HAVE_OSPEED
   ospeed = buf.c_cflag & CBAUD;
#          endif
#        else /* !TCGETA */
   auto struct sgttyb  buf;


   /*
      Get the terminal mode.
   */
   ioctl(1, TIOCGETP, &buf);
   /*
      Get ospeed!
   */
#          if HAVE_OSPEED
   ospeed = buf.sg_ospeed;
#          endif
#        endif /* !TCGETA */
#      endif /* !HAVE_TERMIOS_H || !HAVE_TERMIOS_FUNC */
}



   LOCAL int
outchar (ch)
   int ch;
/*
   Termcap's `tputs()' function prints a character to the module local defined
     file `fp_outchar', which must be assigned before using `tputs()' itself.
*/
{
  return fputc(ch, fp_outchar);
}



   LOCAL Bool
get_termcap_hls (hls1_set, hls2_set)
   Bool *hls1_set;
   Bool *hls2_set;
/*
   Inspects the Termcap buffer `tc_buf' to detect the tty colour/highlighting
     sequences.  The module global vector `tc_buf[]' must be filled previously.
     May be called only once.
     Returns FALSE if an error has occured, otherwise TRUE.
*/
{
   register int    i=(*hls1_set) ? 2 : 0;
   register int    j=(*hls2_set) ? TC_MC_MAX-2 : TC_MC_MAX;
   static   char  *tc[TC_MC_MAX]={
                                   TC_MC_HL1S,
                                   TC_MC_HL1E,
                                   TC_MC_HL2S,
                                   TC_MC_HL2E
                                 };
#      if HAVE_OSPEED
   static   char  *padding;
#      endif
   static   char  *area;
   auto     char  *ptr_char;
   auto     char  *ptr_area;
   static   Bool   func_accessed=FALSE;
   auto     Bool   is_error=FALSE;


   if (!func_accessed)
    {
      func_accessed = TRUE;
      area = (char *)my_malloc (TC_BUFLEN,
                                124, __FILE__, ((long)__LINE__)-1,
                                "area", 0);
      ptr_area = area;
#      if HAVE_OSPEED
      /*
         Get the padding sequence.
      */
      padding = tgetstr("pc", &ptr_area);
      PC = (padding) ? *padding : '\0';
      /*
         Get the terminal speed.
      */
      get_ospeed ();
#      endif
      for ( ; (i < j) && !is_error ; i++)
       {
         ptr_char = tgetstr(tc[i], &ptr_area);
         if (ptr_char != (char *)NULL)
          {
            switch (i)
             {
               case 0:
                 ehls1s.seq = ptr_char;
                 break;
               case 1:
                 ehls1e.seq = ptr_char;
                 *hls1_set = TRUE;
                 break;
               case 2:
                 ehls2s.seq = ptr_char;
                 break;
               case 3:
                 ehls2e.seq = ptr_char;
                 *hls2_set = TRUE;
                 break;
               default:
                 /*
                    Error, more then 2 highlight sequence pairs given.
                 */
                 is_error = TRUE;
             }
          }
         else
           /*
              Error, no terminal capability string found for mode `tc[i]'.
           */
           is_error = TRUE;
       }
    }

   return (Bool)!is_error;
}
#    endif /* USE_HLS */



#    if USE_PAGER
   LOCAL Bool
get_termcap_scr_attrib (rows, cols)
   int *rows;
   int *cols;
/*
   Inspects the Termcap buffer `tc_buf' to detect first the amount of rows
     and columns of the tty, then whether the terminal wraps the line
     automatically at right margin.  If Termcap isn't present, defaults are
     used.  The module global vector `tc_buf[]' must be filled previously.
     May be called only once.
     Returns FALSE if an error has occured, otherwise TRUE.
*/
{
   static Bool  func_accessed=FALSE;


   if (!func_accessed)
    {
      func_accessed = TRUE;
      /*
         Get the amount of tty rows and columns.
      */
      if (   tgetnum("li") == -1
          || tgetnum("co") == -1)
        return FALSE;
      else
       {
         *rows = tgetnum("li");
         *cols = tgetnum("co");
         /*
            Check whether tty wraps the line automatically
              at right margin and ignores newline after wrapping.
         */
         tty_am = tgetflag("am");
         tty_xn = tgetflag("xn");
       }
    }

   return TRUE;
}
#    endif /* USE_PAGER */
#  else /* !GCAL_TCAP */
#    if defined(MSDOS) && USE_PAGER
   LOCAL Uchar
peek_byte (segment, offset)
   Uint segment;
   Uint offset;
/*
   Gets a byte of ibm/pc-memory from address (segment:offset).
*/
{
   auto Ulint       long_tmp;
   auto Uchar far  *ptr_char;


   long_tmp = (Ulint)segment;
   long_tmp <<= (Ulint)0x10;
   long_tmp += (Ulint)offset;
   ptr_char = (Uchar far *)long_tmp;

   return *ptr_char;
}
#    endif /* MSDOS && USE_PAGER */
#  endif /* !GCAL_TCAP */
#endif /* USE_PAGER || USE_HLS */



   LOCAL void
get_hl_seq (sequence_str, hls1_set, hls2_set)
   const char *sequence_str;
         Bool *hls1_set;
         Bool *hls2_set;
/*
   Highlighting sequences/marking characters are given in command line
   (-H<seq1_start:seq1_end:seq2_start:seq2_end> option set),
   i.e.:  the colon separated string `sequence_str', which should contain
          highlighting sequence/marking character pairs (2 pairs maximum,
          first for actual day, second for holiday; seq?_start enables,
          seq?_end disables), is delivered,
          e.g.:   \x20:\x20:\x1:#
                    marks holiday date like:  \x1`date'#
                    using given marking characters.
          e.g.:   \x1b[34;42m:\x1b[0;40m      or
                  \033[34;42m:\033[0;40m      or
                  \E[34;42m:\E[0;40m
                    thus all defines starting (ANSI)escape highlighting sequence
                    \x1b[34;42m used for actual day and ending (ANSI)escape
                    highlighting sequence \x1b[0;40m with no given highlighting
                    sequence for holiday, so default highlighting sequences
                    for holiday are used (unnotated entries are always skipped).
          Control code definitions may contain any printable characters.
          Non-printable characters may be encoded in octal or hexadecimal
          notation.  The abbreviation \E resp., \e directly encodes the escape
          character (\x1B resp., \033).
          A character can be encoded octal by typing \NNN (BACKSLASH-octal
          digit(s)), where N must be a valid octal digit (0...7).  Normally,
          3 octal digits must be given.  If the octal character code consists
          of 1 or 2 octal digits, leading zeroes must be added; except the case,
          the encoded octal character is given last in single sequence.
          A character can be encoded hexadecimal by typing \xNN (BACKSLASH-x-
          hexadecimal digit(s)), where N must be a valid hexadecimal digit
          (0...9A...Fa...f).  Normally, 2 hexadecimal digits must be given.
          If the hexadecimal character code consists of 1 hexadecimal digit,
          a leading zero must be added; except the case, the encoded
          hexadecimal character is given last in single sequence.
          If the sequence separator character, the colon `:' character
          itself is used for marking character, it must be specified
          either octal by \072 or hexadecimal by \x3A.
   Get, convert and store them in the global hls structs.
*/
{
   register       int    i=0;
   register       int    n;
   register       int    j;
   register       int    k;
   register       int    diff;
   register       int    seq_no=0;
   static         char  *buf_hls[TC_MC_MAX];
   auto     const char  *ptr_char=sequence_str;
   auto           char  *ptr_err=(char *)NULL;
   auto           char   hc_text[5];
   auto           Bool   is_error=FALSE;
   auto           Bool   is_hex;
   auto           Bool   is_oct;
   auto           Bool   is_esc;


   *hls1_set=*hls2_set = FALSE;
   while (   *ptr_char
          && !is_error
          && (seq_no < TC_MC_MAX))
    {
      /*
         Copy highlighting sequence/marking character to temporary
           sequence string (until column found).
      */
      while(   *ptr_char
            && (*ptr_char != *SEP))
       {
         if ((Uint)i >= maxlen_max)
           resize_all_strings (maxlen_max<<1, __FILE__, (long)__LINE__);
         s1[i++] = *ptr_char++;
       }
      if ((Uint)i == maxlen_max)
        resize_all_strings (maxlen_max<<1, __FILE__, (long)__LINE__);
      if (*ptr_char)
        ptr_char++;
      if (i)
       {
         s1[i] = '\0';
         i=n = 0;
         /*
            Convert all textual:
              hex character sequences \xNN
              oct character sequences \NNN
              esc character sequences \E or \e
            found in sequence string to real characters
         */
         while (s1[i+n])
          {
            is_hex=is_oct=is_esc = FALSE;
            diff = 0;
            if (s1[i+n] == '\\')
             {
               is_esc = (Bool)(tolower(s1[i+n+1]) == 'e');
               is_hex = (Bool)(tolower(s1[i+n+1]) == 'x');
               is_oct = (Bool)isdigit(s1[i+n+1]);
             }
            if (is_esc)
             {
               s1[i] = '\033';   /* \x1b */
               n++;
             }
            else
              if (   is_hex
                  || is_oct)
               {
                 k = 0;
                 if (is_hex)
                  {
                    hc_text[k++] = '0';
                    hc_text[k++] = 'x';
                  }
                 else
                  {
                    if (s1[i+n+1] != '0')
                      hc_text[k++] = '0';
                    else
                      diff = 1;
                    hc_text[k++] = s1[i+n+1];
                  }
                 /*
                    Copy hex/oct digits to prefix.
                 */
                 j = i + n + 2;
                 while (   s1[j]
                        && (s1[j] != *SEP)
                        && (k < 4-diff))
                   hc_text[k++] = s1[j++];
                 hc_text[k] = '\0';
                 /*
                    Convert textual hex/oct character to decimal value.
                 */
#if HAVE_STRTOL
                 j = (int)strtol(hc_text, &ptr_err, 0);
#else /* !HAVE_STRTOL */
                 ptr_err = hc_text + 1;
                 if (is_hex)
                   ptr_err++;
                 j = sbyte2int (ptr_err, (is_hex) ? 16 : 8);
#endif /* !HAVE_STRTOL */
                 /*
                    If conversion error occurs (invalid chars in hex/oct character
                      sequence), don't store ALL sequences, which are previously decoded!
                 */
                 if (   j == -1
#if HAVE_STRTOL
                     || *ptr_err
#endif
                    )
                  {
                    is_error = TRUE;
                    break;
                  }
                 /*
                    Put converted character code back to sequence string.
                 */
                 s1[i] = (char)j;
                 n += (k - 2 + diff) + 1;
               }
            i++;
            s1[i] = s1[i+n];
          }
         if (!is_error)
          {
            /*
               Store highlighting sequence/marker character
                 in according global highlighting sequence
                 struct_variable using static buffer `buf_hls[]'.
            */
            n = (int)strlen(s1);
            buf_hls[seq_no] = (char *)my_malloc (n+1,
                                                 124, __FILE__, ((long)__LINE__)-1,
                                                 "buf_hls[seq_no]", seq_no);
            strcpy(buf_hls[seq_no], s1);
            switch (seq_no)
             {
               case 0:
                 ehls1s.seq = buf_hls[seq_no];
                 ehls1s.len = n;
                 break;
               case 1:
                 ehls1e.seq = buf_hls[seq_no];
                 ehls1e.len = n;
                 *hls1_set = TRUE;
                 break;
               case 2:
                 ehls2s.seq = buf_hls[seq_no];
                 ehls2s.len = n;
                 break;
               case 3:
                 ehls2e.seq = buf_hls[seq_no];
                 ehls2e.len = n;
                 *hls2_set = TRUE;
                 break;
               default:
                 ;   /* Void */
             }
          }
       }
      i = 0;
      seq_no++;
    }
   /*
      Either real highlighting sequences (ESC-char..., length > 1) only or
        marking characters (lenght == 1) only can be managed:
        Aoid mixture of both!
   */
#if USE_HLS
   if (*hls1_set)
     if (   (   (ehls1s.len == 1)
             && (ehls1e.len != 1))
         || (   (ehls1s.len != 1)
             && (ehls1e.len == 1)))
       *hls1_set = FALSE;
   if (*hls2_set)
     if (   (   (ehls2s.len == 1)
             && (ehls2e.len != 1))
         || (   (ehls2s.len != 1)
             && (ehls2e.len == 1)))
       *hls2_set = FALSE;
   if (*hls1_set)
    {
      if (   (ehls1s.len == 1)
          && (ehls1e.len == 1))
       {
         if (*hls2_set)
          {
            if (   (ehls2s.len == 1)
                && (ehls2e.len == 1))
              emu_hls = TRUE;
            else
             {
               if (emu_hls)
                 *hls2_set = FALSE;
               else
                 *hls1_set = FALSE;
             }
          }
         else
           if (!emu_hls)
             *hls1_set = FALSE;
       }
      else
       {
         if (*hls2_set)
          {
            if (   (ehls2s.len != 1)
                && (ehls2e.len != 1))
              ;  /* Void, ok */
            else
             {
               if (emu_hls)
                 *hls1_set = FALSE;
               else
                 *hls2_set = FALSE;
             }
          }
         else
           if (emu_hls)
             *hls1_set = FALSE;
       }
    }
   if (   !*hls1_set
       && *hls2_set)
    {
      if (   (ehls2s.len == 1)
          && (ehls2e.len == 1))
       {
         if (!emu_hls)
           *hls2_set = FALSE;
       }
      else
        if (emu_hls)
          *hls2_set = FALSE;
    }
   if (   *hls1_set
       && *hls2_set)
     if (   emu_hls
         && (ehls1s.len > 1)
         && (ehls2s.len > 1))
       *hls1_set=*hls2_set = FALSE;
#else /* !USE_HLS */
   if (*hls1_set)
     if (   ehls1s.len != 1
         || ehls1e.len != 1)
       *hls1_set = FALSE;
   if (*hls2_set)
     if (   ehls2s.len != 1
         || ehls2e.len != 1)
       *hls2_set = FALSE;
#endif /* !USE_HLS */
}



#if !HAVE_STRTOL
   LOCAL int
sbyte2int (string, base)
   const char *string;
   const int   base;
/*
   Converts a textual b-adic string `string', which contains the absolute
     textual representation of a byte given in number base `base' (2-16)
     to decimal base and returns its value converted to an "int type" in `res'.
     If an error occurs, -1 is returned.
*/
{
  register       int    val;
  register       int    res=0;
  register       int    mul=1;
  register       int    len=(int)strlen(string);
  register       int    len_base_str;
  static   const char   base_str[]="0123456789abcdef";
  auto     const char  *ptr_char=string+(len-1);


  len_base_str = (int)strlen(base_str);
  if (   !len
      || base < 2
      || base > len_base_str)
    return -1;
  for ( ; len ; len--)
   {
     if (strchr(base_str, tolower(*ptr_char)) == (char *)NULL)
       return -1;
     val = len_base_str - (int)strlen(strchr(base_str, *ptr_char--));
     if (val > base)
       return -1;
     res += (val * mul);
     mul *= base;
   }

  return res;
}
#endif /* !HAVE_STRTOL */



#if !HAVE_STRSTR
   LOCAL char *
my_strstr (text, pattern)
   const char *text;
   const char *pattern;
/*
   Search the `pattern' needle in haystack `text'   =8^)
     (emulates the ANSI C strstr() function; not very well optimized).
*/
{
   register       int    i=(int)strlen(text);
   register       int    j=(int)strlen(pattern);
   register       int    k;
   auto     const char  *ptr_char=text;


   if (!j)
     return ptr_char;
   if (i < j)
     return NULL;
   for (i=0 ; *ptr_char ; i++,ptr_char++)
    {
      for (k=0,j=i ; pat[k] && (text[j]==pattern[k]) ; j++,k++)
        ;
      if (   k
          && !pattern[k])
        return ptr_char;
    }

   return NULL;
}
#endif /* !HAVE_STRSTR */
