wscript.exe: Improved argument parsing.
[wine] / programs / wscript / main.c
1 /*
2  * Copyright 2010 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #define COBJMACROS
22
23 #include <windef.h>
24 #include <winbase.h>
25 #include <winreg.h>
26 #include <ole2.h>
27 #include <activscp.h>
28 #include <initguid.h>
29
30 #include "wscript.h"
31
32 #include <wine/debug.h>
33 #include <wine/unicode.h>
34
35 WINE_DEFAULT_DEBUG_CHANNEL(wscript);
36
37 static const WCHAR wscriptW[] = {'W','S','c','r','i','p','t',0};
38 static const WCHAR wshW[] = {'W','S','H',0};
39
40 ITypeInfo *host_ti;
41
42 static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface,
43                                                       REFIID riid, void **ppv)
44 {
45     if(IsEqualGUID(riid, &IID_IUnknown)) {
46         WINE_TRACE("(IID_IUnknown %p)\n", ppv);
47         *ppv = iface;
48     }else if(IsEqualGUID(riid, &IID_IActiveScriptSite)) {
49         WINE_TRACE("(IID_IActiveScriptSite %p)\n", ppv);
50         *ppv = iface;
51     }else {
52         *ppv = NULL;
53         WINE_TRACE("(%s %p)\n", wine_dbgstr_guid(riid), ppv);
54         return E_NOINTERFACE;
55     }
56
57     IUnknown_AddRef((IUnknown*)*ppv);
58     return S_OK;
59 }
60
61 static ULONG WINAPI ActiveScriptSite_AddRef(IActiveScriptSite *iface)
62 {
63     return 2;
64 }
65
66 static ULONG WINAPI ActiveScriptSite_Release(IActiveScriptSite *iface)
67 {
68     return 1;
69 }
70
71 static HRESULT WINAPI ActiveScriptSite_GetLCID(IActiveScriptSite *iface, LCID *plcid)
72 {
73     WINE_TRACE("()\n");
74
75     *plcid = GetUserDefaultLCID();
76     return S_OK;
77 }
78
79 static HRESULT WINAPI ActiveScriptSite_GetItemInfo(IActiveScriptSite *iface,
80         LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppunkItem, ITypeInfo **ppti)
81 {
82     WINE_TRACE("(%s %x %p %p)\n", wine_dbgstr_w(pstrName), dwReturnMask, ppunkItem, ppti);
83
84     if(strcmpW(pstrName, wshW) && strcmpW(pstrName, wscriptW))
85         return E_FAIL;
86
87     if(dwReturnMask & SCRIPTINFO_ITYPEINFO) {
88         ITypeInfo_AddRef(host_ti);
89         *ppti = host_ti;
90     }
91
92     if(dwReturnMask & SCRIPTINFO_IUNKNOWN) {
93         IHost_AddRef(&host_obj);
94         *ppunkItem = (IUnknown*)&host_obj;
95     }
96
97     return S_OK;
98 }
99
100 static HRESULT WINAPI ActiveScriptSite_GetDocVersionString(IActiveScriptSite *iface,
101         BSTR *pbstrVersion)
102 {
103     WINE_FIXME("()\n");
104     return E_NOTIMPL;
105 }
106
107 static HRESULT WINAPI ActiveScriptSite_OnScriptTerminate(IActiveScriptSite *iface,
108         const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo)
109 {
110     WINE_FIXME("()\n");
111     return E_NOTIMPL;
112 }
113
114 static HRESULT WINAPI ActiveScriptSite_OnStateChange(IActiveScriptSite *iface,
115         SCRIPTSTATE ssScriptState)
116 {
117     WINE_TRACE("(%x)\n", ssScriptState);
118     return S_OK;
119 }
120
121 static HRESULT WINAPI ActiveScriptSite_OnScriptError(IActiveScriptSite *iface,
122         IActiveScriptError *pscripterror)
123 {
124     WINE_FIXME("()\n");
125     return E_NOTIMPL;
126 }
127
128 static HRESULT WINAPI ActiveScriptSite_OnEnterScript(IActiveScriptSite *iface)
129 {
130     WINE_TRACE("()\n");
131     return S_OK;
132 }
133
134 static HRESULT WINAPI ActiveScriptSite_OnLeaveScript(IActiveScriptSite *iface)
135 {
136     WINE_TRACE("()\n");
137     return S_OK;
138 }
139
140 static IActiveScriptSiteVtbl ActiveScriptSiteVtbl = {
141     ActiveScriptSite_QueryInterface,
142     ActiveScriptSite_AddRef,
143     ActiveScriptSite_Release,
144     ActiveScriptSite_GetLCID,
145     ActiveScriptSite_GetItemInfo,
146     ActiveScriptSite_GetDocVersionString,
147     ActiveScriptSite_OnScriptTerminate,
148     ActiveScriptSite_OnStateChange,
149     ActiveScriptSite_OnScriptError,
150     ActiveScriptSite_OnEnterScript,
151     ActiveScriptSite_OnLeaveScript
152 };
153
154 IActiveScriptSite script_site = { &ActiveScriptSiteVtbl };
155
156 static BOOL load_typelib(void)
157 {
158     ITypeLib *typelib;
159     HRESULT hres;
160
161     static const WCHAR wscript_exeW[] = {'w','s','c','r','i','p','t','.','e','x','e',0};
162
163     hres = LoadTypeLib(wscript_exeW, &typelib);
164     if(FAILED(hres))
165         return FALSE;
166
167     hres = ITypeLib_GetTypeInfoOfGuid(typelib, &IID_IHost, &host_ti);
168
169     ITypeLib_Release(typelib);
170     return SUCCEEDED(hres);
171 }
172
173 static BOOL get_engine_clsid(const WCHAR *ext, CLSID *clsid)
174 {
175     WCHAR fileid[64], progid[64];
176     DWORD res;
177     LONG size;
178     HKEY hkey;
179     HRESULT hres;
180
181     static const WCHAR script_engineW[] =
182         {'\\','S','c','r','i','p','t','E','n','g','i','n','e',0};
183
184     res = RegOpenKeyW(HKEY_CLASSES_ROOT, ext, &hkey);
185     if(res != ERROR_SUCCESS)
186         return FALSE;
187
188     size = sizeof(fileid)/sizeof(WCHAR);
189     res = RegQueryValueW(hkey, NULL, fileid, &size);
190     RegCloseKey(hkey);
191     if(res != ERROR_SUCCESS)
192         return FALSE;
193
194     WINE_TRACE("fileid is %s\n", wine_dbgstr_w(fileid));
195
196     strcatW(fileid, script_engineW);
197     res = RegOpenKeyW(HKEY_CLASSES_ROOT, fileid, &hkey);
198     if(res != ERROR_SUCCESS)
199         return FALSE;
200
201     size = sizeof(progid)/sizeof(WCHAR);
202     res = RegQueryValueW(hkey, NULL, progid, &size);
203     RegCloseKey(hkey);
204     if(res != ERROR_SUCCESS)
205         return FALSE;
206
207     WINE_TRACE("ProgID is %s\n", wine_dbgstr_w(progid));
208
209     hres = CLSIDFromProgID(progid, clsid);
210     return SUCCEEDED(hres);
211 }
212
213 static HRESULT create_engine(CLSID *clsid, IActiveScript **script_ret,
214         IActiveScriptParse **parser)
215 {
216     IActiveScript *script;
217     IUnknown *unk;
218     HRESULT hres;
219
220     hres = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
221             &IID_IUnknown, (void**)&unk);
222     if(FAILED(hres))
223         return FALSE;
224
225     hres = IUnknown_QueryInterface(unk, &IID_IActiveScript, (void**)&script);
226     IUnknown_Release(unk);
227     if(FAILED(hres))
228         return FALSE;
229
230     hres = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)parser);
231     if(FAILED(hres)) {
232         IActiveScript_Release(script);
233         return FALSE;
234     }
235
236     *script_ret = script;
237     return TRUE;
238 }
239
240 static HRESULT init_engine(IActiveScript *script, IActiveScriptParse *parser)
241 {
242     HRESULT hres;
243
244     if(!load_typelib())
245         return FALSE;
246
247     hres = IActiveScript_SetScriptSite(script, &script_site);
248     if(FAILED(hres))
249         return FALSE;
250
251     hres = IActiveScriptParse64_InitNew(parser);
252     if(FAILED(hres))
253         return FALSE;
254
255     hres = IActiveScript_AddNamedItem(script, wscriptW, SCRIPTITEM_ISVISIBLE);
256     if(FAILED(hres))
257         return FALSE;
258
259     hres = IActiveScript_AddNamedItem(script, wshW, SCRIPTITEM_ISVISIBLE);
260     if(FAILED(hres))
261         return FALSE;
262
263     hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_INITIALIZED);
264     return SUCCEEDED(hres);
265 }
266
267 static BSTR get_script_str(const WCHAR *filename)
268 {
269     const char *file_map;
270     HANDLE file, map;
271     DWORD size, len;
272     BSTR ret;
273
274     file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
275     if(file == INVALID_HANDLE_VALUE)
276         return NULL;
277
278     size = GetFileSize(file, NULL);
279     map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
280     CloseHandle(file);
281     if(map == INVALID_HANDLE_VALUE)
282         return NULL;
283
284     file_map = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
285     CloseHandle(map);
286     if(!file_map)
287         return NULL;
288
289     len = MultiByteToWideChar(CP_ACP, 0, file_map, size, NULL, 0);
290     ret = SysAllocStringLen(NULL, len);
291     MultiByteToWideChar(CP_ACP, 0, file_map, size, ret, len);
292
293     UnmapViewOfFile(file_map);
294     return ret;
295 }
296
297 static void run_script(const WCHAR *filename, IActiveScript *script, IActiveScriptParse *parser)
298 {
299     BSTR text;
300     HRESULT hres;
301
302     text = get_script_str(filename);
303     if(!text) {
304         WINE_FIXME("Could not get script text\n");
305         return;
306     }
307
308     hres = IActiveScriptParse64_ParseScriptText(parser, text, NULL, NULL, NULL, 1, 1,
309             SCRIPTTEXT_HOSTMANAGESSOURCE|SCRIPTITEM_ISVISIBLE, NULL, NULL);
310     SysFreeString(text);
311     if(FAILED(hres)) {
312         WINE_FIXME("ParseScriptText failed: %08x\n", hres);
313         return;
314     }
315
316     hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_STARTED);
317     if(FAILED(hres))
318         WINE_FIXME("SetScriptState failed: %08x\n", hres);
319 }
320
321 int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cmdshow)
322 {
323     const WCHAR *ext, *filename = NULL;
324     IActiveScriptParse *parser;
325     IActiveScript *script;
326     WCHAR **argv;
327     CLSID clsid;
328     int argc, i;
329
330     WINE_TRACE("(%p %p %s %x)\n", hInst, hPrevInst, wine_dbgstr_w(cmdline), cmdshow);
331
332     argv = CommandLineToArgvW(cmdline, &argc);
333     if(!argv)
334         return 1;
335
336     for(i=0; i<argc; i++) {
337         if(*argv[i] == '/' || *argv[i] == '-') {
338             WINE_FIXME("Unsupported argument %s\n", wine_dbgstr_w(argv[i]));
339         }else {
340             filename = argv[i];
341             break;
342         }
343     }
344
345     if(!filename) {
346         WINE_FIXME("No file name specified\n");
347         return 1;
348     }
349
350     ext = strchrW(filename, '.');
351     if(!ext)
352         ext = filename;
353     if(!get_engine_clsid(ext, &clsid)) {
354         WINE_FIXME("Could not find engine for %s\n", wine_dbgstr_w(ext));
355         return 1;
356     }
357
358     CoInitialize(NULL);
359
360     if(!create_engine(&clsid, &script, &parser)) {
361         WINE_FIXME("Could not create script engine\n");
362         CoUninitialize();
363         return 1;
364     }
365
366     if(init_engine(script, parser)) {
367         run_script(filename, script, parser);
368         IActiveScript_Close(script);
369         ITypeInfo_Release(host_ti);
370     }else {
371         WINE_FIXME("Script initialization failed\n");
372     }
373
374     IActiveScript_Release(script);
375     IUnknown_Release(parser);
376
377     CoUninitialize();
378
379     return 0;
380 }