dxdiag: Introduce the file output infrastructure.
[wine] / programs / dxdiag / main.c
1 /*
2  * DxDiag Implementation
3  *
4  * Copyright 2009 Austin English
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define WIN32_LEAN_AND_MEAN
22 #include <windows.h>
23 #include "wine/debug.h"
24 #include "wine/unicode.h"
25
26 #include "dxdiag_private.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(dxdiag);
29
30 struct command_line_info
31 {
32     WCHAR outfile[MAX_PATH];
33     enum output_type output_type;
34     BOOL whql_check;
35 };
36
37 static void usage(void)
38 {
39     WINE_FIXME("Usage message box is not implemented\n");
40     ExitProcess(0);
41 }
42
43 static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type, WCHAR *filename, size_t filename_len)
44 {
45     const WCHAR *endptr;
46     size_t len;
47
48     /* Skip any intervening spaces. */
49     while (*cmdline == ' ')
50         cmdline++;
51
52     /* Ignore filename quoting, if any. */
53     if (*cmdline == '"' && (endptr = strrchrW(cmdline, '"')))
54     {
55         /* Reject a string with only one quote. */
56         if (cmdline == endptr)
57             return FALSE;
58
59         cmdline++;
60     }
61     else
62         endptr = cmdline + strlenW(cmdline);
63
64     len = endptr - cmdline;
65     if (len == 0 || len >= filename_len)
66         return FALSE;
67
68     memcpy(filename, cmdline, len * sizeof(WCHAR));
69     filename[len] = '\0';
70
71     /* Append an extension appropriate for the output type if the filename does not have one. */
72     if (!(endptr = strrchrW(filename, '.')))
73     {
74         const WCHAR *filename_ext = get_output_extension(output_type);
75
76         if (len + strlenW(filename_ext) >= filename_len)
77             return FALSE;
78
79         strcatW(filename, filename_ext);
80     }
81
82     return TRUE;
83 }
84
85 /*
86     Process options [/WHQL:ON|OFF][/X outfile|/T outfile]
87     Returns TRUE if options were present, FALSE otherwise
88     Only one of /X and /T is allowed, /WHQL must come before /X and /T,
89     and the rest of the command line after /X or /T is interpreted as a
90     filename. If a non-option portion of the command line is encountered,
91     dxdiag assumes that the string is a filename for the /T option.
92
93     Native does not interpret quotes, but quotes are parsed here because of how
94     Wine handles the command line.
95 */
96
97 static BOOL process_command_line(const WCHAR *cmdline, struct command_line_info *info)
98 {
99     static const WCHAR whql_colonW[] = {'w','h','q','l',':',0};
100     static const WCHAR offW[] = {'o','f','f',0};
101     static const WCHAR onW[] = {'o','n',0};
102
103     info->whql_check = FALSE;
104     info->output_type = OUTPUT_NONE;
105
106     while (*cmdline)
107     {
108         /* Skip whitespace before arg */
109         while (*cmdline == ' ')
110             cmdline++;
111
112         /* If no option is specified, treat the command line as a filename. */
113         if (*cmdline != '-' && *cmdline != '/')
114         {
115             info->output_type = OUTPUT_TEXT;
116             return process_file_name(cmdline, OUTPUT_TEXT, info->outfile,
117                                      sizeof(info->outfile)/sizeof(WCHAR));
118         }
119
120         cmdline++;
121
122         switch (*cmdline)
123         {
124         case 'T':
125         case 't':
126             info->output_type = OUTPUT_TEXT;
127             return process_file_name(cmdline + 1, OUTPUT_TEXT, info->outfile,
128                                      sizeof(info->outfile)/sizeof(WCHAR));
129         case 'X':
130         case 'x':
131             info->output_type = OUTPUT_XML;
132             return process_file_name(cmdline + 1, OUTPUT_XML, info->outfile,
133                                      sizeof(info->outfile)/sizeof(WCHAR));
134         case 'W':
135         case 'w':
136             if (strncmpiW(cmdline, whql_colonW, 5))
137                 return FALSE;
138
139             cmdline += 5;
140
141             if (!strncmpiW(cmdline, offW, 3))
142             {
143                 info->whql_check = FALSE;
144                 cmdline += 2;
145             }
146             else if (!strncmpiW(cmdline, onW, 2))
147             {
148                 info->whql_check = TRUE;
149                 cmdline++;
150             }
151             else
152                 return FALSE;
153
154             break;
155         default:
156             return FALSE;
157         }
158
159         cmdline++;
160     }
161
162     return TRUE;
163 }
164
165 int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cmdshow)
166 {
167     struct command_line_info info;
168
169     if (!process_command_line(cmdline, &info))
170         usage();
171
172     WINE_TRACE("WHQL check: %s\n", info.whql_check ? "TRUE" : "FALSE");
173     WINE_TRACE("Output type: %d\n", info.output_type);
174     if (info.output_type != OUTPUT_NONE)
175         WINE_TRACE("Output filename: %s\n", debugstr_output_type(info.output_type));
176
177     if (info.output_type != OUTPUT_NONE)
178         output_dxdiag_information(info.outfile, info.output_type);
179     else
180         WINE_FIXME("Information dialog is not implemented\n");
181
182     return 0;
183 }