cmd: mkdir: Handle multiple arguments.
[wine] / programs / wineconsole / wineconsole.c
1 /*
2  * an application for displaying Win32 console
3  *
4  * Copyright 2001, 2002 Eric Pouech
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 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include "wine/server.h"
27 #include "winecon_private.h"
28 #include "winnls.h"
29 #include "winuser.h"
30
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(wineconsole);
34
35 void WINECON_Fatal(const char* msg)
36 {
37     WINE_ERR("%s\n", msg);
38     ExitProcess(0);
39 }
40
41 static void printf_res(UINT uResId, ...)
42 {
43     WCHAR buffer[1024];
44     CHAR ansi[1024];
45     va_list args;
46
47     va_start(args, uResId);
48     LoadStringW(GetModuleHandleW(NULL), uResId, buffer, sizeof(buffer)/sizeof(buffer[0]));
49     WideCharToMultiByte(CP_UNIXCP, 0, buffer, -1, ansi, sizeof(ansi), NULL, NULL);
50     vprintf(ansi, args);
51     va_end(args);
52 }
53
54 static void WINECON_Usage(void)
55 {
56     printf_res(IDS_USAGE_HEADER);
57     printf_res(IDS_USAGE_BACKEND);
58     printf_res(IDS_USAGE_COMMAND);
59     printf_res(IDS_USAGE_FOOTER);
60 }
61
62 /******************************************************************
63  *              WINECON_FetchCells
64  *
65  * updates the local copy of cells (band to update)
66  */
67 static void WINECON_FetchCells(struct inner_data* data, int upd_tp, int upd_bm)
68 {
69     SERVER_START_REQ( read_console_output )
70     {
71         req->handle = wine_server_obj_handle( data->hConOut );
72         req->x      = 0;
73         req->y      = upd_tp;
74         req->mode   = CHAR_INFO_MODE_TEXTATTR;
75         req->wrap   = TRUE;
76         wine_server_set_reply( req, &data->cells[upd_tp * data->curcfg.sb_width],
77                                (upd_bm-upd_tp+1) * data->curcfg.sb_width * sizeof(CHAR_INFO) );
78         wine_server_call( req );
79     }
80     SERVER_END_REQ;
81     data->fnRefresh(data, upd_tp, upd_bm);
82 }
83
84 /******************************************************************
85  *              WINECON_ResizeWithContainer
86  *
87  * For console embedded in a container (e.g. user in a win32 window, or (n)curses
88  * in a TERM, perform resize of console (screen buffer and window) to fit in
89  * (new) container size.
90  */
91 void WINECON_ResizeWithContainer(struct inner_data* data, int width, int height)
92 {
93     struct config_data  cfg;
94
95     if (data->in_set_config) return;
96
97     cfg = data->curcfg;
98     cfg.win_width  = width;
99     cfg.win_height = height;
100
101     /* auto size screen-buffer if it's now smaller than window */
102     if (cfg.sb_width  < cfg.win_width)  cfg.sb_width = cfg.win_width;
103     if (cfg.sb_height < cfg.win_height) cfg.sb_height = cfg.win_height;
104
105     /* and reset window pos so that we don't display outside of the screen-buffer */
106     if (cfg.win_pos.X + cfg.win_width  > cfg.sb_width)  cfg.win_pos.X = cfg.sb_width  - cfg.win_width;
107     if (cfg.win_pos.Y + cfg.win_height > cfg.sb_height) cfg.win_pos.Y = cfg.sb_height - cfg.win_height;
108
109     WINECON_SetConfig(data, &cfg);
110 }
111
112 /******************************************************************
113  *              WINECON_SetHistorySize
114  *
115  *
116  */
117 static BOOL WINECON_SetHistorySize(HANDLE hConIn, int size)
118 {
119     BOOL        ret;
120
121     SERVER_START_REQ(set_console_input_info)
122     {
123         req->handle = wine_server_obj_handle( hConIn );
124         req->mask = SET_CONSOLE_INPUT_INFO_HISTORY_SIZE;
125         req->history_size = size;
126         ret = !wine_server_call_err( req );
127     }
128     SERVER_END_REQ;
129     return ret;
130 }
131
132 /******************************************************************
133  *              WINECON_SetHistoryMode
134  *
135  *
136  */
137 static BOOL WINECON_SetHistoryMode(HANDLE hConIn, int mode)
138 {
139     BOOL        ret;
140
141     SERVER_START_REQ(set_console_input_info)
142     {
143         req->handle = wine_server_obj_handle( hConIn );
144         req->mask = SET_CONSOLE_INPUT_INFO_HISTORY_MODE;
145         req->history_mode = mode;
146         ret = !wine_server_call_err( req );
147     }
148     SERVER_END_REQ;
149     return ret;
150 }
151
152 /******************************************************************
153  *              WINECON_GetConsoleTitle
154  *
155  *
156  */
157 BOOL WINECON_GetConsoleTitle(HANDLE hConIn, WCHAR* buffer, size_t len)
158 {
159     BOOL ret;
160
161     if (len < sizeof(WCHAR)) return FALSE;
162
163     SERVER_START_REQ( get_console_input_info )
164     {
165         req->handle = wine_server_obj_handle( hConIn );
166         wine_server_set_reply( req, buffer, len - sizeof(WCHAR) );
167         if ((ret = !wine_server_call_err( req )))
168         {
169             len = wine_server_reply_size( reply );
170             buffer[len / sizeof(WCHAR)] = 0;
171         }
172     }
173     SERVER_END_REQ;
174     return ret;
175 }
176
177 /******************************************************************
178  *              WINECON_SetEditionMode
179  *
180  *
181  */
182 static BOOL WINECON_SetEditionMode(HANDLE hConIn, int edition_mode)
183 {
184     BOOL ret;
185
186     SERVER_START_REQ( set_console_input_info )
187     {
188         req->handle = wine_server_obj_handle( hConIn );
189         req->mask = SET_CONSOLE_INPUT_INFO_EDITION_MODE;
190         req->edition_mode = edition_mode;
191         ret = !wine_server_call_err( req );
192     }
193     SERVER_END_REQ;
194     return ret;
195 }
196
197 /******************************************************************
198  *              WINECON_GrabChanges
199  *
200  * A change occurs, try to figure out which
201  */
202 void    WINECON_GrabChanges(struct inner_data* data)
203 {
204     struct console_renderer_event       evts[256];
205     int i, num, ev_found;
206     HANDLE h;
207
208     SERVER_START_REQ( get_console_renderer_events )
209     {
210         wine_server_set_reply( req, evts, sizeof(evts) );
211         req->handle = wine_server_obj_handle( data->hSynchro );
212         if (!wine_server_call_err( req )) num = wine_server_reply_size(reply) / sizeof(evts[0]);
213         else num = 0;
214     }
215     SERVER_END_REQ;
216     if (!num) {WINE_WARN("hmm renderer signaled but no events available\n"); return;}
217
218     /* FIXME: should do some event compression here (cursor pos, update) */
219     /* step 1: keep only last cursor pos event */
220     ev_found = -1;
221     for (i = num - 1; i >= 0; i--)
222     {
223         if (evts[i].event == CONSOLE_RENDERER_CURSOR_POS_EVENT)
224         {
225             if (ev_found != -1)
226                 evts[i].event = CONSOLE_RENDERER_NONE_EVENT;
227             ev_found = i;
228         }
229     }
230     /* step 2: manage update events */
231     ev_found = -1;
232     for (i = 0; i < num; i++)
233     {
234         if (evts[i].event == CONSOLE_RENDERER_NONE_EVENT ||
235             evts[i].event == CONSOLE_RENDERER_CURSOR_POS_EVENT ||
236             evts[i].event == CONSOLE_RENDERER_CURSOR_GEOM_EVENT) continue;
237         if (evts[i].event != CONSOLE_RENDERER_UPDATE_EVENT)
238         {
239             ev_found = -1;
240             continue;
241         }
242
243         if (ev_found != -1 &&  /* Only 2 cases where they CANNOT merge */
244             !(evts[i       ].u.update.bottom + 1 < evts[ev_found].u.update.top ||
245               evts[ev_found].u.update.bottom + 1 < evts[i       ].u.update.top))
246         {
247             evts[i].u.update.top    = min(evts[i       ].u.update.top,
248                                           evts[ev_found].u.update.top);
249             evts[i].u.update.bottom = max(evts[i       ].u.update.bottom,
250                                           evts[ev_found].u.update.bottom);
251             evts[ev_found].event = CONSOLE_RENDERER_NONE_EVENT;
252         }
253         ev_found = i;
254     }
255
256     WINE_TRACE("Events:");
257     for (i = 0; i < num; i++)
258     {
259         switch (evts[i].event)
260         {
261         case CONSOLE_RENDERER_NONE_EVENT:
262             WINE_TRACE(" NOP");
263             break;
264         case CONSOLE_RENDERER_TITLE_EVENT:
265             WINE_TRACE(" title()");
266             data->fnSetTitle(data);
267             break;
268         case CONSOLE_RENDERER_ACTIVE_SB_EVENT:
269             SERVER_START_REQ( open_console )
270             {
271                 req->from       = wine_server_obj_handle( data->hConIn );
272                 req->access     = GENERIC_READ | GENERIC_WRITE;
273                 req->attributes = 0;
274                 req->share      = FILE_SHARE_READ | FILE_SHARE_WRITE;
275                 h = wine_server_call_err( req ) ? 0 : wine_server_ptr_handle(reply->handle);
276             }
277             SERVER_END_REQ;
278             WINE_TRACE(" active(%p)", h);
279             if (h)
280             {
281                 CloseHandle(data->hConOut);
282                 data->hConOut = h;
283             }
284             break;
285         case CONSOLE_RENDERER_SB_RESIZE_EVENT:
286             if (data->curcfg.sb_width != evts[i].u.resize.width ||
287                 data->curcfg.sb_height != evts[i].u.resize.height)
288             {
289                 WINE_TRACE(" resize(%d,%d)", evts[i].u.resize.width, evts[i].u.resize.height);
290                 data->curcfg.sb_width  = evts[i].u.resize.width;
291                 data->curcfg.sb_height = evts[i].u.resize.height;
292
293                 data->cells = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, data->cells,
294                                           data->curcfg.sb_width * data->curcfg.sb_height * sizeof(CHAR_INFO));
295
296                 if (!data->cells) WINECON_Fatal("OOM\n");
297                 data->fnResizeScreenBuffer(data);
298                 data->fnComputePositions(data);
299             }
300             break;
301         case CONSOLE_RENDERER_UPDATE_EVENT:
302             WINE_TRACE(" update(%d,%d)", evts[i].u.update.top, evts[i].u.update.bottom);
303             WINECON_FetchCells(data, evts[i].u.update.top, evts[i].u.update.bottom);
304             break;
305         case CONSOLE_RENDERER_CURSOR_POS_EVENT:
306             if (evts[i].u.cursor_pos.x != data->cursor.X || evts[i].u.cursor_pos.y != data->cursor.Y)
307             {
308                 WINE_TRACE(" curs-pos(%d,%d)",evts[i].u.cursor_pos.x, evts[i].u.cursor_pos.y);
309                 data->cursor.X = evts[i].u.cursor_pos.x;
310                 data->cursor.Y = evts[i].u.cursor_pos.y;
311                 data->fnPosCursor(data);
312             }
313             break;
314         case CONSOLE_RENDERER_CURSOR_GEOM_EVENT:
315             if (evts[i].u.cursor_geom.size != data->curcfg.cursor_size ||
316                 evts[i].u.cursor_geom.visible != data->curcfg.cursor_visible)
317             {
318                 WINE_TRACE(" curs-geom(%d,%d)",
319                            evts[i].u.cursor_geom.size, evts[i].u.cursor_geom.visible);
320                 data->fnShapeCursor(data, evts[i].u.cursor_geom.size,
321                                     evts[i].u.cursor_geom.visible, FALSE);
322             }
323             break;
324         case CONSOLE_RENDERER_DISPLAY_EVENT:
325             if (evts[i].u.display.left != data->curcfg.win_pos.X)
326             {
327                 WINE_TRACE(" h-scroll(%d)", evts[i].u.display.left);
328                 data->fnScroll(data, evts[i].u.display.left, TRUE);
329                 data->fnPosCursor(data);
330             }
331             if (evts[i].u.display.top != data->curcfg.win_pos.Y)
332             {
333                 WINE_TRACE(" v-scroll(%d)", evts[i].u.display.top);
334                 data->fnScroll(data, evts[i].u.display.top, FALSE);
335                 data->fnPosCursor(data);
336             }
337             if (evts[i].u.display.width != data->curcfg.win_width ||
338                 evts[i].u.display.height != data->curcfg.win_height)
339             {
340                 WINE_TRACE(" win-size(%d,%d)", evts[i].u.display.width, evts[i].u.display.height);
341                 data->curcfg.win_width = evts[i].u.display.width;
342                 data->curcfg.win_height = evts[i].u.display.height;
343                 data->fnComputePositions(data);
344             }
345             break;
346         case CONSOLE_RENDERER_EXIT_EVENT:
347             data->dying = TRUE;
348             WINE_TRACE(". Exit!!\n");
349             return;
350         default:
351             WINE_FIXME("Unknown event type (%d)\n", evts[i].event);
352         }
353     }
354
355     WINE_TRACE(".\n");
356 }
357
358 /******************************************************************
359  *              WINECON_SetConfig
360  *
361  * Apply to data all the configuration elements from cfg. This includes modification
362  * of server side equivalent and visual parts.
363  * If force is FALSE, only the changed items are modified.
364  */
365 void     WINECON_SetConfig(struct inner_data* data, const struct config_data* cfg)
366 {
367     if (data->in_set_config) return;
368     data->in_set_config = TRUE;
369     if (data->curcfg.cursor_size != cfg->cursor_size ||
370         data->curcfg.cursor_visible != cfg->cursor_visible)
371     {
372         CONSOLE_CURSOR_INFO cinfo;
373         cinfo.dwSize = cfg->cursor_size;
374         /* <FIXME>: this hack is needed to pass thru the invariant test operation on server side
375          * (no notification is sent when invariant operation is requested
376          */
377         cinfo.bVisible = !cfg->cursor_visible;
378         SetConsoleCursorInfo(data->hConOut, &cinfo);
379         /* </FIXME> */
380         cinfo.bVisible = cfg->cursor_visible;
381         /* this shall update (through notif) curcfg */
382         SetConsoleCursorInfo(data->hConOut, &cinfo);
383     }
384     if (data->curcfg.history_size != cfg->history_size)
385     {
386         data->curcfg.history_size = cfg->history_size;
387         WINECON_SetHistorySize(data->hConIn, cfg->history_size);
388     }
389     if (data->curcfg.history_nodup != cfg->history_nodup)
390     {
391         data->curcfg.history_nodup = cfg->history_nodup;
392         WINECON_SetHistoryMode(data->hConIn, cfg->history_nodup);
393     }
394     data->curcfg.menu_mask = cfg->menu_mask;
395     data->curcfg.quick_edit = cfg->quick_edit;
396     if (1 /* FIXME: font info has changed */)
397     {
398         data->fnSetFont(data, cfg->face_name, cfg->cell_height, cfg->font_weight);
399     }
400     if (data->curcfg.def_attr != cfg->def_attr)
401     {
402         data->curcfg.def_attr = cfg->def_attr;
403         SetConsoleTextAttribute(data->hConOut, cfg->def_attr);
404     }
405     /* now let's look at the window / sb size changes...
406      * since the server checks that sb is always bigger than window, 
407      * we have to take care of doing the operations in the right order
408      */
409     /* a set of macros to make things easier to read 
410      * The Test<A><B> macros test if the <A> (width/height) needs to be changed 
411      * for <B> (window / ScreenBuffer) 
412      * The Change<A><B> actually modify the <B> dimension of <A>.
413      */
414 #define TstSBfWidth()   (data->curcfg.sb_width != cfg->sb_width)
415 #define TstWinHPos()    (data->curcfg.win_width != cfg->win_width || data->curcfg.win_pos.X != cfg->win_pos.X)
416
417 #define ChgSBfWidth()   do {c.X = cfg->sb_width; \
418                             c.Y = data->curcfg.sb_height;\
419                             SetConsoleScreenBufferSize(data->hConOut, c);\
420                         } while (0)
421 #define ChgWinHPos()    do {pos.Left = cfg->win_pos.X - data->curcfg.win_pos.X; \
422                             pos.Top = 0; \
423                             pos.Right = pos.Left + cfg->win_width - data->curcfg.win_width; \
424                             pos.Bottom = 0; \
425                             SetConsoleWindowInfo(data->hConOut, FALSE, &pos);\
426                         } while (0)
427 #define TstSBfHeight()  (data->curcfg.sb_height != cfg->sb_height)
428 #define TstWinVPos()    (data->curcfg.win_height != cfg->win_height || data->curcfg.win_pos.Y != cfg->win_pos.Y)
429
430 /* since we're going to apply height after width is done, we use width as defined 
431  * in cfg, and not in data->curcfg because if won't be updated yet */
432 #define ChgSBfHeight()  do {c.X = cfg->sb_width; c.Y = cfg->sb_height; \
433                             SetConsoleScreenBufferSize(data->hConOut, c); \
434                         } while (0)
435 #define ChgWinVPos()    do {pos.Left = 0; \
436                             pos.Top = cfg->win_pos.Y - data->curcfg.win_pos.Y; \
437                             pos.Right = 0; \
438                             pos.Bottom = pos.Top + cfg->win_height - data->curcfg.win_height; \
439                             SetConsoleWindowInfo(data->hConOut, FALSE, &pos);\
440                         } while (0)
441
442     do
443     {
444         COORD       c;
445         SMALL_RECT  pos;
446
447         if (TstSBfWidth())            
448         {
449             if (TstWinHPos())
450             {
451                 /* we're changing both at the same time, do it in the right order */
452                 if (cfg->sb_width >= data->curcfg.win_width)
453                 {
454                     ChgSBfWidth(); ChgWinHPos();
455                 }
456                 else
457                 {
458                     ChgWinHPos(); ChgSBfWidth();
459                 }
460             }
461             else ChgSBfWidth();
462         }
463         else if (TstWinHPos()) ChgWinHPos();
464         if (TstSBfHeight())
465         {
466             if (TstWinVPos())
467             {
468                 if (cfg->sb_height >= data->curcfg.win_height)
469                 {
470                     ChgSBfHeight(); ChgWinVPos();
471                 }
472                 else
473                 {
474                     ChgWinVPos(); ChgSBfHeight();
475                 }
476             }
477             else ChgSBfHeight();
478         }
479         else if (TstWinVPos()) ChgWinVPos();
480     } while (0);
481 #undef TstSBfWidth
482 #undef TstWinHPos
483 #undef ChgSBfWidth
484 #undef ChgWinHPos
485 #undef TstSBfHeight
486 #undef TstWinVPos
487 #undef ChgSBfHeight
488 #undef ChgWinVPos
489
490     data->curcfg.exit_on_die = cfg->exit_on_die;
491     if (data->curcfg.edition_mode != cfg->edition_mode)
492     {
493         data->curcfg.edition_mode = cfg->edition_mode;
494         WINECON_SetEditionMode(data->hConIn, cfg->edition_mode);
495     }
496     /* we now need to gather all events we got from the operations above,
497      * in order to get data correctly updated
498      */
499     WINECON_GrabChanges(data);
500     data->in_set_config = FALSE;
501 }
502
503 /******************************************************************
504  *              WINECON_Delete
505  *
506  * Destroy wineconsole internal data
507  */
508 static void WINECON_Delete(struct inner_data* data)
509 {
510     if (!data) return;
511
512     if (data->fnDeleteBackend)  data->fnDeleteBackend(data);
513     if (data->hConIn)           CloseHandle(data->hConIn);
514     if (data->hConOut)          CloseHandle(data->hConOut);
515     if (data->hSynchro)         CloseHandle(data->hSynchro);
516     HeapFree(GetProcessHeap(), 0, data->cells);
517     HeapFree(GetProcessHeap(), 0, data);
518 }
519
520 /******************************************************************
521  *              WINECON_GetServerConfig
522  *
523  * Fills data->curcfg with the actual configuration running in the server
524  * (getting real information on the server, and not relying on cached 
525  * information in data)
526  */
527 static BOOL WINECON_GetServerConfig(struct inner_data* data)
528 {
529     BOOL        ret;
530
531     SERVER_START_REQ(get_console_input_info)
532     {
533         req->handle = wine_server_obj_handle( data->hConIn );
534         ret = !wine_server_call_err( req );
535         data->curcfg.history_size = reply->history_size;
536         data->curcfg.history_nodup = reply->history_mode;
537         data->curcfg.edition_mode = reply->edition_mode;
538     }
539     SERVER_END_REQ;
540     if (!ret) return FALSE;
541     SERVER_START_REQ(get_console_output_info)
542     {
543         req->handle = wine_server_obj_handle( data->hConOut );
544         ret = !wine_server_call_err( req );
545         data->curcfg.cursor_size = reply->cursor_size;
546         data->curcfg.cursor_visible = reply->cursor_visible;
547         data->curcfg.def_attr = reply->attr;
548         data->curcfg.sb_width = reply->width;
549         data->curcfg.sb_height = reply->height;
550         data->curcfg.win_width = reply->win_right - reply->win_left + 1;
551         data->curcfg.win_height = reply->win_bottom - reply->win_top + 1;
552     }
553     SERVER_END_REQ;
554     WINECON_DumpConfig("first cfg: ", &data->curcfg);
555
556     return ret;
557 }
558
559 /******************************************************************
560  *              WINECON_Init
561  *
562  * Initialisation part I. Creation of server object (console input and
563  * active screen buffer)
564  */
565 static struct inner_data* WINECON_Init(HINSTANCE hInst, DWORD pid, LPCWSTR appname,
566                                        enum init_return (*backend)(struct inner_data*),
567                                        INT nCmdShow)
568 {
569     struct inner_data*  data = NULL;
570     DWORD               ret;
571     struct config_data  cfg;
572     STARTUPINFOW        si;
573
574     data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data));
575     if (!data) return 0;
576
577     GetStartupInfoW(&si);
578
579     if (pid == 0)
580     {
581         if (!si.lpTitle) WINECON_Fatal("Should have a title set");
582         appname = si.lpTitle;
583     }
584
585     data->nCmdShow = nCmdShow;
586     /* load settings */
587     WINECON_RegLoad(appname, &cfg);
588
589     /* some overrides */
590     if (pid == 0)
591     {
592         if (si.dwFlags & STARTF_USECOUNTCHARS)
593         {
594             cfg.sb_width  = si.dwXCountChars;
595             cfg.sb_height = si.dwYCountChars;
596         }
597         if (si.dwFlags & STARTF_USEFILLATTRIBUTE)
598             cfg.def_attr = si.dwFillAttribute;
599         /* should always be defined */
600     }
601
602     /* the handles here are created without the whistles and bells required by console
603      * (mainly because wineconsole doesn't need it)
604      * - they are not inheritable
605      * - hConIn is not synchronizable
606      */
607     SERVER_START_REQ(alloc_console)
608     {
609         req->access     = GENERIC_READ | GENERIC_WRITE;
610         req->attributes = 0;
611         req->pid        = pid;
612         req->input_fd   = -1;
613
614         ret = !wine_server_call_err( req );
615         data->hConIn = wine_server_ptr_handle( reply->handle_in );
616         data->hSynchro = wine_server_ptr_handle( reply->event );
617     }
618     SERVER_END_REQ;
619     if (!ret) goto error;
620     WINE_TRACE("using hConIn %p, hSynchro event %p\n", data->hConIn, data->hSynchro);
621
622     SERVER_START_REQ(create_console_output)
623     {
624         req->handle_in  = wine_server_obj_handle( data->hConIn );
625         req->access     = GENERIC_WRITE|GENERIC_READ;
626         req->attributes = 0;
627         req->share      = FILE_SHARE_READ|FILE_SHARE_WRITE;
628         req->fd         = -1;
629         ret = !wine_server_call_err( req );
630         data->hConOut   = wine_server_ptr_handle( reply->handle_out );
631     }
632     SERVER_END_REQ;
633     if (!ret) goto error;
634     WINE_TRACE("using hConOut %p\n", data->hConOut);
635
636     /* filling data->curcfg from cfg */
637     switch ((*backend)(data))
638     {
639     case init_not_supported:
640         if (backend == WCCURSES_InitBackend)
641         {
642             if (WCUSER_InitBackend( data ) != init_success) break;
643         }
644         else if (backend == WCUSER_InitBackend)
645         {
646             if (WCCURSES_InitBackend( data ) != init_success) break;
647         }
648         /* fall through */
649     case init_success:
650         WINECON_GetServerConfig(data);
651         data->cells = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
652                                 data->curcfg.sb_width * data->curcfg.sb_height * sizeof(CHAR_INFO));
653         if (!data->cells) WINECON_Fatal("OOM\n");
654         data->fnResizeScreenBuffer(data);
655         data->fnComputePositions(data);
656         WINECON_SetConfig(data, &cfg);
657         data->curcfg.registry = cfg.registry;
658         WINECON_DumpConfig("fint", &data->curcfg);
659         SERVER_START_REQ( set_console_input_info )
660         {
661             req->handle = wine_server_obj_handle( data->hConIn );
662             req->win = wine_server_user_handle( data->hWnd );
663             req->mask = SET_CONSOLE_INPUT_INFO_TITLE |
664                         SET_CONSOLE_INPUT_INFO_WIN;
665             wine_server_add_data( req, appname, lstrlenW(appname) * sizeof(WCHAR) );
666             ret = !wine_server_call_err( req );
667         }
668         SERVER_END_REQ;
669         if (!ret) goto error;
670
671         return data;
672     case init_failed:
673         break;
674     }
675
676  error:
677     WINE_ERR("failed to init.\n");
678
679     WINECON_Delete(data);
680     return NULL;
681 }
682
683 /******************************************************************
684  *              WINECON_Spawn
685  *
686  * Spawn the child process when invoked with wineconsole foo bar
687  */
688 static BOOL WINECON_Spawn(struct inner_data* data, LPWSTR cmdLine)
689 {
690     PROCESS_INFORMATION info;
691     STARTUPINFOW        startup;
692     BOOL                done;
693
694     /* we're in the case wineconsole <exe> <options>... spawn the new process */
695     memset(&startup, 0, sizeof(startup));
696     startup.cb          = sizeof(startup);
697     startup.dwFlags     = STARTF_USESTDHANDLES;
698
699     /* the attributes of wineconsole's handles are not adequate for inheritance, so
700      * get them with the correct attributes before process creation
701      */
702     if (!DuplicateHandle(GetCurrentProcess(), data->hConIn,  GetCurrentProcess(),
703                          &startup.hStdInput, GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE, TRUE, 0) ||
704         !DuplicateHandle(GetCurrentProcess(), data->hConOut, GetCurrentProcess(),
705                          &startup.hStdOutput, GENERIC_READ|GENERIC_WRITE, TRUE, 0) ||
706         !DuplicateHandle(GetCurrentProcess(), data->hConOut, GetCurrentProcess(),
707                          &startup.hStdError, GENERIC_READ|GENERIC_WRITE, TRUE, 0))
708     {
709         WINE_ERR("Can't dup handles\n");
710         /* no need to delete handles, we're exiting the program anyway */
711         return FALSE;
712     }
713
714     done = CreateProcessW(NULL, cmdLine, NULL, NULL, TRUE, 0L, NULL, NULL, &startup, &info);
715
716     /* we no longer need the handles passed to the child for the console */
717     CloseHandle(startup.hStdInput);
718     CloseHandle(startup.hStdOutput);
719     CloseHandle(startup.hStdError);
720
721     CloseHandle(info.hProcess);
722     CloseHandle(info.hThread);
723
724     return done;
725 }
726
727 struct wc_init {
728     LPCSTR              ptr;
729     enum {from_event, from_process_name} mode;
730     enum init_return    (*backend)(struct inner_data*);
731     HANDLE              event;
732 };
733
734 #define WINECON_CMD_SHOW_USAGE 0x10000
735
736 /******************************************************************
737  *               WINECON_ParseOptions
738  *
739  * RETURNS
740  *   On success: 0
741  *   On error:   error string id optionally with the CMD_SHOW_USAGE flag
742  */
743 static UINT WINECON_ParseOptions(const char* lpCmdLine, struct wc_init* wci)
744 {
745     memset(wci, 0, sizeof(*wci));
746     wci->ptr = lpCmdLine;
747     wci->mode = from_process_name;
748     wci->backend = WCUSER_InitBackend;
749
750     for (;;)
751     {
752         while (*wci->ptr == ' ' || *wci->ptr == '\t') wci->ptr++;
753         if (wci->ptr[0] != '-') break;
754         if (strncmp(wci->ptr, "--use-event=", 12) == 0)
755         {
756             char*           end;
757             wci->event = (HANDLE)strtol(wci->ptr + 12, &end, 10);
758             if (end == wci->ptr + 12) return IDS_CMD_INVALID_EVENT_ID;
759             wci->mode = from_event;
760             wci->ptr = end;
761         }
762         else if (strncmp(wci->ptr, "--backend=", 10) == 0)
763         {
764             if (strncmp(wci->ptr + 10, "user", 4) == 0)
765             {
766                 wci->backend = WCUSER_InitBackend;
767                 wci->ptr += 14;
768             }
769             else if (strncmp(wci->ptr + 10, "curses", 6) == 0)
770             {
771                 wci->backend = WCCURSES_InitBackend;
772                 wci->ptr += 16;
773             }
774             else
775                 return IDS_CMD_INVALID_BACKEND;
776         }
777         else
778             return IDS_CMD_INVALID_OPTION|WINECON_CMD_SHOW_USAGE;
779     }
780
781     if (wci->mode == from_event)
782         return 0;
783
784     while (*wci->ptr == ' ' || *wci->ptr == '\t') wci->ptr++;
785     if (*wci->ptr == 0)
786         return IDS_CMD_ABOUT|WINECON_CMD_SHOW_USAGE;
787
788     return 0;
789 }
790
791 /******************************************************************
792  *              WinMain
793  *
794  * wineconsole can either be started as:
795  *      wineconsole --use-event=<int>   used when a new console is created (AllocConsole)
796  *      wineconsole <pgm> <arguments>   used to start the program <pgm> from the command line in
797  *                                      a freshly created console
798  * --backend=(curses|user) can also be used to select the desired backend
799  */
800 int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmdLine, INT nCmdShow)
801 {
802     struct inner_data*  data;
803     int                 ret = 1;
804     struct wc_init      wci;
805
806     if ((ret = WINECON_ParseOptions(lpCmdLine, &wci)) != 0)
807     {
808         printf_res(ret & 0xffff);
809         if (ret & WINECON_CMD_SHOW_USAGE)
810             WINECON_Usage();
811         return 0;
812     }
813
814     switch (wci.mode)
815     {
816     case from_event:
817         /* case of wineconsole <evt>, signal process that created us that we're up and running */
818         if (!(data = WINECON_Init(hInst, 0, NULL, wci.backend, nCmdShow))) return 0;
819         ret = SetEvent(wci.event);
820         if (!ret) WINE_ERR("SetEvent failed.\n");
821         break;
822     case from_process_name:
823         {
824             WCHAR           buffer[256];
825
826             MultiByteToWideChar(CP_ACP, 0, wci.ptr, -1, buffer, sizeof(buffer) / sizeof(buffer[0]));
827
828             if (!(data = WINECON_Init(hInst, GetCurrentProcessId(), buffer, wci.backend, nCmdShow)))
829                 return 0;
830             ret = WINECON_Spawn(data, buffer);
831             if (!ret)
832             {
833                 WINECON_Delete(data);
834                 printf_res(IDS_CMD_LAUNCH_FAILED, wine_dbgstr_a(wci.ptr));
835                 return 0;
836             }
837         }
838         break;
839     default:
840         return 0;
841     }
842
843     if (ret)
844     {
845         WINE_TRACE("calling MainLoop.\n");
846         ret = data->fnMainLoop(data);
847     }
848
849     WINECON_Delete(data);
850
851     return ret;
852 }