Jim Lawless' Blog


Yet Another Enhanced Echo Command

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

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

del_icio_us Save to del.icio.us
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


Search this Blog (and site)

Search this Site with PicoSearch


Subscribe to this Blog

 Subscribe!


Contact Me

Email: jimbo@radiks.net


Follow me on Twitter

http://twitter.com/lawlessGuy


Recent Posts

Mad Schemes : Learning Lisp via SICP

Auto Save Clipboard Images Redux

Extending SpiderMonkey JavaScript on Windows

Rhino JavaScript to EXE with launch4j

Compiling Rhino JavaScript to Java

Directory Traversal in Rhino JavaScript

Taking Shape

We've Moved!


Popular Posts

A Command-Line MP3 Player for Windows

Auto Save Images from the Clipboard

Java in a Windows EXE with launch4j

An Interview with Tom Zimmer: Forth System Developer

Setting Windows Console Text Colors in C


Random Posts

Charging by the Byte

Understanding TRS-80 CMD Files

TAP : A Command Processor Library

Speeding up JRuby with NailGun

Flirting with Forth

Removing IE Popups in C

Twimmando No More

An Embedded Mini-Interpreter

Tracing XSLT with a Tiny Java Web Server

An Interview with the Author of the French Silk Assembler


Full List of Posts

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


Blogroll

MicroISV on a Shoestring
DadHacker
The Bottom Feeder
Writin' That Code!
The Recursive ISV
The Thomsen Blog
Prototypically Speaking
The Reinvigorated Programmer