Jim Lawless' Blog


Yet Another Enhanced Echo Command

Originally published on: Sun, 06 Sep 2009 16:12:42 +0000

Please note! If you're having difficulties compiling the C source code presented below, please see my post: Compiling C from the Command Line with Pelles C

Although a number of more robust scripting options are available, I still write batch files in the Windows environment. I enjoy the simplicity but sometimes find myself frustrated with something that I perceive to be a limitation.

Usually, if I hit a wall trying to perform something that can be a cumbersome in a batch file, I will often just write a separate program or script to perform the task.

In some cases, when I need to construct an output text file, I find myself opting for Perl or AWK or something similar if I need to embed special-characters in the output. The greater-than '>' and less-than '<' symbols can be a bit troublesome in a batch file because they handle redirection. This makes building an XML file a little tricky. The & symbol can sometimes cause problems too as it is supposed to be used to chain commands together.

As a solution to my own problem, I wrote this simple version of the batch echo command called echoj. While the EXE provided in a link later in this text is intended for use in a Windows environment, the C code should be portable to other environments.

Here's the C source:

echoj.c


// echoj
// An enhanced batch "echo" command that allows
// data to be written to or appended to a file. Binary characters
// can be specified in hex notation with a leading '$' symbol.
//
// License: MIT / X11
// Copyright (c) 2009 by James K. Lawless
// jimbo@radiks.net http://www.radiks.net/~jimbo
// http://www.mailsend-online.com
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.


#include <stdio.h>
#include <string.h>
#include <ctype.h>

void syntax(void);

int main(int argc,char **argv) {
   FILE *out;
   char *fileName;
   char *fileMode;
   int firstString;
   int i;
   char hexWrk[3];
   int c;
   
   
   if(argc<2) {
      syntax();
      return 1;
   }
   out=stdout;
   fileName=NULL;
   fileMode="w";
   
   for(i=1;i<argc;i++) {
      if(*argv[i]!='-') {
         firstString=i;
         break;
      }
      switch( *(argv[i]+1)) {
         case 'v':
         case 'V':
            if(i==(argc-1)) {
               fprintf(stderr,"Missing argument for -v parameter");
               return 1;
            }
return atoi(argv[i+1]);
            break;
         case 'f':
         case 'F':
            if(i==(argc-1)) {
               fprintf(stderr,"Missing argument for -f parameter");
               return 1;
            }
            fileName=argv[i+1];
            i++;
            break;
         case 'm':
         case 'M':
            if(i==(argc-1)) {
               fprintf(stderr,"Missing argument for -m parameter");
               return 1;
            }
            fileMode=argv[i+1];
            i++;
            break;
      }
   }
   
   if(fileName!=NULL) {
      out=fopen(fileName,fileMode);
      if(out==NULL) {
         fprintf(stderr,"Unable to open file %s in mode %s\n",fileName,fileMode);
         return 1;
      }
   }
      // now, process the rest of the strings as output.
   for(i=firstString;i<argc;i++) {
      if(* (argv[i])=='$') {
         if(strlen(argv[i])==3) {
            hexWrk[0]=*(argv[i]+1);
            hexWrk[1]=*(argv[i]+2);
            hexWrk[2]=0;
            sscanf(hexWrk,"%02x",&c);
            fputc(c,out);
            continue;
         }
      }
      fwrite(argv[i],1,strlen(argv[i]),out);
   }
   if(fileName!=NULL)
      fclose(out);
   return 0;
}

void syntax(void) {
   printf("echoj by Jim Lawless jimbo@radiks.net\n");
   printf("Syntax:\techoj [ -f filename -m file-open-mode -v verification-int ] strings\n");
   printf("\n");
   printf("Valid file open modes:\n");
   printf(" \"a\" Append to specified file in text mode.\n");
   printf(" \"ab\" Append to specified file in binary mode.\n");
   printf(" \"w\" (default) Write (overwrite) specified file in text mode.\n");
   printf(" \"wb\" Write (overwrite) specified file in binary mode.\n\n");
   printf("If -f is not specified, output is written to standard output.\n");
   printf("Strings should be in double-quotes if they contain spaces.\n\n");
   printf("Binary characters can be specified in two-digit hexadecimal notation\n");
   printf("using a '$' at the beginning.\n\n");
   printf("Example:\n");
   printf("echoj \"Here are greater-than and less-than signs: \" $3c \" \" $3e $0a\n\n");
   printf("For more information, including documentation for the -v option,\n");
   printf("please read the related blog post at this shortened URL:\nhttp://bit.ly/2eqbvR\n");
}

If you run echoj from a command-prompt with no arguments, you should see the following help screen:


echoj by Jim Lawless jimbo@radiks.net
Syntax: echoj [ -f filename -m file-open-mode -v verification-int ] strings

Valid file open modes:
   "a" Append to specified file in text mode.
   "ab" Append to specified file in binary mode.
   "w" (default) Write (overwrite) specified file in text mode.
   "wb" Write (overwrite) specified file in binary mode.

If -f is not specified, output is written to standard output.
Strings should be in double-quotes if they contain spaces.

Binary characters can be specified in two-digit hexadecimal notation
using a '$' at the beginning.

Example:
echoj "Here are greater-than and less-than signs: " $3c " " $3e $0a

For more information, including documentation for the -v option,
please read the related blog post at this shortened URL:
http://bit.ly/2eqbvR

Let's examine the different options and different ways to use echoj.

The -v option is intended to provide a simple way to verify that echoj.exe is in your command processor's execution path. It accepts a number as an argument and terminates the echoj process immediately, returning that number.

In a batch file, you might leverage feature in this manner.


echoj.exe -v 42
if "%ERRORLEVEL%"=="42" goto keepgoing
rem error logic here
goto getout
...
:keepgoing
...
:getout

Without any parameters, echoj can be useful when needing to write special characters to the console. Consider the following example.


echoj "Tab1" $09 "Tab2" $0a

The $09 code is the ASCII code for the TAB character. echoj will translate any string on the command line ( after the optional parameters ) from its hexadecimal representation into binary if:

    The string is exactly three characters in length. The string begins with a '$' symbol.

Note that echoj doesn't work like the normal echo. If you enter:


echoj Hey there, everybody!

You'll likely see:


heythere,everybody!

echoj looks for parameters separated by spaces. If you need to include spaces in a parameter, enclose that parameter in double-quotes.

So, the TAB example would also work the same way if we wrote:


echoj Tab1 $09 Tab2 $0a

...and would also work if we quote the hexadecimal paramters.


echoj Tab1 "$09" Tab2 "$0a"

You can display the greater-than, less-than, ampersand, and pipe characters by using $3c, $3e, $26, and $7c respectively.


echoj $3c " " $3e " " $26 " " $7c

Also note that echoj does not provide it's own newline sequence as output. We do this ourselves by placing the code for an ASCII linefeed ($0a) as the last string paramter. Try this example.


echoj Line1 $0a $0a Line3 $0a $0a $0a Line6

You should see:


Line1

Line3


Line6

You may wonder why we don't also specify a carriage-return ($0d) character with each linefeed. For Windows, most of the C libraries seem to translate the $0a character into a combination of $0d and $0a when going to the console or when a file is opened in text mode. The only situations where we really need to concern ourselves with the carriage-return character will be when we are using echoj to write to binary files and we need to issue a newline sequence.

The -f and -m options can be used to write to files as an option to using the standard command-processor's redirection symbols.

Consider the following example:


echoj -f tmp.txt Line1 $0a $0a Line3 $0a $0a $0a Line6
type tmp.txt

The above code writes the same sequence of lines from a prior example to the text file tmp.txt. The default file open mode that echoj uses is "w" for write new text file. This mode opens a file as output and deletes any existing contents if the file had already existed.

Let's append the same line to the file by specifying mode "a" ( append to text file ):


echoj -f tmp.txt -m "a" Line1 $0a $0a Line3 $0a $0a $0a Line6
type tmp.txt

In the output file, you should now see:


Line1

Line3


Line6Line1

Line3


Line6

Let's use the binary options to build a small .com file called alphabet.com. ( You can see the raw assembly source to this file in this blog's topmost masthead image. )


echoj -f alphabet.com -m "wb" $b2 $41 $b9 $1a $00 $b4 $02 $cd
echoj -f alphabet.com -m "ab" $21 $49 $74 $06 $88 $c2 $fe $c2
echoj -f alphabet.com -m "ab" $eb $f3 $b8 $00 $4c $cd $21

If you now execute alphabet.com you should see the alphabet on the console in upper-case.

These file open mode strings are passed in verbatim to the C fopen() function, so echoj will respond to modes such as "a+b" ( append binary ), but I've yet to really experiment with any options beyond the four listed in the syntax-help screen.

The source and executable file for echoj above can be downloaded in a single archive at: http://www.mailsend-online.com/wp/echoj.zip

Unless otherwise noted, all code and text entries are Copyright ©2009 by James K. Lawless



Views expressed in this blog are those of the author and do not necessary reflect those of the author's employer. Views expressed in the comments are those of the responding individual.

stumbleupon Save to StumbleUpon
digg Digg it
reddit Save to Reddit
facebook Share on Facebook
twitter Share on Twitter
aolfav More bookmarks


Previous post: Internet Protocols and Rhino JavaScript
Next post:Command-Line Image Format Conversion


About Jim ...












Follow me on Twitter

http://twitter.com/lawlessGuy


My GitHub Repository

https://github.com/jimlawless



Recent Posts

Compiling C from the Command Line with Pelles C

A Forthcoming Marvel Movie Villain

Uninstalling Problematic Windows Software

Don't be Hatin'

A JavaScript REPL for Android Devices

MailSend is Free

My Blog Engine

The October 10th Bug


Random Posts

RSS feed processing with AWK

Compiling Rhino JavaScript to Java

Command-Line Image Format Conversion

Send GMail From the Windows Command-Line with MailWrench

Preventing Windows Screen-Saver Activation

A Data Manipulation Library for TAP

BPL: Batch Programming Language Interpreter

An Interview with Tom Zimmer: Forth System Developer

A Quine in Forth

My Journey Began with BASIC


Full List of Posts

http://www.mailsend-online.com/bloglist.htm


Recent Posts from my Other Blog

Jobs : The Film

Yet Another Config File Reader for Go (Golang)

Filling a Slice Using Command-line Flags in Go (Golang)

An RPN Interpreter in Go (Golang)

Simulating Try-Catch in Go (Golang)

Sending GMail with Go (Golang)

Variant Types in Golang

My First C64 Interrupt Program

The Triangles Puzzle

Happy 25th, Perl !

My Favorite BASIC One-Liner

Playing with OS/X Text to Speech

The Villain at the end of Marvel's Avengers Move is...

Chicken! Fight like a Robot!

Processing GMail items with Ruby

The Squares Puzzle

Happy 30th Birthday, Commodore 64

Scripting Safari

MailWrench CSharp Command Line Mailer for Windows is now Free Software

Welcome Back, M.U.L.E. !

Rainy Day Fun with the HTML DOM

Building a World War II Foxhole Radio

Prototerp – Unleashing a JavaScript REPL via the Mobile Browser

Steal This Bookmarklet

Happy Birthday, Miles