{"thread":{"id":"21671","subject":"[RFC/PATCH] Add an implementation of uname(1) for Windows","startedAt":"2009-11-19T19:00:19Z","lastAt":"2009-11-19T19:00:19Z","messageCount":1,"participants":["Ramsay Jones"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"127919","messageId":"4B0595C3.6010206@ramsay1.demon.co.uk","threadId":"21671","inReplyTo":null,"subject":"[RFC/PATCH] Add an implementation of uname(1) for Windows","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsay1.demon.co.uk","sentAt":"2009-11-19T19:00:19Z","receivedAt":"2009-11-19T19:00:19Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\nSigned-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\n---\n\nWhile changing the Makefile recently, to fix-up the msvc build on\ncygwin, I thought it may be useful to have a uname(1) command on\nWindows. Well, it isn't actually needed, but it got me thinking\nabout how I would implement it...\n\nI started off with some assembler to access the cpuid instruction\non x86 machines... but I soon discarded that, since all the information\nI needed was already available via an WIN32 API or from the registry.\n\nSo, this is not a serious patch and is not meant for application.\nHowever, some Windows users may find it useful... ;-)\n\nATB,\nRamsay Jones\n\n compat/vcbuild/uname.c |  475 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 475 insertions(+), 0 deletions(-)\n create mode 100644 compat/vcbuild/uname.c\n\ndiff --git a/compat/vcbuild/uname.c b/compat/vcbuild/uname.c\nnew file mode 100644\nindex 0000000..7ca6cdd\n--- /dev/null\n+++ b/compat/vcbuild/uname.c\n@@ -0,0 +1,475 @@\n+\n+/*\n+ * uname(1) for Windows\n+ *\n+ * This program is licensed under the GPL v2.\n+ *\n+ * This file is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License, version 2,\n+ * as published by the Free Software Foundation.\n+ *\n+ * This file is distributed in the hope that it will be useful, but\n+ * WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ * General Public License for more details.\n+ *\n+ */\n+\n+/*\n+ * MSVC:         cl uname.c advapi32.lib\n+ *\n+ * Open Watcom:  wcl386 uname.c\n+ *\n+ * Cygwin gcc:   gcc -mno-cygwin -o uname uname.c\n+ *\n+ */\n+\n+#ifndef _WIN32_WINNT\n+#define _WIN32_WINNT 0x0500\n+#endif\n+\n+#include <windows.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <stdarg.h>\n+#include <string.h>\n+\n+#define VERSION_STR \"0.1\"\n+\n+static char *prog;\n+\n+static int all       = 0;\n+static int kernel    = 0;\n+static int host      = 0;\n+static int release   = 0;\n+static int version   = 0;\n+static int machine   = 0;\n+static int processor = 0;\n+static int hardware  = 0;\n+static int os        = 0;\n+\n+static void set_program_name(char *argv0, char *def)\n+{\n+\tstatic char name[256];\n+\tchar *slash;\n+\tint len;\n+\n+\tif (!argv0 || !*argv0) {\n+\t\tprog = def;\n+\t\treturn;\n+\t}\n+\n+\t/* get last path component */\n+\tif ((slash = strrchr(argv0, '/')) == NULL)\n+\t\tprog = argv0;\n+\telse\n+\t\tprog = slash+1;\n+\n+\t/* check for win32 separator */\n+\tif ((slash = strrchr(prog, '\\\\')) != NULL)\n+\t\tprog = slash+1;\n+\n+\t/* strip '.exe' suffix, if any */\n+\tlen = strlen(prog);\n+\tif (len > 4 && strcmp(&prog[len-4], \".exe\") == 0) {\n+\t\tif (len < sizeof(name)) {\n+\t\t\tstrcpy(name, prog);\n+\t\t\tname[len-4] = '\\0';\n+\t\t\tprog = name;\n+\t\t}\n+\t}\n+}\n+\n+static void die(const char *fmt, ...)\n+{\n+\tva_list ap;\n+\n+\tif (prog)\n+\t\tfprintf(stderr, \"%s: \", prog);\n+\tva_start(ap, fmt);\n+\tvfprintf(stderr, fmt, ap);\n+\tva_end(ap);\n+\tfprintf(stderr, \"\\n\");\n+\texit(1);\n+}\n+\n+static void usage(void)\n+{\n+\tprintf(\"usage: %s [OPTION]...\\n\\n\", prog);\n+\tprintf(\"  -a, --all                \"\n+\t\t      \"print all information, except -p and -i\\n\");\n+\tprintf(\"  -s, --kernel-name        \"\n+\t\t      \"print the kernel name\\n\");\n+\tprintf(\"  -n, --nodename           \"\n+\t\t      \"print the network node hostname\\n\");\n+\tprintf(\"  -r, --kernel-release     \"\n+\t\t      \"print the kernel release\\n\");\n+\tprintf(\"  -v, --kernel-version     \"\n+\t\t      \"print the kernel version\\n\");\n+\tprintf(\"  -m, --machine            \"\n+\t\t      \"print the machine hardware name\\n\");\n+\tprintf(\"  -p, --processor          \"\n+\t\t      \"print the processor type\\n\");\n+\tprintf(\"  -i, --hardware-platform  \"\n+\t\t      \"print the hardware platform\\n\");\n+\tprintf(\"  -o, --operating-system   \"\n+\t\t      \"print the operating system\\n\");\n+\tprintf(\"      --help               \"\n+\t\t      \"print this help and exit\\n\");\n+\tprintf(\"      --version            \"\n+\t\t      \"print version information and exit\\n\");\n+\tprintf(\"\\n\");\n+\texit(0);\n+}\n+\n+static void print_version(void)\n+{\n+\tprintf(\"%s version %s\\n\", prog, VERSION_STR);\n+\texit(0);\n+}\n+\n+static void do_long_option(char *opt)\n+{\n+\tstatic struct { char *name; int *option; } tab[] = {\n+\t\t{ \"--all\",               &all       },\n+\t\t{ \"--kernel-name\",       &kernel    },\n+\t\t{ \"--nodename\",          &host      },\n+\t\t{ \"--kernel-release\",    &release   },\n+\t\t{ \"--kernel-version\",    &version   },\n+\t\t{ \"--machine\",           &machine   },\n+\t\t{ \"--processor\",         &processor },\n+\t\t{ \"--hardware-platform\", &hardware  },\n+\t\t{ \"--operating-system\",  &os        },\n+\t\t{ NULL,                  NULL       }\n+\t};\n+\tint i;\n+\n+\tif (!opt || !*opt)\n+\t\tdie(\"internal error: !opt || !*opt\");\n+\n+\tfor (i = 0; tab[i].name; i++) {\n+\t\tif (strcmp(opt, tab[i].name) == 0) {\n+\t\t\t*tab[i].option = 1;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tif (strcmp(opt, \"--help\") == 0)\n+\t\tusage();          /* does not return */\n+\n+\tif (strcmp(opt, \"--version\") == 0)\n+\t\tprint_version();  /* does not return */\n+\n+\tdie(\"unknown option: '%s', try --help\", opt);\n+}\n+\n+static void do_short_options(char *opts)\n+{\n+\tchar *a = &opts[1];\n+\n+\twhile (*a) {\n+\t\tswitch (*a) {\n+\t\t\tcase 'a': all = 1;       break;\n+\t\t\tcase 's': kernel = 1;    break;\n+\t\t\tcase 'n': host = 1;      break;\n+\t\t\tcase 'r': release = 1;   break;\n+\t\t\tcase 'v': version = 1;   break;\n+\t\t\tcase 'm': machine = 1;   break;\n+\t\t\tcase 'p': processor = 1; break;\n+\t\t\tcase 'i': hardware = 1;  break;\n+\t\t\tcase 'o': os = 1;        break;\n+\t\t\tdefault:\n+\t\t\t\t  die(\"unknown option: '%c', try --help\", *a);\n+\t\t\t\t  break;\n+\t\t}\n+\t\t++a;\n+\t}\n+}\n+\n+static void process_args(int argc, char *argv[])\n+{\n+\tint argpos = 1;\n+\n+\tif (argc == 1)    /* no options... */\n+\t\tkernel = 1;  /* same as -s */\n+\n+\t/* check for [OPTIONS]... */\n+\twhile (argpos < argc) {\n+\t\tif (argv[argpos][0] != '-')  /* no option */\n+\t\t\tbreak;\n+\t\tif (argv[argpos][1] == '-')  /* --long option */\n+\t\t\tdo_long_option(argv[argpos]);\n+\t\telse                         /* -short option(s) */\n+\t\t\tdo_short_options(argv[argpos]);\n+\t\targpos++;\n+\t}\n+\n+\t/* no other args allowed */\n+\tif (argpos != argc)\n+\t\tdie(\"unexpected argument: '%s', try --help\", argv[argpos]);\n+\n+\tif (all)\n+\t\tkernel = host = release = version = machine = os = 1;\n+}\n+\n+static char *get_kernel(void)\n+{\n+\treturn \"Windows\";\n+}\n+\n+static char *get_hostname(void)\n+{\n+\tstatic char name[256];\n+\tunsigned long len = sizeof(name)-1;\n+\n+\tif (GetComputerNameExA(ComputerNameDnsHostname, name, &len))\n+\t\treturn name;\n+\treturn \"unknown\";\n+}\n+\n+static OSVERSIONINFO *get_version_info(void)\n+{\n+\tstatic OSVERSIONINFO ver;\n+\tstatic int called;\n+\n+\tif (!called) {\n+\t\tver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\n+\t\tGetVersionEx(&ver);\n+\t\t++called;\n+\t}\n+\treturn &ver;\n+}\n+static char *get_release(void)\n+{\n+\tstatic char str[256];\n+\tOSVERSIONINFO *v = get_version_info();\n+\t_snprintf(str, sizeof(str)-1, \"%d.%d\",\n+\t\t\tv->dwMajorVersion, v->dwMinorVersion);\n+\treturn str;\n+}\n+\n+static char *get_version(void)\n+{\n+\tstatic char str[256];\n+\tOSVERSIONINFO *v = get_version_info();\n+\t_snprintf(str, sizeof(str)-1, \"%d(%s)\",\n+\t\t\tv->dwBuildNumber, v->szCSDVersion);\n+\treturn str;\n+}\n+\n+static SYSTEM_INFO *get_system_info(void)\n+{\n+\tstatic SYSTEM_INFO sys;\n+\tstatic int called;\n+\n+\tif (!called) {\n+\t\tGetSystemInfo(&sys);\n+\t\t++called;\n+\t}\n+\treturn &sys;\n+}\n+\n+static char *get_arch(int arch)\n+{\n+\tstatic struct { int arch_enum; char *name; } tab[] = {\n+\t\t{ PROCESSOR_ARCHITECTURE_INTEL,   \"INTEL\"    },\n+\t\t{ PROCESSOR_ARCHITECTURE_MIPS,    \"MIPS\"     },\n+\t\t{ PROCESSOR_ARCHITECTURE_ALPHA,   \"ALPHA\"    },\n+\t\t{ PROCESSOR_ARCHITECTURE_PPC,     \"PPC\"      },\n+\t\t{ PROCESSOR_ARCHITECTURE_SHX,     \"SHX\"      },\n+\t\t{ PROCESSOR_ARCHITECTURE_ARM,     \"ARM\"      },\n+\t\t{ PROCESSOR_ARCHITECTURE_IA64,    \"IA-64\"    },\n+\t\t{ PROCESSOR_ARCHITECTURE_ALPHA64, \"ALPHA-64\" },\n+\t\t{ PROCESSOR_ARCHITECTURE_AMD64,   \"AMD-64\"   },\n+\t\t{ PROCESSOR_ARCHITECTURE_UNKNOWN, NULL       }\n+\t};\n+\tint i;\n+\n+\tfor (i = 0; tab[i].name; i++)\n+\t\tif (tab[i].arch_enum == arch)\n+\t\t\treturn tab[i].name;\n+\treturn NULL;\n+}\n+\n+static char *get_x86_machine(int family)\n+{\n+\tstatic char m[256];\n+\n+\tswitch (family) {\n+\t\tcase 3: case 4: case 5: case 6:\n+\t\t\t_snprintf(m, sizeof(m)-1, \"i%c86\", family+'0');\n+\t\t\tbreak;\n+\t\tcase 15: /* pentium 4 */\n+\t\t\tstrcpy(m, \"i686\");\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t_snprintf(m, sizeof(m)-1, \"x86 Family %d\", family);\n+\t\t\tbreak;\n+\t}\n+\treturn m;\n+}\n+\n+static char *get_machine(void)\n+{\n+\tSYSTEM_INFO *s = get_system_info();\n+\tchar *arch = get_arch(s->wProcessorArchitecture);\n+\n+\tif (arch) {\n+\t\tif (strcmp(arch, \"INTEL\") == 0)\n+\t\t\treturn get_x86_machine(s->wProcessorLevel);\n+\t\treturn arch;\n+\t}\n+\treturn \"unknown\";\n+}\n+\n+static char cpuinfo_key[] =\n+\t\"HARDWARE\\\\DESCRIPTION\\\\System\\\\CentralProcessor\\\\0\";\n+\n+static char *get_processor_name(void)\n+{\n+\tstatic char cpu[256];\n+\tDWORD t, len = sizeof(cpu)-1;\n+\tLONG l;\n+\tHKEY key;\n+\n+\tl = RegOpenKeyA(HKEY_LOCAL_MACHINE, cpuinfo_key, &key);\n+\tif (l != ERROR_SUCCESS)\n+\t\treturn NULL;\n+\n+\tl = RegQueryValueExA(key, \"ProcessorNameString\", NULL, &t, cpu, &len);\n+\tif (l != ERROR_SUCCESS)\n+\t\tl = RegQueryValueExA(key, \"Identifier\", NULL, &t, cpu, &len);\n+\n+\tRegCloseKey(key);\n+\n+\tif (l != ERROR_SUCCESS)\n+\t\treturn NULL;\n+\n+\treturn cpu;\n+}\n+\n+static char *map_sysinfo_to_cpu(void)\n+{\n+\tstatic char cpu[256];\n+\tsize_t n = sizeof(cpu)-1;\n+\tSYSTEM_INFO *s = get_system_info();\n+\tchar *arch = get_arch(s->wProcessorArchitecture);\n+\n+\tif (arch && (strcmp(arch, \"INTEL\") == 0)) {\n+\t\tint family = s->wProcessorLevel;\n+\t\tint model = (((s->wProcessorRevision) >> 8) & 0xff);\n+\t\tint stepping = ((s->wProcessorRevision) & 0xff);\n+\n+\t\t_snprintf(cpu, n, \"x86 Family %d Model %d Stepping %d\",\n+\t\t\t\tfamily, model, stepping);\n+\t\treturn cpu;\n+\t}\n+\treturn NULL;\n+}\n+\n+static char *get_processor(void)\n+{\n+\tchar *cpu = get_processor_name();\n+\n+\tif (cpu || (cpu = map_sysinfo_to_cpu()))\n+\t\treturn cpu;\n+\n+\treturn \"unknown\";\n+}\n+\n+static char *get_hardware(void)\n+{\n+\treturn \"unknown\";\n+}\n+\n+static char osinfo_key[] =\n+\t\"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\";\n+\n+static char *get_product_name(void)\n+{\n+\tstatic char product[256];\n+\tDWORD type, len = sizeof(product)-1;\n+\tLONG l;\n+\tHKEY key;\n+\n+\tl = RegOpenKeyA(HKEY_LOCAL_MACHINE, osinfo_key, &key);\n+\tif (l != ERROR_SUCCESS)\n+\t\treturn NULL;\n+\n+\tl = RegQueryValueExA(key, \"ProductName\", NULL, &type, product, &len);\n+\n+\tRegCloseKey(key);\n+\n+\tif (l != ERROR_SUCCESS)\n+\t\treturn NULL;\n+\n+\treturn product;\n+}\n+\n+/*\n+ * The OS version does not provide a unique mapping to OS product name.\n+ * So, use this function only as a last resort.\n+ */\n+static char *map_version_to_os(void)\n+{\n+\tstatic struct { int version; char *os_name; } tab[] = {\n+\t\t{ 0x500, \"Windows 2000\"        },\n+\t\t{ 0x501, \"Windows XP\"          },\n+\t\t{ 0x502, \"Windows Server 2003\" }, /* also Server 2003 R2 */\n+\t\t{ 0x600, \"Windows Vista\"       }, /* also Server 2008    */\n+\t\t{ 0x601, \"Windows 7\"           }, /* also Server 2008 R2 */\n+\t\t{ 0,     NULL                  }\n+\t};\n+\tOSVERSIONINFO *v = get_version_info();\n+\tint i, version = (v->dwMajorVersion << 8) + v->dwMinorVersion;\n+\n+\tfor (i = 0; tab[i].version != 0; i++) {\n+\t\tif (version == tab[i].version)\n+\t\t\treturn tab[i].os_name;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static char *get_os(void)\n+{\n+\tchar *os_name = get_product_name();\n+\n+\tif (os_name || (os_name = map_version_to_os()))\n+\t\treturn os_name;\n+\n+\treturn \"Windows\";\n+}\n+\n+static void print_info(void)\n+{\n+\tstatic struct { int *opt; char *(*get)(void); } info[] = {\n+\t\t{ &kernel,    get_kernel    },\n+\t\t{ &host,      get_hostname  },\n+\t\t{ &release,   get_release   },\n+\t\t{ &version,   get_version   },\n+\t\t{ &machine,   get_machine   },\n+\t\t{ &processor, get_processor },\n+\t\t{ &hardware,  get_hardware  },\n+\t\t{ &os,        get_os        },\n+\t\t{ NULL,       0             }\n+\t};\n+\tchar *pad = \"\";\n+\tint i;\n+\n+\tfor (i = 0; info[i].opt; i++) {\n+\t\tif (*(info[i].opt)) {\n+\t\t\tprintf(\"%s%s\", pad, (info[i].get)());\n+\t\t\tpad = \" \";\n+\t\t}\n+\t}\n+\tprintf(\"\\n\");\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tset_program_name(argv[0], \"uname\");\n+\tprocess_args(argc,argv);\n+\tprint_info();\n+\treturn 0;\n+}\n-- \n1.6.5\n"}]}