echo ; /*  Note that this declares an int available externally
 failat 1
;lc:lc -cuwsf -Heco.sym -O -v -dEXTRA eco.c  ; to generate ecox
;lc:blink lib:cres.o ecox.o TO ecox LIB lib:lc.lib SD SC ND VERBOSE
 lc:lc -cuwsf -Heco.sym -O -v eco.c          ; to generate eco
 lc:blink lib:cres.o eco.o TO eco LIB lib:lc.lib SD SC ND VERBOSE
 quit
*/
/*
 *  Created on 88/10/27             Last mod: 90/04/01
 *
 *  Module:     Eco                 by  Dario de Judicibus -  Rome (Italy)
 *
 *  Purpose:    echoes a string to the console - allowed control sequences
 *              Created from eco. It includes \[...] escape sequences.
 *              ECO is the Italian word for Echo.
 *
 *  Syntax:     eco "string"
 *
 *  ParmList:   string    a sequence of character strings and control
 *                        sequences (CS). Escape character is backslash (\)
 *                        See eco.hlp for CS list. Execute eco.me as demo.
 *                        Double quotes are mandatory if string contains:
 *                          semicolons   (;)
 *                          minor chars  (<)
 *                          major chars  (>)
 *                        Double quotes can be included by escape sequence \q
 *
 *   Author's addresses:  MAIL     Dario de Judicibus
 *                                 via Canton 101
 *                                 I 00144 Roma ITALIA
 *
 *                        E-Mail   SAM Link (+39.6.423233)  Userid 244
 *                                 MC Link (+39.6.4180440)         MC2120
 *
 *  =========================================================================
 *  @Mod |   on   | by  |  reason
 *  -----+--------+-----+----------------------------------------------------
 *  @001 | 871027 | DdJ | Vers 1 Rel 0 Mod 0
 *  @002 | 881002 | DdJ | Vers 2 Rel 0 Mod 0 - Lattice 4.0
 *  @003 | 881017 | DdJ | Vers 2 Rel 1 Mod 0 - \< & \>
 *  @004 | 881027 | DdJ | Vers 3 Rel 0 Mod 0 - \[...]
 *  @005 | 890724 | DdJ | Vers 3 Rel 1 Mod 0 - Lattice 5.02
 *  @006 | 890726 | DdJ | Vers 3 Rel 2 Mod 0 - \W & \D
 *  @007 | 890916 | DdJ | Vers 3 Rel 2 Mod 1 - \{..} & fixed bug in \[..]
 *  @008 | 891125 | DdJ | Vers 3 Rel 2 Mod 2 - Added getenv() (Lattice bug)
 *  @009 | 900111 | DdJ | Vers 3 Rel 3 Mod 0 - pre-comp. include & resident
 *  @010 | 900401 | DdJ | Vers 3 Rel 4 Mod 0 - New command feature
 */

/*
**  #include lines collected in ecohdr.c and pre-compiled
*/

 #ifdef EXTRA
/*
**  I had to add an internal getenv() routine because the Lattice C 5.04
**  one has a bug: the variable content is returned overlayed to the
**  variable file name ENV:vatiable_name.
**  #define getenv GetEnv is used to avoid a Lattice error message
*/
 #define ENVSIZE 256
 #define getenv GetEnv
 typedef struct FileHandle FH;
 char *getenv(char *);
 #endif
 void help(void);

 #define tohex(c)        (isdigit(c)?((c)&0x0F):(((c)-('7'))&0x0F))
 #ifdef EXTRA
 #define todigit(c)      (isdigit(c)?(((c)-('0'))&0x0F):0)
 #endif

/* Help                                                            */

 #define HLP_0 "\n[32;4mECO Vers. 3.40[33m by Dario de Judicibus - Italy[0m\n\nEscape sequences \\[33m<char>[0m:\n\n"
 #define HLP_1 "Special Chars: [33mq [0mQuotes, [33mx[0m__ Hex value (two digits), [33m?[0m ? character\nColours:       [33m0..3 [0mForeground [33m4..7 [0mBackground\n"
 #define HLP_2 "Screen:        [33mC [0mHome and clear, [33mc [0mClear to end of display\nStyles:        [33mp [0mPlain, [33mb [0mBold, [33mi [0mItalic, [33mu [0mUnderscore, [33mr [0mReverse, [33md [0mDefault\n"
 #define HLP_3 "Cursor:        [33m< [0mInvisible, [33m> [0mVisible, [33mH [0mHome, [33m([0mx[33m,[0my[33m)[0m Cursor positioning\nLine control:  [33mn [0mNewline, [33mf [0mCarriage Return, [33ms [0mno Newline at the end\n"
 #define HLP_4 "Tabs:          [33mv [0mVertical, [33mt [0mHorizontal\n"
 #ifdef EXTRA
 #define HLP_A "Variables:     [33m[[0mvar..var[33m][0m where var = {d1..d7,t1..t7,w1..w2,p}\nDelays:        [33mW [0mWait for ENTER, [33mDx[0m Wait for x seconds\n"
 #define HLP_B "Environment:   [33m{[0mvariable[33m}[0m where variable is an ENV: variable\n               [33m$[0mcommand[33m$[0m will execute the command from current CLI\n"
 #endif
 #define HLP_X "\n[33mECO ?[0m or [33mECO \\h[0m for help, [33mECO \042?\042[0m or [33mECO \\?[0m to output ? as first char in list.\n\n"

/* Control Sequences Definitions                                   */

 #define ESCAPE '\\'            /* Escape character                */
 #define ESC    "\\"            /* Escape character                */
 #define DEF    "\2330;31;40m"  /* Normal                          */
 #define F_0    "\23330m"       /* Foreground 0                    */
 #define F_1    "\23331m"       /* Foreground 1                    */
 #define F_2    "\23332m"       /* Foreground 2                    */
 #define F_3    "\23333m"       /* Foreground 3                    */
 #define B_0    "\23340m"       /* Background 0                    */
 #define B_1    "\23341m"       /* Background 1                    */
 #define B_2    "\23342m"       /* Background 2                    */
 #define B_3    "\23343m"       /* Background 3                    */
 #define S_P    "\2330m"        /* Plain                           */
 #define S_B    "\2331m"        /* Bold                            */
 #define S_I    "\2333m"        /* Italic                          */
 #define S_U    "\2334m"        /* Underscore                      */
 #define S_R    "\2337m"        /* Reverse                         */
 #define VT     "\v"            /* Vertical tab                    */
 #define HT     "\t"            /* Horizontal tab                  */
 #define CRLF   "\n"            /* newline                         */
 #define CR     "\x0D"          /* carriage return -- first column */
 #define HOME   "\233H"         /* home                            */
 #define CLSALL "\233H\233J"    /* home and clear screen           */
 #define CLS    "\233J"         /* clear screen                    */
 #define CUROFF "\2330 p"       /* make cursor invisible           */
 #define CURON  "\233 p"        /* make cursor visible             */
 #define QUOTE  "\x22"
 #define QMARK  "\x3F"
 #define NL     '\0'
 #define NOVALUE (-1)

 #define LAST(c)   ((c == '\0')||(c == '\n'))
 #define SKIP(c)   ((c == ' ')||(c == '\t')||(c == '\n'))
 #define INDEX(c) ((((c)<('0'))||((c)>('~')))?NOVALUE:((c)-('0')))

 BOOL nostop = FALSE;

/*------------------------------------------------------------------------*\
 *     # -> used in table    ! -> used in switch    (blank) -> not used   *
 *                                                                        *
 *     #0  #1  #2  #3  #4  #5  #6  #7   8   9   :   ;  #<   =  #>  #?     *
 *      @   A   B  #C  !D   E   F   G  #H   I   J   K   L   M   N   O     *
 *      P   Q   R   S   T   U   V  !W   X   Y   Z  ![  #\   ]   ^   _     *
 *      `   a  #b  #c  #d   e  #f   g  !h  #i   j   k   l   m  #n   o     *
 *     #p  #q  #r  !s  #t  #u  #v   w  !x   y   z  !{   |   }   ~         *
 *                                                                        *
 *     !(  !$                                                             *
\*------------------------------------------------------------------------*/

 char *_table[] =
 {
     F_0,    F_1,    F_2,    F_3,    B_0,    B_1,    B_2,    B_3,
      NL,     NL,     NL,     NL, CUROFF,     NL,  CURON,  QMARK,
      NL,     NL,     NL, CLSALL,     NL,     NL,     NL,     NL,
    HOME,     NL,     NL,     NL,     NL,     NL,     NL,     NL,
      NL,     NL,     NL,     NL,     NL,     NL,     NL,     NL,
      NL,     NL,     NL,     NL,    ESC,     NL,     NL,     NL,
      NL,     NL,    S_B,    CLS,    DEF,     NL,     CR,     NL,
      NL,    S_I,     NL,     NL,     NL,     NL,   CRLF,     NL,
     S_P,  QUOTE,    S_R,     NL,     HT,    S_U,     VT,     NL,
      NL,     NL,     NL,     NL,     NL,     NL,     NL   /* end */
 };

 #ifdef EXTRA
 char *weekdays[] =
 {
  "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday",
  "SUN"   ,"MON"   ,"TUE"    ,"WED"      ,"THU"     ,"FRI"   ,"SAT"
 };
 #endif

/*
 *  Format text
 */

 void _main(line)
 register unsigned char *line;
 {
   BOOL inQuote;
 #ifdef EXTRA
   char *old, *temp, *env, clk[8], h[64];
 #endif
 #ifndef EXTRA
   char *old, *temp, h[10];
 #endif
   UWORD j, index;

   echo = Output();

   if (!(line)) Exit(RETURN_WARN);/* Must be called from CLI   */
   while (*line != ' ') line++;   /* Skip over command name    */
   temp = line;
   while (SKIP(*temp)) temp++;    /* Skip over leading blanks  */
   if (*temp == '\0')             /* End of line: do just a \n */
   {
     Write(echo,CRLF,1);
     Exit(RETURN_OK);
   }

   if (*temp == '?')              /* Help, only if first char  */
   {
     help();
     Exit(RETURN_OK);
   }
   inQuote = (*temp == '"');      /* if the line starts with " */
   line = inQuote?++temp:++line;  /* take just what in quote,  */
                                  /* otherwise get all         */
   old = line;
   temp = (char *)NL;
   while(!LAST(*line))
   {
     if (*line == ESCAPE)
     {
       *line = '\0';
       Write(echo,old,strlen(old));
       switch(*(++line))
       {
         case 'h': help();
                   line++;
                   break;
         case 's': temp = line;
                   nostop = TRUE;
                   line++;
                   break;
         case '(':
                   h[0] = '\233';
                   line++;
                   for (j=1; *line != ')' && !LAST(*line); j++, line++)
                     h[j] = (*line == ',') ? ';' : *line;
                   h[j] = ')'; /* to be sure */
                   h[j++] = 'H'; h[j] = '\0';
                   Write(echo,h,strlen(h));
                   line++;
                   break;
         case 'x': if (isxdigit(line[1]) && isxdigit(line[2]))
                   {
                     h[0] = (tohex(line[1])<<4)|tohex(line[2]);
                     Write(echo,h,1);
                   }
                   line = &line[3];
                   break;
 #ifdef EXTRA
         case '[': for (line++;(*line != ']') && !LAST(*line);line++)
                   {
                     h[0] = '\0';
                     if (*line == 'p')
                     {
                       (VOID)getcd(0,&h[0]);
                     }
                     else if ((*(line+1) != ']') && !LAST(*(line+1)))
                     {
                       getclk(clk);
                       index = 0x07&todigit(*(line+1));
                       if (index) switch(*line)
                       {
                         case 'd': (void)stpdate(h,(int)index,&clk[1]);
                                   break;
                         case 't': (void)stptime(h,(int)index,&clk[4]);
                                   break;
                         case 'w': index = (UWORD)clk[0] + (0x01&index)*7;
                                   (void)strcpy(h,weekdays[index]);
                                   break;
                         default:  break;
                       }
                     }
                     Write(echo,h,strlen(h));
                   }
                   line++;
                   break;
         case '{': for (j=0,line++;(*line != '}') && !LAST(*line);j++,line++)
                   {
                     h[j] = *line;
                   }
                   h[j] = '\0';
                   env = getenv(&h[0]);
                   if (env)
                   {
                     Write(echo,env,strlen(env));
                     (void)free(env);
                   }
                   line++;
                   break;
         case '$': for (j=0,line++;(*line != '$') && !LAST(*line);j++,line++)
                   {
                     h[j] = *line;
                   }
                   h[j] = '\0';
                   Execute(&h[0],NULL,echo);
                   line++;
                   break;
         case 'D': if (isdigit(line[1]))
                   {
                     j = TICKS_PER_SECOND * todigit(line[1]);
                     Delay(j);
                   }
                   line = &line[2];
                   break;
         case 'W': Read(echo,h,63);
                   line++;
                   break;
 #endif
         default:  index = (UWORD)INDEX(*line);
                   if ((index != NOVALUE)&&(_table[index] != NL))
                     Write(echo,_table[index],strlen(_table[index]));
                   line++;
       }
       old = line;
     }
     else
     {
       if (inQuote && (*line == '"'))
         break;
       line++;
     }
   }
   *line = '\0';
   Write(echo,old,strlen(old));
   if (!(nostop && (line == ++temp))) Write(echo,CRLF,1);
   Exit(RETURN_OK);
 }

 #ifdef EXTRA
/*
**  This procedure will be deleted as soon as the Lattice one will
**  be fixed.
*/
 char *getenv(varname)
   char *varname;
 {
   char *p, *buf = NULL;
   FH *envfh;

   if ((buf = calloc(1,ENVSIZE)) != NULL)
   {
     p = strcpy(buf,"ENV:");
     p = strcat(buf,varname);
     if ((envfh = (FH *)Open(buf,MODE_OLDFILE)) != NULL)
     {
       p = memset(buf,'\0',ENVSIZE);
       (void)Read((BPTR)envfh,buf,ENVSIZE);
       Close((BPTR)envfh);
     }
     else
       buf[0] = '\0';
   }

   return (buf);
 }
 #endif

/*
**  Help
*/
 void help()
 {
   Write(echo,HLP_0,strlen(HLP_0));
   Write(echo,HLP_1,strlen(HLP_1));
   Write(echo,HLP_2,strlen(HLP_2));
   Write(echo,HLP_3,strlen(HLP_3));
   Write(echo,HLP_4,strlen(HLP_4));
 #ifdef EXTRA
   Write(echo,HLP_A,strlen(HLP_A));
   Write(echo,HLP_B,strlen(HLP_B));
 #endif
   Write(echo,HLP_X,strlen(HLP_X));
 }
