Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxService.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxService.cpp	(revision 34272)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxService.cpp	(revision 34273)
@@ -519,7 +519,7 @@
          * mutex because VBoxService might spawn itself to execute some commands.
          */
-        rc = VBoxServiceToolboxMain(argc - 1, &argv[1]);
-        if (rc != VERR_NOT_FOUND) /* Internal tool found? Then bail out. */
-            return rc;
+        int iExitCode;
+        if (VBoxServiceToolboxMain(argc - 1, &argv[1], &iExitCode))
+            return iExitCode;
     }
 #endif
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h	(revision 34272)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h	(revision 34273)
@@ -280,35 +280,35 @@
 
 #ifdef VBOXSERVICE_TOOLBOX
-extern int VBoxServiceToolboxMain(int argc, char **argv);
+extern bool         VBoxServiceToolboxMain(int argc, char **argv, int *piExitCode);
 #endif
 
 #ifdef RT_OS_WINDOWS
 # ifdef VBOX_WITH_GUEST_PROPS
-extern int VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList);
-extern int VBoxServiceWinGetComponentVersions(uint32_t uiClientID);
+extern int          VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList);
+extern int          VBoxServiceWinGetComponentVersions(uint32_t uiClientID);
 # endif /* VBOX_WITH_GUEST_PROPS */
 #endif /* RT_OS_WINDOWS */
 
 #ifdef VBOX_WITH_GUEST_CONTROL
-extern int  VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms);
-extern int  VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms);
-extern int  VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms);
-extern int  VBoxServiceControlExecProcess(uint32_t uContext, const char *pszCmd, uint32_t uFlags,
-                                          const char *pszArgs, uint32_t uNumArgs,
-                                          const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
-                                          const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS);
-extern void VBoxServiceControlExecDestroyThreadData(PVBOXSERVICECTRLTHREADDATAEXEC pThread);
-extern void VBoxServiceControlExecDeletePipeBuffer(PVBOXSERVICECTRLEXECPIPEBUF pBuf);
-extern int  VBoxServiceControlExecReadPipeBufferContent(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
-                                                        uint8_t *pbBuffer, uint32_t cbBuffer, uint32_t *pcbToRead);
-extern int  VBoxServiceControlExecWritePipeBuffer(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
-                                                  uint8_t *pbData, uint32_t cbData, bool fPendingClose, uint32_t *pcbWritten);
+extern int          VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms);
+extern int          VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms);
+extern int          VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms);
+extern int          VBoxServiceControlExecProcess(uint32_t uContext, const char *pszCmd, uint32_t uFlags,
+                                                  const char *pszArgs, uint32_t uNumArgs,
+                                                  const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
+                                                  const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS);
+extern void         VBoxServiceControlExecDestroyThreadData(PVBOXSERVICECTRLTHREADDATAEXEC pThread);
+extern void         VBoxServiceControlExecDeletePipeBuffer(PVBOXSERVICECTRLEXECPIPEBUF pBuf);
+extern int          VBoxServiceControlExecReadPipeBufferContent(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
+                                                                uint8_t *pbBuffer, uint32_t cbBuffer, uint32_t *pcbToRead);
+extern int          VBoxServiceControlExecWritePipeBuffer(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
+                                                          uint8_t *pbData, uint32_t cbData, bool fPendingClose, uint32_t *pcbWritten);
 #endif /* VBOX_WITH_GUEST_CONTROL */
 
 #ifdef VBOXSERVICE_MANAGEMENT
-extern uint32_t VBoxServiceBalloonQueryPages(uint32_t cbPage);
+extern uint32_t     VBoxServiceBalloonQueryPages(uint32_t cbPage);
 #endif
 #if defined(VBOX_WITH_PAGE_SHARING) && defined(RT_OS_WINDOWS)
-extern RTEXITCODE VBoxServicePageSharingInitFork(void);
+extern RTEXITCODE   VBoxServicePageSharingInitFork(void);
 #endif
 
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp	(revision 34272)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp	(revision 34273)
@@ -23,4 +23,5 @@
 
 #include <iprt/assert.h>
+#include <iprt/buildconfig.h>
 #include <iprt/dir.h>
 #include <iprt/file.h>
@@ -28,4 +29,5 @@
 #include <iprt/list.h>
 #include <iprt/mem.h>
+#include <iprt/message.h>
 #include <iprt/path.h>
 #include <iprt/string.h>
@@ -37,12 +39,22 @@
 
 #include <VBox/VBoxGuestLib.h>
+#include <VBox/version.h>
 #include "VBoxServiceInternal.h"
 #include "VBoxServiceUtils.h"
 
-/** @todo r=bird: Don't use VBoxServiceError here, just use RTMsg**.  */
-/** @todo r=bird: 'static' is a wonderful keyword, please use it as much as
- *        possible like I've said in the coding guidelines.  Not only does it
- *        help wrt to linking, but it also helps understanding what's
- *        internal and external interfaces in a source file! */
+
+#define CAT_OPT_NO_CONTENT_INDEXED              1000
+
+/**
+ * An file/directory entry. Used to cache
+ * file names/paths for later processing.
+ */
+typedef struct VBOXSERVICETOOLBOXPATHENTRY
+{
+    /** Our node. */
+    RTLISTNODE  Node;
+    /** Name of the entry. */
+    char       *pszName;
+} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
 
 
@@ -50,8 +62,10 @@
  * Displays a help text to stdout.
  */
-void VBoxServiceToolboxShowUsage(void)
+static void VBoxServiceToolboxShowUsage(void)
 {
     RTPrintf("Toolbox Usage:\n"
              "cat [FILE] - Concatenate FILE(s), or standard input, to standard output\n"
+             "\n"
+             "mkdir - Make directories\n"
              "\n");
 }
@@ -59,11 +73,19 @@
 
 /**
- *
- *
- * @return  int
- *
+ * Displays the program's version number.
+ */
+static void VBoxServiceToolboxShowVersion(void)
+{
+    RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+}
+
+
+/**
+ * Displays an error message because of syntax error.
+ *
+ * @return  VERR_INVALID_PARAMETER
  * @param   pszFormat
  */
-int VBoxServiceToolboxErrorSyntax(const char *pszFormat, ...)
+static int VBoxServiceToolboxErrorSyntax(const char *pszFormat, ...)
 {
     va_list args;
@@ -73,5 +95,4 @@
              "Syntax error: %N\n", pszFormat, &args);
     va_end(args);
-    VBoxServiceToolboxShowUsage();
     return VERR_INVALID_PARAMETER;
 }
@@ -79,12 +100,65 @@
 
 /**
- *
- *
- * @return  int
- *
- * @param   hInput
- * @param   hOutput
- */
-int VBoxServiceToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
+ * Destroys a path buffer list.
+ *
+ * @return  IPRT status code.
+ * @param   pList                   Pointer to list to destroy.
+ */
+static void VBoxServiceToolboxPathBufDestroy(PRTLISTNODE pList)
+{
+    AssertPtr(pList);
+    PVBOXSERVICETOOLBOXPATHENTRY pNode = RTListNodeGetFirst(pList, VBOXSERVICETOOLBOXPATHENTRY, Node);
+    while (pNode)
+    {
+        PVBOXSERVICETOOLBOXPATHENTRY pNext = RTListNodeIsLast(pList, &pNode->Node)
+                                                              ? NULL :
+                                                                RTListNodeGetNext(&pNode->Node,
+                                                                                  VBOXSERVICETOOLBOXPATHENTRY, Node);
+        RTListNodeRemove(&pNode->Node);
+
+        RTStrFree(pNode->pszName);
+
+        RTMemFree(pNode);
+        pNode = pNext;
+    }
+}
+
+
+/**
+ * Adds a path entry (file/directory/whatever) to a given path buffer list.
+ *
+ * @return  IPRT status code.
+ * @param   pList                   Pointer to list to add entry to.
+ * @param   pszName                 Name of entry to add.
+ */
+static int VBoxServiceToolboxPathBufAddPathEntry(PRTLISTNODE pList, const char *pszName)
+{
+    AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
+
+    int rc = VINF_SUCCESS;
+    PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
+    if (pNode)
+    {
+        pNode->pszName = RTStrDup(pszName);
+        AssertPtr(pNode->pszName);
+
+        /*rc =*/ RTListAppend(pList, &pNode->Node);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+    return rc;
+}
+
+
+/**
+ * Performs the actual output operation of "vbox_cat".
+ *
+ * @return  IPRT status code.
+ * @param   hInput                  Handle of input file (if any) to use;
+ *                                  else stdin will be used.
+ * @param   hOutput                 Handle of output file (if any) to use;
+ *                                  else stdout will be used.
+ */
+static int VBoxServiceToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
 {
     int rc = VINF_SUCCESS;
@@ -93,5 +167,5 @@
         rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
         if (RT_FAILURE(rc))
-            VBoxServiceError("cat: Could not translate input file to native handle, rc=%Rrc\n", rc);
+            RTMsgError("cat: Could not translate input file to native handle, rc=%Rrc\n", rc);
     }
 
@@ -100,5 +174,5 @@
         rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
         if (RT_FAILURE(rc))
-            VBoxServiceError("cat: Could not translate output file to native handle, rc=%Rrc\n", rc);
+            RTMsgError("cat: Could not translate output file to native handle, rc=%Rrc\n", rc);
     }
 
@@ -130,15 +204,12 @@
 
 
-/** @todo r=bird: Again, function headers like this are uslesss and better left
- *        out. */
-/**
- *
- *
- * @return  int
- *
- * @param   argc
- * @param   argv
- */
-int VBoxServiceToolboxMkDir(int argc, char **argv)
+/**
+ * Main function for tool "vbox_mkdir".
+ *
+ * @return  RTEXITCODE.
+ * @param   argc                    Number of arguments.
+ * @param   argv                    Pointer to argument array.
+ */
+static int VBoxServiceToolboxMkDir(int argc, char **argv)
 {
      static const RTGETOPTDEF s_aOptions[] =
@@ -152,8 +223,7 @@
      RTGETOPTUNION ValueUnion;
      RTGETOPTSTATE GetState;
-     RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
-     /** @todo r=bird: Pass RTGETOPTINIT_FLAGS_OPTS_FIRST, our mkdir shall be
-      *        like the GNU one wrt to option (dash-something) and argument
-      *        order (dirs). */
+     RTGetOptInit(&GetState, argc, argv,
+                  s_aOptions, RT_ELEMENTS(s_aOptions),
+                  1 /* Index of argv to start with. */, RTGETOPTINIT_FLAGS_OPTS_FIRST);
 
      int rc = VINF_SUCCESS;
@@ -161,11 +231,10 @@
      bool fVerbose = false;
 
-     char szDir[RTPATH_MAX];
      RTFMODE newMode = 0;
-#ifdef RT_OS_WINDOWS
-     RTFMODE fileMode = 0;
-#else
-     RTFMODE fileMode = S_IRWXU | S_IRWXG | S_IRWXO; /** @todo r=bird: We've got RTFS_ defines for these, they are x-platform.  Why 'file' when we're creating directories? */
-#endif
+     RTFMODE dirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
+
+     /* Init directory list. */
+     RTLISTNODE dirList;
+     RTListInit(&dirList);
 
      while (   (ch = RTGetOpt(&GetState, &ValueUnion))
@@ -175,4 +244,8 @@
          switch (ch)
          {
+             case 'h':
+                 VBoxServiceToolboxShowUsage();
+                 return RTEXITCODE_SUCCESS;
+
              case 'p':
                  fMakeParentDirs = true;
@@ -182,5 +255,8 @@
                  rc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 8 /* Base */, &newMode);
                  if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
-                     VBoxServiceVerbose(0, "mkdir: Mode flag strings not implemented yet!\n");
+                 {
+                     RTMsgError("mkdir: Mode flag strings not implemented yet! Use octal numbers instead.\n");
+                     return RTEXITCODE_SYNTAX;
+                 }
                  break;
 
@@ -189,21 +265,19 @@
                  break;
 
+             case 'V':
+                 VBoxServiceToolboxShowVersion();
+                 return RTEXITCODE_SUCCESS;
+
              case VINF_GETOPT_NOT_OPTION:
              {
-                 rc = RTPathAbs(ValueUnion.psz, szDir, sizeof(szDir));
-                 if (RT_FAILURE(rc))
-                     VBoxServiceError("mkdir: Could not build absolute directory!\n");
-                 /** @todo r=bird: you can make multiple directories just by
-                  *  adding them to the mkdir command line:
-                  *     "mkdir foo/ foo/bar foo/bar/wiz"
-                  *  This will now only create foo/bar/wiz, which will fail
-                  *  because the two previous steps weren't executed.  It
-                  *  will also leak memory, but that's not important. (Also,
-                  *  I don't get why we need to call RTPathAbs here as nobody
-                  *  is going to change the current directory.) */
+                 /* Add path(s) to buffer. This enables processing multiple paths
+                  * at once.
+                  *
+                  * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
+                  * processing this loop it's safe to immediately exit on syntax errors
+                  * or showing the help text (see above). */
+                 rc = VBoxServiceToolboxPathBufAddPathEntry(&dirList, ValueUnion.psz);
                  break;
              }
-             /** @todo r=bird: Missing handling of the standard options 'V' and
-              *        'h'. */
 
              default:
@@ -219,50 +293,62 @@
              mode_t umaskMode = umask(0); /* Get current umask. */
              if (newMode)
-                fileMode = newMode;
+                dirMode = newMode;
 #endif
          }
 
-         rc = fMakeParentDirs ?
-                RTDirCreateFullPath(szDir, fileMode)
-              : RTDirCreate(szDir, fileMode);
-
-         if (RT_SUCCESS(rc) && fVerbose)
-             VBoxServiceVerbose(0, "mkdir: Created directory '%s', mode 0x%RTfmode\n", szDir, fileMode); /** @todo r=bird: drop the 0x here, use %#RTfmode instead. */
-         else if (RT_FAILURE(rc)) /** @todo Add a switch with more helpful error texts! */
+         PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
+         RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
          {
-             PCRTSTATUSMSG pMsg = RTErrGet(rc);
-             if (pMsg)
-                 VBoxServiceError("mkdir: Could not create directory: %s\n", pMsg->pszMsgFull);
-             else
-                 VBoxServiceError("mkdir: Could not create directory, rc=%Rrc\n", rc);
+             rc = fMakeParentDirs ?
+                    RTDirCreateFullPath(pNodeIt->pszName, dirMode)
+                  : RTDirCreate(pNodeIt->pszName, dirMode);
+
+             if (RT_SUCCESS(rc) && fVerbose)
+                 RTMsgError("mkdir: Created directory 's', mode %#RTfmode\n", pNodeIt->pszName, dirMode);
+             else if (RT_FAILURE(rc)) /** @todo Add a switch with more helpful error texts! */
+             {
+                 PCRTSTATUSMSG pMsg = RTErrGet(rc);
+                 if (pMsg)
+                     RTMsgError("mkdir: Could not create directory '%s': %s\n",
+                                pNodeIt->pszName, pMsg->pszMsgFull);
+                 else
+                     RTMsgError("mkdir: Could not create directory '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
+             }
          }
-
      }
      else if (fVerbose)
-         VBoxServiceError("mkdir: Failed with rc=%Rrc\n", rc);
-     return rc;
-}
-
-
-/**
- *
- *
- * @return  int
- *
- * @param   argc
- * @param   argv
- */
-int VBoxServiceToolboxCat(int argc, char **argv)
+         RTMsgError("mkdir: Failed with rc=%Rrc\n", rc);
+
+     VBoxServiceToolboxPathBufDestroy(&dirList);
+     return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Main function for tool "vbox_cat".
+ *
+ * @return  RTEXITCODE.
+ * @param   argc                    Number of arguments.
+ * @param   argv                    Pointer to argument array.
+ */
+static int VBoxServiceToolboxCat(int argc, char **argv)
 {
      static const RTGETOPTDEF s_aOptions[] =
      {
-         { "--input",     'i', RTGETOPT_REQ_STRING },
-         { "--output",    'o', RTGETOPT_REQ_STRING },
-         { "--flags",     'f', RTGETOPT_REQ_STRING }
-     /** @todo r=bird: Missing options 'A', 'b', 'e', 'E', 'n', 's', 'T',
-      *        'u', 'v' as found on 'man cat' on a linux system. They must
-      *        not be implemented, just return an apologetic error message. */
+         /* Sorted by short ops. */
+         { "--show-all",            'a',                         RTGETOPT_REQ_NOTHING },
+         { "--number-nonblank",     'b',                         RTGETOPT_REQ_NOTHING },
+         { NULL,                    'e',                         RTGETOPT_REQ_NOTHING },
+         { NULL,                    'E',                         RTGETOPT_REQ_NOTHING },
+         { "--flags",               'f',                         RTGETOPT_REQ_STRING  },
+         { "--no-content-indexed",  CAT_OPT_NO_CONTENT_INDEXED,  RTGETOPT_REQ_NOTHING },
+         { "--number",              'n',                         RTGETOPT_REQ_NOTHING },
+         { "--output",              'o',                         RTGETOPT_REQ_STRING  },
+         { "--squeeze-blank",       's',                         RTGETOPT_REQ_NOTHING },
+         { NULL,                    't',                         RTGETOPT_REQ_NOTHING },
+         { "--show-tabs",           'T',                         RTGETOPT_REQ_NOTHING },
+         { NULL,                    'u',                         RTGETOPT_REQ_NOTHING },
+         { "--show-noneprinting",   'v',                         RTGETOPT_REQ_NOTHING }
      };
-
 
      int ch;
@@ -272,10 +358,15 @@
 
      int rc = VINF_SUCCESS;
-     RTFILE hInput = NIL_RTFILE;
-
+     bool fUsageOK = true;
+
+     char szOutput[RTPATH_MAX] = { 0 };
      RTFILE hOutput = NIL_RTFILE;
      uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
                      | RTFILE_O_WRITE
                      | RTFILE_O_DENY_WRITE;
+
+     /* Init directory list. */
+     RTLISTNODE inputList;
+     RTListInit(&inputList);
 
      while (   (ch = RTGetOpt(&GetState, &ValueUnion))
@@ -285,42 +376,50 @@
          switch (ch)
          {
-             /** @todo r=bird: You add a flag --no-content-indexed without a
-              * short form (use a #define CAT_OPT_NO_CONTENT_INDEXED 1000 for
-              * iShort). */
-
-             case 'f':
-                 /* Process flags; no fancy parsing here yet. */
-                 if (RTStrIStr(ValueUnion.psz, "noindex"))
-                     fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
-                 else
-                 {
-                     VBoxServiceError("cat: Unknown flag set!\n");
-                     rc = VERR_INVALID_PARAMETER;
-                 }
-                 break;
+             case 'a':
+             case 'b':
+             case 'e':
+             case 'E':
+             case 'n':
+             case 's':
+             case 't':
+             case 'T':
+             case 'v':
+                 RTMsgError("cat: Sorry, option '%s' is not implemented yet!\n",
+                            ValueUnion.pDef->pszLong);
+                 rc = VERR_INVALID_PARAMETER;
+                 break;
+
+             case 'h':
+                 VBoxServiceToolboxShowUsage();
+                 return RTEXITCODE_SUCCESS;
 
              case 'o':
-                 rc = RTFileOpen(&hOutput, ValueUnion.psz, fFlags);
-                 if (RT_FAILURE(rc))
-                     VBoxServiceError("cat: Could not create output file \"%s\"! rc=%Rrc\n",
-                                      ValueUnion.psz, rc);
-                 break;
-
-                 /** @todo r=bird: Again, there shall be no need for any -i
-                  *        options since all non-options are input files. */
-
-             case 'i':
+                 if (!RTStrPrintf(szOutput, sizeof(szOutput), ValueUnion.psz))
+                     rc = VERR_NO_MEMORY;
+                 break;
+
+             case 'u':
+                 /* Ignored. */
+                 break;
+
+             case 'V':
+                 VBoxServiceToolboxShowVersion();
+                 return RTEXITCODE_SUCCESS;
+
+             case CAT_OPT_NO_CONTENT_INDEXED:
+                 fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
+                 break;
+
              case VINF_GETOPT_NOT_OPTION:
              {
-                 rc = RTFileOpen(&hInput, ValueUnion.psz,
-                                 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
-                 if (RT_FAILURE(rc))
-                     VBoxServiceError("cat: Could not open input file \"%s\"! rc=%Rrc\n",
-                                      ValueUnion.psz, rc);
+                 /* Add file(s) to buffer. This enables processing multiple paths
+                  * at once.
+                  *
+                  * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
+                  * processing this loop it's safe to immediately exit on syntax errors
+                  * or showing the help text (see above). */
+                 rc = VBoxServiceToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
                  break;
              }
-
-             /** @todo r=bird: Missing handling of the standard options 'V' and
-              *        'h'. */
 
              default:
@@ -330,32 +429,66 @@
 
      if (RT_SUCCESS(rc))
-         rc  = VBoxServiceToolboxCatOutput(hInput, hOutput);
-
-     if (hInput != NIL_RTFILE)
-         RTFileClose(hInput);
+     {
+         if (strlen(szOutput))
+         {
+             rc = RTFileOpen(&hOutput, szOutput, fFlags);
+             if (RT_FAILURE(rc))
+                 RTMsgError("cat: Could not create output file '%s'! rc=%Rrc\n",
+                            szOutput, rc);
+         }
+
+         if (RT_SUCCESS(rc))
+         {
+             /* Process each input file. */
+             PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
+             RTFILE hInput = NIL_RTFILE;
+             RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
+             {
+                 rc = RTFileOpen(&hInput, pNodeIt->pszName,
+                                 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+                 if (RT_SUCCESS(rc))
+                 {
+                     rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
+                     RTFileClose(hInput);
+                 }
+                 else
+                 {
+                     PCRTSTATUSMSG pMsg = RTErrGet(rc);
+                     if (pMsg)
+                         RTMsgError("cat: Could not open input file '%s': %s\n",
+                                    pNodeIt->pszName, pMsg->pszMsgFull);
+                     else
+                         RTMsgError("cat: Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
+                 }
+
+                 if (RT_FAILURE(rc))
+                     break;
+             }
+
+             /* If not input files were defined, process stdin. */
+             if (RTListNodeIsFirst(&inputList, &inputList))
+                 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
+         }
+     }
+
      if (hOutput != NIL_RTFILE)
          RTFileClose(hOutput);
-     return rc;
-}
-
-
-/**
- * Main routine for toolbox command line handling.
- *
- * @return  int
- *
- * @param   argc
- * @param   argv
- */
-int VBoxServiceToolboxMain(int argc, char **argv)
-{
-    /** @todo r=bird: The return type of this function is mixed; both RTEXITCODE
-     *  and IPRT status code.  That doesn't cut it.  The RTEXITCODE part should
-     *  be returned separately from the handled-or-unhandled bit.
-     *
-     *  Also, please change VBoxServiceToolboxCat and VBoxServiceToolboxMkDir to
-     *  return RTEXITCODE and use RTMsg* like RTZipTarCmd (and later
-     *  RTZipGzipCmd). */
-    int rc = VERR_NOT_FOUND;
+     VBoxServiceToolboxPathBufDestroy(&inputList);
+
+     return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Entry point for internal toolbox.
+ *
+ * @return  True if an internal tool was handled, false if not.
+ * @param   argc                    Number of arguments.
+ * @param   argv                    Pointer to argument array.
+ * @param   piExitCode              Pointer to receive exit code when internal command
+ *                                  was handled.
+ */
+bool VBoxServiceToolboxMain(int argc, char **argv, int *piExitCode)
+{
     if (argc > 0) /* Do we have at least a main command? */
     {
@@ -363,16 +496,15 @@
             || !strcmp(argv[0], "vbox_cat"))
         {
-            rc = VBoxServiceToolboxCat(argc, argv);
+            *piExitCode = VBoxServiceToolboxCat(argc, argv);
+            return true;
         }
         else if (   !strcmp(argv[0], "mkdir")
                  || !strcmp(argv[0], "vbox_mkdir"))
         {
-            rc = VBoxServiceToolboxMkDir(argc, argv);
+            *piExitCode = VBoxServiceToolboxMkDir(argc, argv);
+            return true;
         }
     }
-
-    if (rc != VERR_NOT_FOUND)
-        rc = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
-    return rc;
-}
-
+    return false;
+}
+
