The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,641 other followers

.NET/C# – TEE filter that also runs on Windows (XP) Embedded – update

Posted by jpluimers on 2010/04/15

Last week, I posted a C# implementation of the tee filter from Sterling W. “Chip” Camden.

Since then I have modified it slightly.
Not because the implementation is bad, but because some pieces of software play dirty when saving their redirected output.

One of those applications is SubInAcl, otherwise a great tool for showing and modifying ACL and Ownership information of Windows NT objects (files, registry entries, etc).

However, when redirecing output or piping it, it writes a zero byte after each byte of text.
I’m not sure why: it might try to face some Unicode output, or just be buggy.

The new sourcecode is below. You can also download the project and binary as tee.C#.7z (you need the freeware 7zip compression tool to decompress this).

For instance, see the output (hexadecimal and ASCII) of this command

subinacl > output.txt

000000: 53 00 75 00 62 00 49 00  6E 00 41 00 63 00 6C 00 S.u.b.I.n.A.c.l.
000010: 20 00 76 00 65 00 72 00  73 00 69 00 6F 00 6E 00  .v.e.r.s.i.o.n.
000020: 20 00 35 00 2E 00 32 00  2E 00 33 00 37 00 39 00  .5...2...3.7.9.
000030: 30 00 2E 00 31 00 31 00  38 00 30 00 0D 00 0A 00 0...1.1.8.0.....
000040: 0D 00 0A 00 49 00 6E 00  76 00 61 00 6C 00 69 00 ....I.n.v.a.l.i.
000050: 64 00 20 00 41 00 72 00  67 00 75 00 6D 00 65 00 d. .A.r.g.u.m.e.
000060: 6E 00 74 00 20 00 21 00  0D 00 0A 00 55 00 73 00 n.t. .!.....U.s.
000070: 65 00 20 00 3A 00 0D 00  0A 00 53 00 75 00 62 00 e. .:.....S.u.b.
000080: 49 00 6E 00 61 00 63 00  6C 00 20 00 2F 00 68 00 I.n.a.c.l. ./.h.
000090: 65 00 6C 00 70 00 20 00  74 00 6F 00 20 00 67 00 e.l.p. .t.o. .g.
0000A0: 65 00 74 00 20 00 74 00  68 00 65 00 20 00 75 00 e.t. .t.h.e. .u.
0000B0: 73 00 61 00 67 00 65 00  20 00 69 00 6E 00 66 00 s.a.g.e. .i.n.f.
0000C0: 6F 00 72 00 6D 00 61 00  74 00 69 00 6F 00 6E 00 o.r.m.a.t.i.o.n.
0000D0: 0D 00 0A 00 6F 00 72 00  0D 00 0A 00 53 00 75 00 ....o.r.....S.u.
0000E0: 62 00 49 00 6E 00 41 00  63 00 6C 00 20 00 2F 00 b.I.n.A.c.l. ./.
0000F0: 68 00 65 00 6C 00 70 00  20 00 73 00 79 00 6E 00 h.e.l.p. .s.y.n.
000100: 74 00 61 00 78 00 20 00  74 00 6F 00 20 00 75 00 t.a.x. .t.o. .u.
000110: 6E 00 64 00 65 00 72 00  73 00 74 00 61 00 6E 00 n.d.e.r.s.t.a.n.
000120: 64 00 20 00 53 00 75 00  62 00 49 00 6E 00 41 00 d. .S.u.b.I.n.A.
000130: 63 00 6C 00 20 00 73 00  79 00 6E 00 74 00 61 00 c.l. .s.y.n.t.a.
000140: 78 00 2E 00 0D 00 0A 00                          x.......

I modified the C# version of tee so when piped through tee, you get the output like this: all the zero bytes have been removed.

000000: 53 75 62 49 6E 41 63 6C  20 76 65 72 73 69 6F 6E SubInAcl version
000010: 20 35 2E 32 2E 33 37 39  30 2E 31 31 38 30 0D 0A  5.2.3790.1180..
000020: 0D 0A 49 6E 76 61 6C 69  64 20 41 72 67 75 6D 65 ..Invalid Argume
000030: 6E 74 20 21 0D 0A 55 73  65 20 3A 0D 0A 53 75 62 nt !..Use :..Sub
000040: 49 6E 61 63 6C 20 2F 68  65 6C 70 20 74 6F 20 67 Inacl /help to g
000050: 65 74 20 74 68 65 20 75  73 61 67 65 20 69 6E 66 et the usage inf
000060: 6F 72 6D 61 74 69 6F 6E  0D 0A 6F 72 0D 0A 53 75 ormation..or..Su
000070: 62 49 6E 41 63 6C 20 2F  68 65 6C 70 20 73 79 6E bInAcl /help syn
000080: 74 61 78 20 74 6F 20 75  6E 64 65 72 73 74 61 6E tax to understan
000090: 64 20 53 75 62 49 6E 41  63 6C 20 73 79 6E 74 61 d SubInAcl synta
0000A0: 78 2E 0D 0A                                      x...

This is the change in the sourcecode:

                                if (0 == b) //##jpl: 20100407 - bugfix for SubInACL (it fakes Unicode output by putting extra null bytes in the output)
                                    continue;

This is the complete sourcecode:

using System;
using System.IO;
using System.Collections.Generic;
// Sends standard input to standard output and to all files in command line.
// C# implementation april 4th, 2010 by Jeroen Wiert Pluimers
// https://wiert.wordpress.com
// based on tee by Chip Camden, Camden Software Consulting, November 2005
// 	... and Anonymous Cowards everywhere!
// http://www.camdensoftware.com
// http://chipstips.com/?tag=cpptee
//
// TEE [-a | --append] [-i | --ignore] [--help | /?] [-f] [file1] [...]
//    Example:
// 	tee --append file0.txt -f --help file2.txt
//    will append to file0.txt, --help, and file2.txt
//
// -a | --append	Appends files instead of overwriting
// 			  (setting is per tee instance)
// -i | --ignore	Ignore cancel Ctrl+C keypress: see UnixUtils tee
// /? | --help		Displays this message and immediately quits
// -f			Stop recognizing flags, force all following filenames literally
//
// Duplicate filenames are quietly ignored.
// Press Ctrl+Z (End of File character) then Enter to abort.
namespace tee
{
    class Program
    {
        static void help()
        {
            Console.Error.WriteLine("Sends standard input to standard output and to all files in command line.");
            Console.Error.WriteLine("C# implementation april 4th, 2010 by Jeroen Wiert Pluimers (https://wiert.wordpress.com");
            Console.Error.WriteLine("https://wiert.wordpress.com");
            Console.Error.WriteLine("based on tee by Chip Camden, Camden Software Consulting, November 2005");
            Console.Error.WriteLine("	... and Anonymous Cowards everywhere!");
            Console.Error.WriteLine("http://www.camdensoftware.com");
            Console.Error.WriteLine("http://chipstips.com/?tag=cpptee");
            Console.Error.WriteLine("");
            Console.Error.WriteLine("tee [-a | --append] [-i | --ignore] [--help | /?] [-f] [file1] [...]");
            Console.Error.WriteLine("   Example:");
            Console.Error.WriteLine(" tee --append file0.txt -f --help file2.txt");
            Console.Error.WriteLine("   will append to file0.txt, --help, and file2.txt");
            Console.Error.WriteLine("");
            Console.Error.WriteLine("-a | --append    Appends files instead of overwriting");
            Console.Error.WriteLine("                 (setting is per tee instance)");
            Console.Error.WriteLine("-i | --ignore    Ignore cancel Ctrl+C keypress: see UnixUtils tee");
            Console.Error.WriteLine("/? | --help      Displays this message and immediately quits");
            Console.Error.WriteLine("-f               Stop recognizing flags, force all following filenames literally");
            Console.Error.WriteLine("");
            Console.Error.WriteLine("Duplicate filenames are quietly ignored.");
            Console.Error.WriteLine("Press Ctrl+Z (End of File character) then Enter to abort.");
        }

        static void OnCancelKeyPressed(Object sender, ConsoleCancelEventArgs args)
        {
            // Set the Cancel property to true to prevent the process from
            // terminating.
            args.Cancel = true;
        }

        static List<String> filenames = new List<String>();

        static void addFilename(string value)
        {
            if (-1 == filenames.IndexOf(value))
                filenames.Add(value);
        }

        static int Main(string[] args)
        {
            try
            {
                bool appendToFiles = false;
                bool stopInterpretingFlags = false;
                bool ignoreCtrlC = false;

                foreach (string arg in args)
                {
                    //Since we're already parsing.... might as well check for flags:
                    if (stopInterpretingFlags)  //Stop interpreting flags, assume is filename
                    {
                        addFilename(arg);
                    }
                    else if (arg.Equals("/?") || arg.Equals("-h") || arg.Equals("--help"))
                    {
                        help();
                        return 1; //Quit immediately
                    }
                    else if (arg.Equals("-a") || arg.Equals("--append"))
                    {
                        appendToFiles = true;
                    }
                    else if (arg.Equals("-i") || arg.Equals("--ignore"))
                    {
                        ignoreCtrlC = true;
                    }
                    else if (arg.Equals("-f"))
                    {
                        stopInterpretingFlags = true;
                    }
                    else
                    {	//If it isn't any of the above, it's a filename
                        addFilename(arg);
                    }
                    //Add more flags as necessary, just remember to SKIP adding them to the file processing stream!
                }

                if (ignoreCtrlC) //Implement the Ctrl+C fix selectively (mirror UnixUtils tee behavior)
                    Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKeyPressed);

                List<BinaryWriter> binaryWriters = new List<BinaryWriter>(filenames.Count); //Add only as many streams as there are distinct files
                try
                {
                    foreach (String filename in filenames)
                    {
                        binaryWriters.Add(new BinaryWriter(appendToFiles ?
                            File.AppendText(filename).BaseStream :
                            File.Create(filename)));  // Open the files specified as arguments
                    }
                    using (BinaryReader stdin = new BinaryReader(Console.OpenStandardInput()))
                    {
                        using (BinaryWriter stdout = new BinaryWriter(Console.OpenStandardOutput()))
                        {
                            Byte b;
                            while (true)
                            {
                                try
                                {
                                    b = stdin.ReadByte();  // Read standard in
                                }
                                catch (EndOfStreamException)
                                {
                                    break;
                                }
                                if (0 == b) //##jpl: 20100407 - bugfix for SubInACL (it fakes Unicode output by putting extra null bytes in the output)
                                    continue;
                                // The actual tee:
                                stdout.Write(b); // Write standard out
                                foreach (BinaryWriter binaryWriter in binaryWriters)
                                {
                                    binaryWriter.Write(b); // Write to each file
                                }
                            }
                        }
                    }
                }
                finally
                {
                    foreach (BinaryWriter binaryWriter in binaryWriters)
                    {
                        binaryWriter.Flush();  // Flush and close each file
                        binaryWriter.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(String.Concat("tee: ", ex.Message));  // Send error messages to stderr
            }

            return 0;
        }
    }
}

–jeroen

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: