[sort] RFC: Implementation of command line sort utility

Ann and Jason Edmeades jason at edmeades.me.uk
Thu Sep 27 18:51:46 CDT 2012


Attached is a patch which gives a near windows like 'sort' utility
by parsing the command line and then calling through to the unix
underlying sort. In tests it gives pretty much the same result as
windows other than I have to force the locale to get preceeding
spaces included in the sort.

Tested in cmd and wineconsole, including mixing stdout with stdin
and pipes. Its very slightly hacky in its implementation as in
order to make a 'background unix process' be able to interoperate
with wineconsole, I store stdin into a temporary file, sort then
delete it. Similarly if writing to stdout it saves to a temporary
file and then displays the contents.

What I can say is in my tests, I get identical results in most
tests between windows and wine except when using character offsets
and shorter lines get ordered differently (very edge case)

I'd like to think this would be commitable, but would appreciate
peoples thoughts on whether this would be acceptable.

I am also not sure on the .po stuff / languages - I've copied what
xcopy and cmd do, and built a .rc file, do I need do anything else.
What about the configure changes - would I include those in any
patch?

Jason
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20120928/8b4e9969/attachment-0001.html>
-------------- next part --------------
From 3384d14b6e620d0ecb779b38e9c3a705f9691818 Mon Sep 17 00:00:00 2001
From: Jason Edmeades <jason at edmeades.me.uk>
Date: Fri, 28 Sep 2012 00:42:01 +0100
Subject: [PATCH 1/1] [sort] RFC: Implementation of command line sort utility

Attached is a patch which gives a near windows like 'sort' utility
by parsing the command line and then calling through to the unix
underlying sort. In tests it gives pretty much the same result as
windows other than I have to force the locale to get preceeding
spaces included in the sort.

Tested in cmd and wineconsole, including mixing stdout with stdin
and pipes. Its very slightly hacky in its implementation as in
order to make a 'background unix process' be able to interoperate
with wineconsole, I store stdin into a temporary file, sort then
delete it. Similarly if writing to stdout it saves to a temporary
file and then displays the contents.

What I can say is in my tests, I get identical results in most
tests between windows and wine except when using character offsets
and shorter lines get ordered differently (very edge case)

I'd like to think this would be commitable, but would appreciate
peoples thoughts on whether this would be acceptable. 

I am also not sure on the .po stuff / languages - I've copied what
xcopy and cmd do, and built a .rc file, do I need do anything else.
What about the configure changes - would I include those in any
patch?
---
 configure                 |    1 +
 configure.ac              |    1 +
 programs/sort/Makefile.in |   12 ++
 programs/sort/sort.c      |  477 +++++++++++++++++++++++++++++++++++++++++++++
 programs/sort/sort.h      |   36 ++++
 programs/sort/sort.rc     |   33 ++++
 6 files changed, 560 insertions(+)
 create mode 100644 programs/sort/Makefile.in
 create mode 100755 programs/sort/sort.c
 create mode 100644 programs/sort/sort.h
 create mode 100644 programs/sort/sort.rc

diff --git a/configure b/configure
index 21044af..f6ce001 100755
--- a/configure
+++ b/configure
@@ -15871,6 +15871,7 @@ wine_fn_config_program secedit enable_secedit install
 wine_fn_config_program servicemodelreg enable_servicemodelreg install
 wine_fn_config_program services enable_services install
 wine_fn_config_test programs/services/tests services.exe_test
+wine_fn_config_program sort enable_sort install,po
 wine_fn_config_program spoolsv enable_spoolsv install
 wine_fn_config_program start enable_start install,po
 wine_fn_config_program svchost enable_svchost install
diff --git a/configure.ac b/configure.ac
index 84b5de8..6509809 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3144,6 +3144,7 @@ WINE_CONFIG_PROGRAM(secedit,,[install])
 WINE_CONFIG_PROGRAM(servicemodelreg,,[install])
 WINE_CONFIG_PROGRAM(services,,[install])
 WINE_CONFIG_TEST(programs/services/tests)
+WINE_CONFIG_PROGRAM(sort,,[install,po])
 WINE_CONFIG_PROGRAM(spoolsv,,[install])
 WINE_CONFIG_PROGRAM(start,,[install,po])
 WINE_CONFIG_PROGRAM(svchost,,[install])
diff --git a/programs/sort/Makefile.in b/programs/sort/Makefile.in
new file mode 100644
index 0000000..1fbf60c
--- /dev/null
+++ b/programs/sort/Makefile.in
@@ -0,0 +1,12 @@
+MODULE    = sort.exe
+
+APPMODE   = -mconsole -municode
+IMPORTS   = user32
+
+C_SRCS = \
+	sort.c
+
+RC_SRCS = sort.rc
+PO_SRCS = sort.rc
+
+ at MAKE_PROG_RULES@
diff --git a/programs/sort/sort.c b/programs/sort/sort.c
new file mode 100755
index 0000000..c792ab4
--- /dev/null
+++ b/programs/sort/sort.c
@@ -0,0 +1,477 @@
+/*
+ * sort - Wine-compatible sort
+ *
+ * Copyright (C) 2012 J Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "sort.h"
+#include "windows.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(sort);
+
+/*****************************************************************************
+ * Constants for the various supported parameters
+ */
+static const WCHAR parmRECMAXW[] = { '/','R','E','C','O','R','D','_',
+  'M','A','X','I','M','U','M','\0'};
+static const WCHAR parmRECW[] = {    '/','R','E','C','\0'};
+static const WCHAR parmMEMORYW[] = { '/','M','E','M','O','R','Y','\0'};
+static const WCHAR parmMW[] = {      '/','M','\0'};
+static const WCHAR parmLOCALEW[] = { '/','L','O','C','A','L','E','\0'};
+static const WCHAR parmLW[] = {      '/','L','\0'};
+static const WCHAR parmRW[] = {      '/','R','\0'};
+static const WCHAR parmPlusW[] = {   '/','+','\0'};
+static const WCHAR parmTEMPW[] = {   '/','T','E','M','P','O','R','A','R','Y','\0'};
+static const WCHAR parmTW[] = {      '/','T','\0'};
+static const WCHAR parmOUTPUTW[] = { '/','O','U','T','P','U','T','\0'};
+static const WCHAR parmOW[] = {      '/','O','\0'};
+static const WCHAR localeCW[] = {    'C','\0'};
+static const WCHAR localeC2W[] = {   '"','C','"','\0'};
+
+/*****************************************************************************
+ * Global variables
+ */
+LPSTR (*CDECL wine_get_unix_file_name_ptr)(LPCWSTR) = NULL;
+
+#define MAX_WRITECONSOLE_SIZE 65535
+static WCHAR *output_bufW = NULL;
+static char  *output_bufA = NULL;
+
+/*****************************************************************************
+ * Load a message from the resource file.
+ */
+static WCHAR *SORT_LoadMessage(UINT id) {
+  static WCHAR msg[MAXSTRING];
+  const WCHAR failedMsg[]  = {'F', 'a', 'i', 'l', 'e', 'd', '!', 0};
+
+  if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
+    WINE_FIXME("LoadString failed with %d\n", GetLastError());
+    lstrcpyW(msg, failedMsg);
+  }
+  return msg;
+}
+
+/*****************************************************************************
+ * Output a formatted unicode string. Ideally this will go to the console
+ *  and hence required WriteConsoleW to output it, however if file i/o is
+ *  redirected, it needs to be WriteFile'd using OEM (not ANSI) format
+ * Code cloned from XCOPY_wprintf adding in stdout/stderr support
+ *  where stream can now be STD_OUTPUT_HANDLE or STD_ERROR_HANDLE
+ */
+static void __cdecl SORT_wprintf(int stream, const WCHAR *format, ...) {
+
+  static BOOL  toConsole    = TRUE;
+  static BOOL  traceOutput  = FALSE;
+
+  __ms_va_list parms;
+  DWORD   nOut;
+  int len;
+  DWORD   res = 0;
+
+  __ms_va_start(parms, format);
+  SetLastError(NO_ERROR);
+  len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, format, 0, 0, output_bufW,
+                       MAX_WRITECONSOLE_SIZE/sizeof(*output_bufW), &parms);
+  __ms_va_end(parms);
+  if (len == 0 && GetLastError() != NO_ERROR) {
+    WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(format));
+    return;
+  }
+
+  /* Try to write as unicode whenever we think it's a console */
+  if (toConsole) {
+    res = WriteConsoleW(GetStdHandle(stream),
+                        output_bufW, len, &nOut, NULL);
+  }
+
+  /* If writing to console has failed (ever) we assume it's file
+     i/o so convert to OEM codepage and output                  */
+  if (!res) {
+    BOOL usedDefaultChar = FALSE;
+    DWORD convertedChars;
+
+    toConsole = FALSE;
+
+    /* Convert to OEM, then output */
+    convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
+                                         len, output_bufA, MAX_WRITECONSOLE_SIZE,
+                                         "?", &usedDefaultChar);
+    WriteFile(GetStdHandle(stream), output_bufA, convertedChars,
+              &nOut, FALSE);
+  }
+
+  /* Trace whether screen or console */
+  if (!traceOutput) {
+    WINE_TRACE("Writing to console? (%d)\n", toConsole);
+    traceOutput = TRUE;
+  }
+  return;
+}
+
+/*****************************************************************************
+ * Convert a Windows file or directory to a unix file or directory
+ */
+void GetUnixPath(char *unix_pathequiv, WCHAR *windows_name) {
+  char *unix_name = NULL;
+
+  unix_pathequiv[0] = 0x00;
+  unix_name = wine_get_unix_file_name_ptr(windows_name);
+  if (unix_name) {
+    strcpy(unix_pathequiv, unix_name);
+    HeapFree( GetProcessHeap(), 0, unix_name );
+  } else {
+    SORT_wprintf(STD_ERROR_HANDLE, SORT_LoadMessage(CONVERT_FAILED), windows_name);
+  }
+}
+
+/***************************************************************************
+ * SORT_is_console_handle
+ *
+ * Identifies a handle compared to a console handle
+ * Note: Cloned from equivalent routine in cmd.exe
+ */
+static inline BOOL SORT_is_console_handle(HANDLE h)
+{
+    return (((DWORD_PTR)h) & 3) == 3;
+}
+
+/***************************************************************************
+ * SORT_Readfile
+ *
+ * Read characters in from a console/file, returning result in Unicode
+ * Note: Cloned from equivalent routine in cmd.exe
+ */
+BOOL SORT_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead)
+{
+    DWORD numRead;
+
+    if (SORT_is_console_handle(hIn))
+        /* Try to read from console as Unicode */
+        return ReadConsoleW(hIn, intoBuf, maxChars, charsRead, NULL);
+
+    /* We assume it's a file handle and read then convert from assumed OEM codepage */
+    if (!ReadFile(hIn, output_bufA, maxChars, &numRead, NULL))
+        return FALSE;
+
+    *charsRead = MultiByteToWideChar(GetConsoleCP(), 0, output_bufA, numRead, intoBuf, maxChars);
+
+    return TRUE;
+}
+
+/*****************************************************************************
+ * Display the contents of a unix file out to stdout/stderr - expect file to
+ * be non existant.
+ */
+void DisplayContents(WCHAR *filename, int stream) {
+
+  WCHAR   buffer[MAXSTRING];
+  DWORD   count;
+
+  HANDLE h = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (h != INVALID_HANDLE_VALUE) {
+    while (SORT_ReadFile(h, buffer, sizeof(buffer)/sizeof(WCHAR) - 1, &count)) {
+      if (count == 0) break;             /* ReadFile reports success on EOF! */
+      buffer[count] = 0;
+      SORT_wprintf(stream, buffer);
+    }
+    CloseHandle (h);
+  }
+}
+
+/*****************************************************************************
+ * Save the contents of a stream to a file
+ */
+void SaveContents(WCHAR *filename, int stream) {
+
+  WCHAR   buffer[MAXSTRING];
+  DWORD   count, nOut;
+  BOOL    usedDefaultChar = FALSE;
+  DWORD   convertedChars;
+
+  HANDLE h = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
+                         CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (h != INVALID_HANDLE_VALUE) {
+    while (SORT_ReadFile(GetStdHandle(stream), buffer, sizeof(buffer)/sizeof(WCHAR) - 1, &count)) {
+      if (count == 0) break;             /* ReadFile reports success on EOF! */
+      buffer[count] = 0;
+
+      /* Convert to OEM, then output */
+      convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, buffer,
+                                           count, output_bufA, MAX_WRITECONSOLE_SIZE,
+                                           "?", &usedDefaultChar);
+      WriteFile(h, output_bufA, convertedChars, &nOut, FALSE);
+    }
+    CloseHandle (h);
+  }
+}
+
+/*****************************************************************************
+ * Main entry point. This is a console application so we have a main() not a
+ * winmain().
+ */
+
+int wmain (int argc, WCHAR *argvW[])
+{
+  int     args = argc;
+  LONG    opt_charNum = 0;
+  BOOL    opt_Clocale = FALSE;
+  LONG    opt_memory  = -1;
+  LONG    opt_maxrec  = -1;
+  BOOL    opt_reverse = FALSE;
+  WCHAR   opt_inputfile[MAXSTRING];
+  WCHAR   opt_temporarydir[MAXSTRING];
+  WCHAR   opt_outputfile[MAXSTRING];
+  char    unix_pathequiv[MAXSTRING];
+  char    unix_commandline[MAXSTRING];
+  char    buffer[MAXSTRING];
+  WCHAR   stderrfile[MAXSTRING];
+  BOOL    temporaryStdIn = FALSE;
+
+  opt_inputfile[0] = '\0';
+  opt_temporarydir[0] = '\0';
+  opt_outputfile[0] = '\0';
+  stderrfile[0] = '\0';
+
+  /*
+   * Allocate buffer to use when reading/writing to console
+   * Note: Not freed - memory will be allocated once and released when
+   *         program ends
+   */
+  output_bufW = HeapAlloc(GetProcessHeap(), 0, MAX_WRITECONSOLE_SIZE);
+  output_bufA = HeapAlloc(GetProcessHeap(), 0, MAX_WRITECONSOLE_SIZE);
+  if (!output_bufW || !output_bufA) {
+    WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
+    return 1;
+  }
+
+  /* Get internal routine to do the conversion from windows to unix for us */
+  wine_get_unix_file_name_ptr = (void*)
+                                GetProcAddress(GetModuleHandleA("KERNEL32"),
+                                               "wine_get_unix_file_name");
+  if (wine_get_unix_file_name_ptr == NULL) {
+    SORT_wprintf(STD_ERROR_HANDLE, SORT_LoadMessage(ENTRYPOINT_MISSING));
+    return 1;
+  }
+
+  /* Loop through, processing each arg except the first (program name) */
+  argvW++;
+  args--;
+  while (args > 0) {
+    WINE_TRACE("Command line parm: '%s'\n", wine_dbgstr_w(*argvW));
+
+    if ((CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                        parmRECMAXW, -1, *argvW, -1) == CSTR_EQUAL) ||
+        (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                        parmRECW, -1, *argvW, -1) == CSTR_EQUAL)) {
+      argvW++;
+      args--;
+      opt_maxrec = atolW(*argvW);
+      WINE_TRACE("Found record_maximum of %d\n", opt_maxrec);
+      WINE_WARN("Ignoring maximum record length as no unix equivalent\n");
+    } else if ((CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                               parmMEMORYW, -1, *argvW, -1) == CSTR_EQUAL) ||
+               (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                               parmMW, -1, *argvW, -1) == CSTR_EQUAL)) {
+      argvW++;
+      args--;
+      opt_memory = atolW(*argvW);
+      WINE_TRACE("Found memory of %d\n", opt_memory);
+    } else if ((CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                               parmLOCALEW, -1, *argvW, -1) == CSTR_EQUAL) ||
+               (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                               parmLW, -1, *argvW, -1) == CSTR_EQUAL)) {
+      argvW++;
+      args--;
+      if ((CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                          localeCW, -1, *argvW, -1) == CSTR_EQUAL) ||
+          (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                          localeC2W, -1, *argvW, -1) == CSTR_EQUAL)) {
+        WINE_TRACE("Found C locale\n");
+        opt_Clocale = TRUE;
+      } else {
+        SORT_wprintf(STD_ERROR_HANDLE, SORT_LoadMessage(INVALID_LOCALE));
+        return 1;
+      }
+    } else if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                              parmRW, -1, *argvW, -1) == CSTR_EQUAL) {
+      opt_reverse = TRUE;
+      WINE_TRACE("Found reverse option\n");
+    } else if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                              parmPlusW, 2, *argvW, 2) == CSTR_EQUAL) {
+      opt_charNum = atolW((*argvW) + 2);
+      WINE_TRACE("Found character offset option %d\n", opt_charNum);
+    } else if ((CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                               parmTEMPW, -1, *argvW, -1) == CSTR_EQUAL) ||
+               (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                               parmTW, -1, *argvW, -1) == CSTR_EQUAL)) {
+      argvW++;
+      args--;
+      if (opt_temporarydir[0] != '\0') {
+        SORT_wprintf(STD_ERROR_HANDLE, SORT_LoadMessage(INVALID_SYNTAX));
+        return 1;
+      } else {
+        strcpyW(opt_temporarydir, *argvW);
+        WINE_TRACE("Found temporary dir of %s\n", wine_dbgstr_w(opt_temporarydir));
+      }
+    } else if ((CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                               parmOUTPUTW, -1, *argvW, -1) == CSTR_EQUAL) ||
+               (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                               parmOW, -1, *argvW, -1) == CSTR_EQUAL)) {
+      argvW++;
+      args--;
+      if (opt_outputfile[0] != '\0') {
+        SORT_wprintf(STD_ERROR_HANDLE, SORT_LoadMessage(INVALID_SYNTAX));
+        return 1;
+      } else {
+        strcpyW(opt_outputfile, *argvW);
+        WINE_TRACE("Found output file of %s\n", wine_dbgstr_w(opt_temporarydir));
+      }
+    } else {
+      if (opt_inputfile[0] != '\0') {
+        SORT_wprintf(STD_ERROR_HANDLE, SORT_LoadMessage(MULTIPLE_INPUTS));
+        return 1;
+      } else {
+        strcpyW(opt_inputfile, *argvW);
+        WINE_TRACE("Found input file of %s\n", wine_dbgstr_w(opt_temporarydir));
+      }
+    }
+    argvW++;
+    args--;
+  }
+
+  /* Display the results of the parameter parsing */
+  WINE_TRACE("Options:\n");
+  WINE_TRACE("  Reading from    : %s\n",
+             (opt_inputfile[0] == '\0')?"stdin": wine_dbgstr_w(opt_inputfile));
+  WINE_TRACE("  Writing to      : %s\n",
+             (opt_outputfile[0] == '\0')?"stdout": wine_dbgstr_w(opt_outputfile));
+  WINE_TRACE("  Temporary dir   : %s\n",
+             (opt_temporarydir[0] == '\0')?"n/a": wine_dbgstr_w(opt_temporarydir));
+  WINE_TRACE("  Char offset     : %d\n", (opt_charNum));
+  WINE_TRACE("  Memory (K)      : %d\n", (opt_memory));
+  WINE_TRACE("  MaxRec          : %d\n", (opt_maxrec));
+  if (opt_reverse) WINE_TRACE("  Reversed\n");
+  else             WINE_TRACE("  Normal sort order\n");
+  if (opt_Clocale) WINE_TRACE("  C locale\n");
+  else             WINE_TRACE("  Normal locale\n");
+
+  /* Child process will hang reading from stdin, so go via a file  */
+  if (opt_inputfile[0] == '\0') {
+    WCHAR tempPath[MAXSTRING];
+    static const WCHAR prefixW[] = {'s','t',0};
+    GetTempPathW(MAXSTRING, tempPath);
+    GetTempFileNameW(tempPath, prefixW, 0, opt_inputfile);
+    WINE_WARN("sort does not support stdin yet - going via intermediate file %s\n",
+              wine_dbgstr_w(opt_inputfile));
+    temporaryStdIn = TRUE;
+
+    /* This is ugly and bad performant - read stdin until end of stream, and write
+       to the intermediate temporary file                                         */
+    SaveContents(opt_inputfile, STD_INPUT_HANDLE);
+
+  } else {
+    /* Verify file exists prior to calling through */
+    if (GetFileAttributesW(opt_inputfile) == INVALID_FILE_ATTRIBUTES) {
+      SORT_wprintf(STD_ERROR_HANDLE, SORT_LoadMessage(MISSING_SOURCE), opt_inputfile);
+      return 1;
+    }
+  }
+
+  /* Child process cannot output to stdout (e.g. does not go to wineconsole) */
+  if (opt_outputfile[0] == '\0') {
+    WCHAR tempPath[MAXSTRING];
+    static const WCHAR prefixW[] = {'s','t',0};
+    GetTempPathW(MAXSTRING, tempPath);
+    GetTempFileNameW(tempPath, prefixW, 0, opt_outputfile);
+    GetTempFileNameW(tempPath, prefixW, 0, stderrfile);
+    WINE_WARN("sort does not support stdout yet - going via intermediate files %s and %s.\n",
+              wine_dbgstr_w(opt_outputfile), wine_dbgstr_w(stderrfile));
+  }
+
+  /* Now build an ansi string equivalent to the Unix SORT command string */
+  unix_commandline[0] = 0x00;
+
+  /* FIXME: Unix sort does odd things with preceeding whitespace depending on
+     the locale. Default to the C locale to get this as close to windows as
+     possible.
+  if (opt_Clocale) */
+  strcat(unix_commandline, "LC_ALL=C ");          /* Force C locale         */
+  strcat(unix_commandline, "sort ");                  /* The program to invoke  */
+  strcat(unix_commandline, "--ignore-case ");         /* Force case insensitive */
+  if (opt_reverse)
+    strcat(unix_commandline, "--reverse ");         /* Reverse sort           */
+  if (opt_memory != -1) {
+    sprintf(buffer, "--buffer-size=%dK ", opt_memory);
+    strcat(unix_commandline, buffer);               /* Specify memory size    */
+  }
+  if (opt_charNum != 0) {
+    strcat(unix_commandline, "--field-separator='\\0' "); /* One field only   */
+    sprintf(buffer, "--key=1.%d ", opt_charNum);    /* Start key at column    */
+    strcat(unix_commandline, buffer);
+  }
+  if (opt_temporarydir[0]) {
+    GetUnixPath(unix_pathequiv, opt_temporarydir);
+    strcat(unix_commandline, "--temporary-directory="); /* Temporary location */
+    strcat(unix_commandline, unix_pathequiv);       /*    unix directory      */
+    strcat(unix_commandline, " ");                  /*    ending seperator    */
+  }
+  /* Note: Due to the stdout/stderr hack, this will always be suppliec */
+  if (opt_outputfile[0]) {
+    GetUnixPath(unix_pathequiv, opt_outputfile);
+    strcat(unix_commandline, "--output=");          /* Output file            */
+    strcat(unix_commandline, unix_pathequiv);       /*    unix directory      */
+    strcat(unix_commandline, " ");                  /*    ending seperator    */
+  }
+  if (opt_inputfile[0]) {
+    GetUnixPath(unix_pathequiv, opt_inputfile);
+    strcat(unix_commandline, unix_pathequiv);       /*    unix directory      */
+    strcat(unix_commandline, " ");                  /*    ending seperator    */
+  }
+
+  /* Hack to work around stdout/stderr support - we need to go via a file as
+     a unix program cannot output to e.g. wineconsole                         */
+  if (stderrfile[0]) {
+    GetUnixPath(unix_pathequiv, stderrfile);
+    sprintf(buffer, "2>%s", unix_pathequiv);        /* Redirect stdout        */
+    strcat(unix_commandline, buffer);
+  }
+
+  /* Now spawn the unix sort to do its work */
+  WINE_TRACE("Running %s", unix_commandline);
+  system(unix_commandline);
+  WINE_TRACE("Completed!");
+
+  /* If the program expected to write to stdout, we now need to pretend that  */
+  /* it did!                                                                  */
+  if (stderrfile[0]) {
+    /* Read and output all of stderr and then stdout files, then delete them */
+    DisplayContents(stderrfile, STD_ERROR_HANDLE);
+    DisplayContents(opt_outputfile, STD_OUTPUT_HANDLE);
+    DeleteFileW(stderrfile);
+    DeleteFileW(opt_outputfile);
+  }
+
+  /* If the program expected to read from stdin and we put stdin into a       */
+  /* temporary file, now is a good time to delete it!                         */
+  if (temporaryStdIn) DeleteFileW(opt_inputfile);
+  return 0;
+}
+
diff --git a/programs/sort/sort.h b/programs/sort/sort.h
new file mode 100644
index 0000000..3f7225e
--- /dev/null
+++ b/programs/sort/sort.h
@@ -0,0 +1,36 @@
+/*
+ * sort - Wine-compatible sort
+ *
+ * Copyright (C) 2012 J Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windef.h>
+#ifndef RC_INVOKED
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/* Constants */
+#define MAXSTRING 32767
+
+/* String table identifiers */
+#define ENTRYPOINT_MISSING 0x100
+#define CONVERT_FAILED     0x101
+#define INVALID_LOCALE     0x102
+#define INVALID_SYNTAX     0x103
+#define MULTIPLE_INPUTS    0x104
+#define MISSING_SOURCE     0x105
diff --git a/programs/sort/sort.rc b/programs/sort/sort.rc
new file mode 100644
index 0000000..2a38f07
--- /dev/null
+++ b/programs/sort/sort.rc
@@ -0,0 +1,33 @@
+/*
+ * Wine sort resources
+ *
+ * Copyright (C) 2012 J Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "sort.h"
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+ENTRYPOINT_MISSING, "Cannot get the address of 'wine_get_unix_file_name'\n"
+CONVERT_FAILED,     "Failed to convert %1 to unix path\n"
+INVALID_LOCALE,     "Invalid locale specified\n"
+INVALID_SYNTAX,     "Invalid syntax\n"
+MULTIPLE_INPUTS,    "Can only provide a single input file to sort\n"
+MISSING_SOURCE,     "File %1 does not exist\n"
+}
-- 
1.7.9.5


More information about the wine-patches mailing list