src/main/win32/SDL_win32_main.c
author Sam Lantinga
Mon, 12 Jul 2010 22:08:50 -0700
changeset 4511 ae7799d16c87
parent 2286 41a6675d8700
permissions -rw-r--r--
Daniel Wyatt 2010-03-26 14:52:53 PDT

If a non-console Windows SDL program has a non-quoted 0th argument followed
optionally by more non-quoted arguments and then by an empty quoted argument,
it will crash (attempts to dereference a NULL pointer).

In other words, something like this:
test.exe [non-quoted args] "" [...]

The fix is a one-liner in ParseCommandLine() of
src/main/win32/SDL_win32_main.c.

You can test this with any non-console SDL program on windows like this:
1) Open a console (cmd.exe)
2) Launch the program in one of the following ways:
program ""
program arg1 ""
program arg1 "" arg3

These will not cause a crash:
"program" [...]
program "arg1" ""

When a Windows program is launched from Explorer, its 0th argument seems to
always be quoted, so it won't be a problem in that case.

I've tested this on Windows XP SP3 and Windows 7.
     1 /*
     2     SDL_main.c, placed in the public domain by Sam Lantinga  4/13/98
     3 
     4     The WinMain function -- calls your program's main() function
     5 */
     6 
     7 #include <stdio.h>
     8 #include <stdlib.h>
     9 
    10 #define WIN32_LEAN_AND_MEAN
    11 #include <windows.h>
    12 
    13 /* Include the SDL main definition header */
    14 #include "SDL.h"
    15 #include "SDL_main.h"
    16 
    17 #ifdef main
    18 # ifndef _WIN32_WCE_EMULATION
    19 #  undef main
    20 # endif /* _WIN32_WCE_EMULATION */
    21 #endif /* main */
    22 
    23 #if defined(_WIN32_WCE) && _WIN32_WCE < 300
    24 /* seems to be undefined in Win CE although in online help */
    25 #define isspace(a) (((CHAR)a == ' ') || ((CHAR)a == '\t'))
    26 #endif /* _WIN32_WCE < 300 */
    27 
    28 static void
    29 UnEscapeQuotes(char *arg)
    30 {
    31     char *last = NULL;
    32 
    33     while (*arg) {
    34         if (*arg == '"' && *last == '\\') {
    35             char *c_curr = arg;
    36             char *c_last = last;
    37 
    38             while (*c_curr) {
    39                 *c_last = *c_curr;
    40                 c_last = c_curr;
    41                 c_curr++;
    42             }
    43             *c_last = '\0';
    44         }
    45         last = arg;
    46         arg++;
    47     }
    48 }
    49 
    50 /* Parse a command line buffer into arguments */
    51 static int
    52 ParseCommandLine(char *cmdline, char **argv)
    53 {
    54     char *bufp;
    55     char *lastp = NULL;
    56     int argc, last_argc;
    57 
    58     argc = last_argc = 0;
    59     for (bufp = cmdline; *bufp;) {
    60         /* Skip leading whitespace */
    61         while (isspace(*bufp)) {
    62             ++bufp;
    63         }
    64         /* Skip over argument */
    65         if (*bufp == '"') {
    66             ++bufp;
    67             if (*bufp) {
    68                 if (argv) {
    69                     argv[argc] = bufp;
    70                 }
    71                 ++argc;
    72             }
    73             /* Skip over word */
    74             lastp = bufp;
    75             while (*bufp && (*bufp != '"' || *lastp == '\\')) {
    76                 lastp = bufp;
    77                 ++bufp;
    78             }
    79         } else {
    80             if (*bufp) {
    81                 if (argv) {
    82                     argv[argc] = bufp;
    83                 }
    84                 ++argc;
    85             }
    86             /* Skip over word */
    87             while (*bufp && !isspace(*bufp)) {
    88                 ++bufp;
    89             }
    90         }
    91         if (*bufp) {
    92             if (argv) {
    93                 *bufp = '\0';
    94             }
    95             ++bufp;
    96         }
    97 
    98         /* Strip out \ from \" sequences */
    99         if (argv && last_argc != argc) {
   100             UnEscapeQuotes(argv[last_argc]);
   101         }
   102         last_argc = argc;
   103     }
   104     if (argv) {
   105         argv[argc] = NULL;
   106     }
   107     return (argc);
   108 }
   109 
   110 /* Show an error message */
   111 static void
   112 ShowError(const char *title, const char *message)
   113 {
   114 /* If USE_MESSAGEBOX is defined, you need to link with user32.lib */
   115 #ifdef USE_MESSAGEBOX
   116     MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK);
   117 #else
   118     fprintf(stderr, "%s: %s\n", title, message);
   119 #endif
   120 }
   121 
   122 /* Pop up an out of memory message, returns to Windows */
   123 static BOOL
   124 OutOfMemory(void)
   125 {
   126     ShowError("Fatal Error", "Out of memory - aborting");
   127     return FALSE;
   128 }
   129 
   130 #if defined(_MSC_VER) && !defined(_WIN32_WCE)
   131 /* The VC++ compiler needs main defined */
   132 #define console_main main
   133 #endif
   134 
   135 /* This is where execution begins [console apps] */
   136 int
   137 console_main(int argc, char *argv[])
   138 {
   139     int status;
   140 
   141     /* Run the application main() code */
   142     status = SDL_main(argc, argv);
   143 
   144     /* Exit cleanly, calling atexit() functions */
   145     exit(status);
   146 
   147     /* Hush little compiler, don't you cry... */
   148     return 0;
   149 }
   150 
   151 /* This is where execution begins [windowed apps] */
   152 int WINAPI
   153 WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPTSTR szCmdLine, int sw)
   154 {
   155     char **argv;
   156     int argc;
   157     char *cmdline;
   158 #ifdef _WIN32_WCE
   159     wchar_t *bufp;
   160     int nLen;
   161 #else
   162     char *bufp;
   163     size_t nLen;
   164 #endif
   165 
   166 #ifdef _WIN32_WCE
   167     nLen = wcslen(szCmdLine) + 128 + 1;
   168     bufp = SDL_stack_alloc(wchar_t, nLen * 2);
   169     wcscpy(bufp, TEXT("\""));
   170     GetModuleFileName(NULL, bufp + 1, 128 - 3);
   171     wcscpy(bufp + wcslen(bufp), TEXT("\" "));
   172     wcsncpy(bufp + wcslen(bufp), szCmdLine, nLen - wcslen(bufp));
   173     nLen = wcslen(bufp) + 1;
   174     cmdline = SDL_stack_alloc(char, nLen);
   175     if (cmdline == NULL) {
   176         return OutOfMemory();
   177     }
   178     WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL);
   179 #else
   180     /* Grab the command line */
   181     bufp = GetCommandLine();
   182     nLen = SDL_strlen(bufp) + 1;
   183     cmdline = SDL_stack_alloc(char, nLen);
   184     if (cmdline == NULL) {
   185         return OutOfMemory();
   186     }
   187     SDL_strlcpy(cmdline, bufp, nLen);
   188 #endif
   189 
   190     /* Parse it into argv and argc */
   191     argc = ParseCommandLine(cmdline, NULL);
   192     argv = SDL_stack_alloc(char *, argc + 1);
   193     if (argv == NULL) {
   194         return OutOfMemory();
   195     }
   196     ParseCommandLine(cmdline, argv);
   197 
   198     /* Run the main program */
   199     console_main(argc, argv);
   200 
   201     /* Hush little compiler, don't you cry... */
   202     return 0;
   203 }
   204 
   205 /* vi: set ts=4 sw=4 expandtab: */