From b3c4bb00f3047ec00bce174a2c74519859e234fc Mon Sep 17 00:00:00 2001
From: Louis Lenders <xerox.xerox2000x@gmail.com>
Date: Mon, 17 Feb 2020 23:33:29 +0100
Subject: [PATCH] tasklist.exe: add minimal functionality

Signed-off-by: Louis Lenders <xerox.xerox2000x@gmail.com>
---
 programs/tasklist/tasklist.c | 180 ++++++++++++++++++++++++++++++++++-
 1 file changed, 179 insertions(+), 1 deletion(-)

diff --git a/programs/tasklist/tasklist.c b/programs/tasklist/tasklist.c
index fd03f50dae..6e88895d5e 100644
--- a/programs/tasklist/tasklist.c
+++ b/programs/tasklist/tasklist.c
@@ -16,18 +16,196 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include <windows.h>
+#include <winternl.h>
+
+#include "wine/heap.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(tasklist);
 
+static HRESULT (WINAPI *pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*);
+
+static BOOL has_pid = FALSE, has_eq = FALSE, has_filter=FALSE, has_imagename = FALSE;
+
+WCHAR req_pid[sizeof(HANDLE)];
+WCHAR req_imagename[MAX_PATH];
+
+static void write_to_stdout(const WCHAR *str)
+{
+    char *str_converted;
+    UINT str_converted_length;
+    DWORD bytes_written;
+    UINT str_length = lstrlenW(str);
+    int codepage = CP_ACP;
+
+    str_converted_length = WideCharToMultiByte(codepage, 0, str, str_length, NULL, 0, NULL, NULL);
+    str_converted = heap_alloc(str_converted_length);
+    WideCharToMultiByte(codepage, 0, str, str_length, str_converted, str_converted_length, NULL, NULL);
+
+    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), str_converted, str_converted_length, &bytes_written, NULL);
+    if (bytes_written < str_converted_length)
+        ERR("Failed to write output\n");
+
+    heap_free(str_converted);
+}
+
+static void write_out_processes(void)
+{
+    ULONG                            ret_size, i;
+    NTSTATUS                         status;
+    BYTE                             *buf = NULL;
+    ULONG                            size = 0;
+    SYSTEM_PROCESS_INFORMATION       *spi = NULL;
+    ULONG                            processcount = 0;
+    WCHAR                            pid[sizeof(HANDLE)];
+
+    do
+    {
+        size += 0x10000;
+        buf = heap_alloc(size);
+
+        status = pNtQuerySystemInformation(SystemProcessInformation, buf, size, &ret_size);
+
+        if (status == STATUS_INFO_LENGTH_MISMATCH)
+            heap_free(buf);
+
+    } while (status == STATUS_INFO_LENGTH_MISMATCH);
+
+    /* Get number of processes */
+    spi = (SYSTEM_PROCESS_INFORMATION *)buf;
+    while (spi)
+    {
+        processcount++;
+        if (spi->NextEntryOffset == 0)
+            break;
+        spi = (SYSTEM_PROCESS_INFORMATION *)((BYTE *)spi + spi->NextEntryOffset);
+    }
+
+    spi = (SYSTEM_PROCESS_INFORMATION *)buf;
+    /* only print non-verbose processname + pid in csv format*/
+    for (i=0; i<processcount; i++)
+    {
+        swprintf(pid, ARRAY_SIZE(pid), L"%d", spi->UniqueProcessId);
+
+        if (has_filter && has_pid && has_eq && wcsicmp(pid,req_pid))
+            TRACE("filter on, skip writing info for %s\n",  wine_dbgstr_w(pid));
+        else if (has_filter && has_imagename && has_eq && wcsicmp(spi->ProcessName.Buffer,req_imagename))
+            TRACE("filter on, skip writing onfo for %s\n",  wine_dbgstr_w(spi->ProcessName.Buffer));
+        else
+        {
+            write_to_stdout(spi->ProcessName.Buffer);
+            write_to_stdout(L",");
+            write_to_stdout(pid);
+            write_to_stdout(L"\n");
+        }
+        spi = (SYSTEM_PROCESS_INFORMATION *)((BYTE *)spi + spi->NextEntryOffset);
+    }
+}
+
+
+static BOOL process_arguments(int argc, WCHAR *argv[])
+{
+    int i;
+    WCHAR *argdata;
+
+    if (argc == 1)
+        return TRUE;
+
+    for (i = 1; i < argc; i++)
+    {
+        argdata = argv[i];
+
+        if (*argdata != '/')
+            goto invalid;
+        argdata++;
+
+        if (!wcsicmp(L"nh", argdata) || !wcsicmp(L"v", argdata))
+            FIXME("Option %s ignored\n", wine_dbgstr_w(argdata));
+
+        if (!wcsicmp(L"fo", argdata))
+        {
+            i++;
+            FIXME("Option %s ignored\n", wine_dbgstr_w(argdata));
+        }
+
+        if (!wcsicmp(L"\?", argdata) || !wcsicmp(L"s", argdata) || !wcsicmp(L"svc", argdata) ||
+        !wcsicmp(L"u", argdata) || !wcsicmp(L"p", argdata) || !wcsicmp(L"m", argdata))
+        {
+            FIXME("Option %s not implemented, returning error for now\n", wine_dbgstr_w(argdata));
+            return FALSE;
+        }
+
+        if (!wcsicmp(L"fi", argdata))
+        {
+            has_filter = TRUE;
+
+            if (!argv[i + 1])
+                goto invalid;
+            i++;
+
+            argdata = argv[i];
+
+            while(iswspace(*argdata)) ++argdata;
+            /* FIXME might need more invalid option stings here */
+            if (!wcsnicmp(L"PID", argdata,3))
+                has_pid = TRUE;
+
+            else if (!wcsnicmp(L"Imagename", argdata,9))
+                has_imagename = TRUE;
+            /* FIXME report other unhandled stuff like STATUS, CPUTIME etc. here */
+            else
+                FIXME("Filter option %s ignored\n", wine_dbgstr_w(argdata));
+
+            while(!iswspace(*argdata)) ++argdata;
+            while(iswspace(*argdata)) ++argdata;
+
+            if (!wcsnicmp(L"eq", argdata,2))
+                has_eq = TRUE;
+            else
+                FIXME("Filter operator %s ignored\n", wine_dbgstr_w(argdata));
+
+            while(!iswspace(*argdata)) ++argdata;
+            while(iswspace(*argdata)) ++argdata;
+            /* FIXME report / return error if pid or imagename is missing in command line (?)*/
+            if(has_pid)
+                lstrcpyW(req_pid, argdata);
+
+            if(has_imagename)
+                lstrcpyW(req_imagename, argdata);
+        }
+    }
+
+    return TRUE;
+
+    invalid:
+    ERR("Invalid usage\n");
+    return FALSE;
+}
+
 int __cdecl wmain(int argc, WCHAR *argv[])
 {
     int i;
 
-    WINE_FIXME("stub:");
+    pNtQuerySystemInformation = (void *)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation");
+
+    if (!pNtQuerySystemInformation)
+    {
+        ERR("failed to get functionpointer\n");
+        return -1;
+    }
+
+    WINE_FIXME("stub, only printing tasks in cvs format...\n");
     for (i = 0; i < argc; i++)
         WINE_FIXME(" %s", wine_dbgstr_w(argv[i]));
     WINE_FIXME("\n");
 
+    if(!process_arguments(argc, argv))
+        return 1;
+
+    write_out_processes();
+
     return 0;
 }
-- 
2.25.0

