1 /* Copyright (C) 2001-2012 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134, San Rafael,
13    CA  94903, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /* dwmainc.c */
18 
19 #include "windows_.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <io.h>
23 #include <fcntl.h>
24 #include <process.h>
25 #include "ierrors.h"
26 #include "iapi.h"
27 #include "vdtrace.h"
28 #include "gdevdsp.h"
29 #include "dwdll.h"
30 #include "dwimg.h"
31 #include "dwtrace.h"
32 
33 /* Patch by Rod Webster (rodw) */
34 /* Added conditional below to allow Borland Compilation (Tested on 5.5) */
35 /* It would be better to place this code in an include file but no dwmainc.h exists */
36 #ifdef __BORLANDC__
37 #define _isatty isatty
38 #define _setmode setmode
39 #endif
40 
41 GSDLL gsdll;
42 void *instance;
43 BOOL quitnow = FALSE;
44 HANDLE hthread;
45 DWORD thread_id;
46 HWND hwndforeground;    /* our best guess for our console window handle */
47 
48 char start_string[] = "systemdict /start get exec\n";
49 
50 /*********************************************************************/
51 /* stdio functions */
52 
53 static int GSDLLCALL
gsdll_stdin(void * instance,char * buf,int len)54 gsdll_stdin(void *instance, char *buf, int len)
55 {
56     return _read(fileno(stdin), buf, len);
57 }
58 
59 static int GSDLLCALL
gsdll_stdout(void * instance,const char * str,int len)60 gsdll_stdout(void *instance, const char *str, int len)
61 {
62     fwrite(str, 1, len, stdout);
63     fflush(stdout);
64     return len;
65 }
66 
67 static int GSDLLCALL
gsdll_stderr(void * instance,const char * str,int len)68 gsdll_stderr(void *instance, const char *str, int len)
69 {
70     fwrite(str, 1, len, stderr);
71     fflush(stderr);
72     return len;
73 }
74 
75 #ifndef WINDOWS_NO_UNICODE
76 /* stdio functions - versions that translate to/from utf-8 */
77 static int GSDLLCALL
gsdll_stdin_utf8(void * instance,char * buf,int len)78 gsdll_stdin_utf8(void *instance, char *buf, int len)
79 {
80     static WCHAR thiswchar = 0; /* wide character to convert to multiple bytes */
81     static int nmore = 0;       /* number of additional encoding bytes to generate */
82     UINT consolecp = 0;
83     int nret = 0;               /* number of bytes returned to caller */
84     int i;
85 
86     while (len) {
87         while (len && nmore) {
88             nmore--;
89             *buf++ = 0x80 | ((thiswchar >> (6 * nmore)) & 0x3F), nret++;
90             len--;
91         }
92         while (len) {
93             if (0 >= _read(fileno(stdin), buf, 1))
94                 return nret;
95             nret++, buf++, len--;
96             if (buf[-1] == '\n')
97                 /* return at end of line (note: no traslation needed) */
98                 return nret;
99             else if ((unsigned char)buf[-1] <= 0x7F)
100                 /* no translation needed for 7-bit ASCII codes */
101                 continue;
102             else {
103                 /* extended character, may be double */
104                 BYTE dbcsstr[2];
105 
106                 dbcsstr[0] = buf[-1];
107                 if (!consolecp)
108                     consolecp = GetConsoleCP();
109                 thiswchar = L'?'; /* initialize in case the conversion below fails */
110                 if (IsDBCSLeadByteEx(consolecp, dbcsstr[0])) {
111                     /* double-byte character code, fetch the trail byte */
112                     _read(fileno(stdin), &dbcsstr[1], 1);
113                     MultiByteToWideChar(consolecp, 0, dbcsstr, 2, &thiswchar, 1);
114                 }
115                 else {
116                     MultiByteToWideChar(consolecp, 0, dbcsstr, 1, &thiswchar, 1);
117                 }
118                 /* convert thiswchar to utf-8 */
119                 if (thiswchar <= 0x007F) {          /* encoded as single byte */
120                     buf[-1] = (char)thiswchar;
121                 } else if (thiswchar <= 0x07FF) {   /* encoded as 2 bytes */
122                     buf[-1] = 0xC0 | ((thiswchar >> 6) & 0x1F);
123                     nmore = 1;
124                     break;
125                 } else if (thiswchar <= 0xFFFF) {   /* encoded as 3 bytes */
126                     buf[-1] = 0xE0 | ((thiswchar >> 12) & 0xF);
127                     nmore = 2;
128                     break;
129                 } else
130                     /* note: codes outside the BMP not handled */
131                     buf[-1] = '?';
132             }
133         }
134     }
135     return nret;
136 }
137 
138 static void
gsdll_utf8write(FILE * stdwr,const char * str,int len,WCHAR * thiswchar,int * nmore)139 gsdll_utf8write(FILE *stdwr, const char *str, int len, WCHAR *thiswchar, int *nmore)
140 {
141     UINT consolecp = 0;
142 
143     while (len) {
144         const char *str0;
145 
146         /* write ASCII chars without translation */
147         for (str0 = str; len && !(*str & 0x80); str++, len--);
148         if (str > str0) {
149             if (*nmore) {
150                 /* output previous, incomplete utf-8 sequence as ASCII "?" */
151                 fwrite("?", 1, 1, stdwr);
152                 *nmore = 0, *thiswchar = 0;
153             }
154             fwrite(str0, 1, str - str0, stdwr);
155         }
156         /* accumulate lead/trail bytes into *thiswchar */
157         for (; len; str++, len--) {
158             switch (*str & 0xC0) {
159                 case 0x80:      /* trail byte */
160                     if (*nmore) {
161                         (*nmore)--;
162                         *thiswchar |= (WCHAR)(unsigned char)(*str & 0x3F) << (6 * *nmore);
163                         }
164                     else {
165                         /* lead byte missing; output unexpected trail byte as ASCII "?" */
166                         *nmore = 0;
167                         *thiswchar = L'?';
168                     }
169                     break;
170                 case 0xC0:      /* lead byte */
171                     if (*nmore)
172                         /* output previous, incomplete utf-8 sequence as ASCII "?" */
173                         fwrite("?", 1, 1, stdwr);
174                     if (!(*str & 0x20))
175                         *nmore = 1;     /* 2-byte encoding */
176                     else if (!(*str & 0x10))
177                         *nmore = 2;     /* 3-byte encoding */
178                     else if (!(*str & 0x08))
179                         *nmore = 3;     /* 4-byte encoding */
180                     else
181                         *nmore = 0;     /* restricted (> 4) or invalid encodings */
182                     if (*nmore)
183                         *thiswchar = (WCHAR)(unsigned char)(*str & (0x3F >> *nmore)) << (6 * *nmore);
184                     else {
185                         /* output invalid encoding as ASCII "?" */
186                         *thiswchar = L'?';
187                     }
188                     break;
189                 default:        /* cannot happen because *str has MSB set */
190                     break;
191             }
192             /* output wide character if finished */
193             if (!*nmore) {
194                 char mbstr[8];
195                 int n_mbstr;
196 
197                 if (!consolecp)
198                     consolecp = GetConsoleOutputCP();
199                 n_mbstr = WideCharToMultiByte(consolecp, 0, thiswchar, 1, mbstr, sizeof mbstr, NULL, NULL);
200                 if (n_mbstr <= 0)
201                     fwrite("?", 1, 1, stdwr);
202                 else
203                     fwrite(mbstr, 1, n_mbstr, stdwr);
204                 *thiswchar = 0; /* cleanup */
205                 str++, len--;
206                 break;
207             }
208         }
209     }
210     fflush(stdwr);
211 }
212 
213 static int GSDLLCALL
gsdll_stdout_utf8(void * instance,const char * utf8str,int bytelen)214 gsdll_stdout_utf8(void *instance, const char *utf8str, int bytelen)
215 {
216     static WCHAR thiswchar = 0; /* accumulates the bits from multiple encoding bytes */
217     static int nmore = 0;       /* expected number of additional encoding bytes */
218 
219     gsdll_utf8write(stdout, utf8str, bytelen, &thiswchar, &nmore);
220     return bytelen;
221 }
222 
223 static int GSDLLCALL
gsdll_stderr_utf8(void * instance,const char * utf8str,int bytelen)224 gsdll_stderr_utf8(void *instance, const char *utf8str, int bytelen)
225 {
226     static WCHAR thiswchar = 0; /* accumulates the bits from multiple encoding bytes */
227     static int nmore = 0;       /* expected number of additional encoding bytes */
228 
229     gsdll_utf8write(stderr, utf8str, bytelen, &thiswchar, &nmore);
230     return bytelen;
231 }
232 #endif
233 
234 /*********************************************************************/
235 /* dll device */
236 
237 /* We must run windows from another thread, since main thread */
238 /* is running Ghostscript and blocks on stdin. */
239 
240 /* We notify second thread of events using PostThreadMessage()
241  * with the following messages. Apparently Japanese Windows sends
242  * WM_USER+1 with lParam == 0 and crashes. So we use WM_USER+101.
243  * Fix from Akira Kakuto
244  */
245 #define DISPLAY_OPEN WM_USER+101
246 #define DISPLAY_CLOSE WM_USER+102
247 #define DISPLAY_SIZE WM_USER+103
248 #define DISPLAY_SYNC WM_USER+104
249 #define DISPLAY_PAGE WM_USER+105
250 #define DISPLAY_UPDATE WM_USER+106
251 
252 /*
253 #define DISPLAY_DEBUG
254 */
255 
256 /* The second thread is the message loop */
winthread(void * arg)257 static void winthread(void *arg)
258 {
259     MSG msg;
260     thread_id = GetCurrentThreadId();
261     hthread = GetCurrentThread();
262 
263     while (!quitnow && GetMessage(&msg, (HWND)NULL, 0, 0)) {
264         switch (msg.message) {
265             case DISPLAY_OPEN:
266                 image_open((IMAGE *)msg.lParam);
267                 break;
268             case DISPLAY_CLOSE:
269                 {
270                     IMAGE *img = (IMAGE *)msg.lParam;
271                     HANDLE hmutex = img->hmutex;
272                     image_close(img);
273                     CloseHandle(hmutex);
274                 }
275                 break;
276             case DISPLAY_SIZE:
277                 image_updatesize((IMAGE *)msg.lParam);
278                 break;
279             case DISPLAY_SYNC:
280                 image_sync((IMAGE *)msg.lParam);
281                 break;
282             case DISPLAY_PAGE:
283                 image_page((IMAGE *)msg.lParam);
284                 break;
285             case DISPLAY_UPDATE:
286                 image_poll((IMAGE *)msg.lParam);
287                 break;
288             default:
289                 TranslateMessage(&msg);
290                 DispatchMessage(&msg);
291         }
292     }
293 }
294 
295 /* New device has been opened */
296 /* Tell user to use another device */
display_open(void * handle,void * device)297 int display_open(void *handle, void *device)
298 {
299     IMAGE *img;
300 #ifdef DISPLAY_DEBUG
301     fprintf(stdout, "display_open(0x%x, 0x%x)\n", handle, device);
302 #endif
303     img = image_new(handle, device);    /* create and add to list */
304     if (img) {
305         img->hmutex = CreateMutex(NULL, FALSE, NULL);
306         PostThreadMessage(thread_id, DISPLAY_OPEN, 0, (LPARAM)img);
307     }
308     return 0;
309 }
310 
display_preclose(void * handle,void * device)311 int display_preclose(void *handle, void *device)
312 {
313     IMAGE *img;
314 #ifdef DISPLAY_DEBUG
315     fprintf(stdout, "display_preclose(0x%x, 0x%x)\n", handle, device);
316 #endif
317     img = image_find(handle, device);
318     if (img) {
319         /* grab mutex to stop other thread using bitmap */
320         WaitForSingleObject(img->hmutex, 120000);
321     }
322     return 0;
323 }
324 
display_close(void * handle,void * device)325 int display_close(void *handle, void *device)
326 {
327     IMAGE *img;
328 #ifdef DISPLAY_DEBUG
329     fprintf(stdout, "display_close(0x%x, 0x%x)\n", handle, device);
330 #endif
331     img = image_find(handle, device);
332     if (img) {
333         /* This is a hack to pass focus from image window to console */
334         if (GetForegroundWindow() == img->hwnd)
335             SetForegroundWindow(hwndforeground);
336 
337         image_delete(img);      /* remove from list, but don't free */
338         PostThreadMessage(thread_id, DISPLAY_CLOSE, 0, (LPARAM)img);
339     }
340     return 0;
341 }
342 
display_presize(void * handle,void * device,int width,int height,int raster,unsigned int format)343 int display_presize(void *handle, void *device, int width, int height,
344         int raster, unsigned int format)
345 {
346     IMAGE *img;
347 #ifdef DISPLAY_DEBUG
348     fprintf(stdout, "display_presize(0x%x 0x%x, %d, %d, %d, %d, %ld)\n",
349         handle, device, width, height, raster, format);
350 #endif
351     img = image_find(handle, device);
352     if (img) {
353         /* grab mutex to stop other thread using bitmap */
354         WaitForSingleObject(img->hmutex, 120000);
355     }
356     return 0;
357 }
358 
display_size(void * handle,void * device,int width,int height,int raster,unsigned int format,unsigned char * pimage)359 int display_size(void *handle, void *device, int width, int height,
360         int raster, unsigned int format, unsigned char *pimage)
361 {
362     IMAGE *img;
363 #ifdef DISPLAY_DEBUG
364     fprintf(stdout, "display_size(0x%x 0x%x, %d, %d, %d, %d, %ld, 0x%x)\n",
365         handle, device, width, height, raster, format, pimage);
366 #endif
367     img = image_find(handle, device);
368     if (img) {
369         image_size(img, width, height, raster, format, pimage);
370         /* release mutex to allow other thread to use bitmap */
371         ReleaseMutex(img->hmutex);
372         PostThreadMessage(thread_id, DISPLAY_SIZE, 0, (LPARAM)img);
373     }
374     return 0;
375 }
376 
display_sync(void * handle,void * device)377 int display_sync(void *handle, void *device)
378 {
379     IMAGE *img;
380 #ifdef DISPLAY_DEBUG
381     fprintf(stdout, "display_sync(0x%x, 0x%x)\n", handle, device);
382 #endif
383     img = image_find(handle, device);
384     if (img && !img->pending_sync) {
385         img->pending_sync = 1;
386         PostThreadMessage(thread_id, DISPLAY_SYNC, 0, (LPARAM)img);
387     }
388     return 0;
389 }
390 
display_page(void * handle,void * device,int copies,int flush)391 int display_page(void *handle, void *device, int copies, int flush)
392 {
393     IMAGE *img;
394 #ifdef DISPLAY_DEBUG
395     fprintf(stdout, "display_page(0x%x, 0x%x, copies=%d, flush=%d)\n",
396         handle, device, copies, flush);
397 #endif
398     img = image_find(handle, device);
399     if (img)
400         PostThreadMessage(thread_id, DISPLAY_PAGE, 0, (LPARAM)img);
401     return 0;
402 }
403 
display_update(void * handle,void * device,int x,int y,int w,int h)404 int display_update(void *handle, void *device,
405     int x, int y, int w, int h)
406 {
407     IMAGE *img;
408     img = image_find(handle, device);
409     if (img && !img->pending_update && !img->pending_sync) {
410         img->pending_update = 1;
411         PostThreadMessage(thread_id, DISPLAY_UPDATE, 0, (LPARAM)img);
412     }
413     return 0;
414 }
415 
416 /*
417 #define DISPLAY_DEBUG_USE_ALLOC
418 */
419 #ifdef DISPLAY_DEBUG_USE_ALLOC
420 /* This code isn't used, but shows how to use this function */
display_memalloc(void * handle,void * device,unsigned long size)421 void *display_memalloc(void *handle, void *device, unsigned long size)
422 {
423     void *mem;
424 #ifdef DISPLAY_DEBUG
425     fprintf(stdout, "display_memalloc(0x%x 0x%x %d)\n",
426         handle, device, size);
427 #endif
428     mem = malloc(size);
429 #ifdef DISPLAY_DEBUG
430     fprintf(stdout, "  returning 0x%x\n", (int)mem);
431 #endif
432     return mem;
433 }
434 
display_memfree(void * handle,void * device,void * mem)435 int display_memfree(void *handle, void *device, void *mem)
436 {
437 #ifdef DISPLAY_DEBUG
438     fprintf(stdout, "display_memfree(0x%x, 0x%x, 0x%x)\n",
439         handle, device, mem);
440 #endif
441     free(mem);
442     return 0;
443 }
444 #endif
445 
display_separation(void * handle,void * device,int comp_num,const char * name,unsigned short c,unsigned short m,unsigned short y,unsigned short k)446 int display_separation(void *handle, void *device,
447    int comp_num, const char *name,
448    unsigned short c, unsigned short m,
449    unsigned short y, unsigned short k)
450 {
451     IMAGE *img;
452 #ifdef DISPLAY_DEBUG
453     fprintf(stdout, "display_separation(0x%x, 0x%x, %d '%s' %d,%d,%d,%d)\n",
454         handle, device, comp_num, name, (int)c, (int)m, (int)y, (int)k);
455 #endif
456     img = image_find(handle, device);
457     if (img)
458         image_separation(img, comp_num, name, c, m, y, k);
459     return 0;
460 }
461 
462 display_callback display = {
463     sizeof(display_callback),
464     DISPLAY_VERSION_MAJOR,
465     DISPLAY_VERSION_MINOR,
466     display_open,
467     display_preclose,
468     display_close,
469     display_presize,
470     display_size,
471     display_sync,
472     display_page,
473     display_update,
474 #ifdef DISPLAY_DEBUG_USE_ALLOC
475     display_memalloc,   /* memalloc */
476     display_memfree,    /* memfree */
477 #else
478     NULL,       /* memalloc */
479     NULL,       /* memfree */
480 #endif
481     display_separation
482 };
483 
484 /*********************************************************************/
485 
486 #ifdef WINDOWS_NO_UNICODE
main(int argc,char * argv[])487 int main(int argc, char *argv[])
488 #else
489 static int main_utf8(int argc, char *argv[])
490 #endif
491 {
492     int code, code1;
493     int exit_code;
494     int exit_status;
495     int nargc;
496     char **nargv;
497     char buf[256];
498     char dformat[64];
499     char ddpi[64];
500 
501     if (!_isatty(fileno(stdin)))
502         _setmode(fileno(stdin), _O_BINARY);
503     _setmode(fileno(stdout), _O_BINARY);
504     _setmode(fileno(stderr), _O_BINARY);
505 
506     hwndforeground = GetForegroundWindow();     /* assume this is ours */
507     memset(buf, 0, sizeof(buf));
508     if (load_dll(&gsdll, buf, sizeof(buf))) {
509         fprintf(stderr, "Can't load Ghostscript DLL\n");
510         fprintf(stderr, "%s\n", buf);
511         return 1;
512     }
513 
514     if (gsdll.new_instance(&instance, NULL) < 0) {
515         fprintf(stderr, "Can't create Ghostscript instance\n");
516         return 1;
517     }
518 
519 #ifdef DEBUG
520     visual_tracer_init();
521     gsdll.set_visual_tracer(&visual_tracer);
522 #endif
523 
524     if (_beginthread(winthread, 65535, NULL) == -1) {
525         fprintf(stderr, "GUI thread creation failed\n");
526     }
527     else {
528         int n = 30;
529         /* wait for thread to start */
530         Sleep(0);
531         while (n && (hthread == INVALID_HANDLE_VALUE)) {
532             n--;
533             Sleep(100);
534         }
535         while (n && (PostThreadMessage(thread_id, WM_USER, 0, 0) == 0)) {
536             n--;
537             Sleep(100);
538         }
539         if (n == 0)
540             fprintf(stderr, "Can't post message to GUI thread\n");
541     }
542 
543 #ifdef WINDOWS_NO_UNICODE
544     gsdll.set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
545 #else
546     gsdll.set_stdio(instance,
547         _isatty(fileno(stdin)) ?  gsdll_stdin_utf8 : gsdll_stdin,
548         _isatty(fileno(stdout)) ?  gsdll_stdout_utf8 : gsdll_stdout,
549         _isatty(fileno(stderr)) ?  gsdll_stderr_utf8 : gsdll_stderr);
550 #endif
551     gsdll.set_display_callback(instance, &display);
552 
553     {   int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
554                 DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
555         HDC hdc = GetDC(NULL);  /* get hdc for desktop */
556         int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
557         sprintf(ddpi, "-dDisplayResolution=%d", GetDeviceCaps(hdc, LOGPIXELSY));
558         ReleaseDC(NULL, hdc);
559         if (depth == 32)
560             format = DISPLAY_COLORS_RGB | DISPLAY_UNUSED_LAST |
561                 DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
562         else if (depth == 16)
563             format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
564                 DISPLAY_DEPTH_16 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST |
565                 DISPLAY_NATIVE_555;
566         else if (depth > 8)
567             format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE |
568                 DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
569         else if (depth >= 8)
570             format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
571                 DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
572         else if (depth >= 4)
573             format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
574                 DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
575         sprintf(dformat, "-dDisplayFormat=%d", format);
576     }
577     nargc = argc + 2;
578     nargv = (char **)malloc((nargc + 1) * sizeof(char *));
579     if (nargv == NULL) {
580         fprintf(stderr, "Malloc failure!\n");
581     } else {
582         nargv[0] = argv[0];
583         nargv[1] = dformat;
584         nargv[2] = ddpi;
585         memcpy(&nargv[3], &argv[1], argc * sizeof(char *));
586 
587 #if defined(_MSC_VER) || defined(__BORLANDC__)
588         __try {
589 #endif
590             code = gsdll.init_with_args(instance, nargc, nargv);
591             if (code == 0)
592                 code = gsdll.run_string(instance, start_string, 0, &exit_code);
593             code1 = gsdll.exit(instance);
594             if (code == 0 || (code == e_Quit && code1 != 0))
595                 code = code1;
596 #if defined(_MSC_VER) || defined(__BORLANDC__)
597         } __except(exception_code() == EXCEPTION_STACK_OVERFLOW) {
598             code = e_Fatal;
599             fprintf(stderr, "*** C stack overflow. Quiting...\n");
600         }
601 #endif
602 
603         gsdll.delete_instance(instance);
604 
605 #ifdef DEBUG
606         visual_tracer_close();
607 #endif
608 
609         unload_dll(&gsdll);
610 
611         free(nargv);
612     }
613     /* close other thread */
614     quitnow = TRUE;
615     PostThreadMessage(thread_id, WM_QUIT, 0, (LPARAM)0);
616     Sleep(0);
617 
618     exit_status = 0;
619     switch (code) {
620         case 0:
621         case e_Info:
622         case e_Quit:
623             break;
624         case e_Fatal:
625             exit_status = 1;
626             break;
627         default:
628             exit_status = 255;
629     }
630 
631     return exit_status;
632 }
633 
634 #ifndef WINDOWS_NO_UNICODE
wmain(int argc,wchar_t * argv[],wchar_t * envp[])635 int wmain(int argc, wchar_t *argv[], wchar_t *envp[]) {
636     /* Duplicate args as utf8 */
637     char **nargv;
638     int i, code;
639 
640     nargv = calloc(argc, sizeof(nargv[0]));
641     if (nargv == NULL)
642         goto err;
643     for (i=0; i < argc; i++) {
644         nargv[i] = malloc(wchar_to_utf8(NULL, argv[i]));
645         if (nargv[i] == NULL)
646             goto err;
647         (void)wchar_to_utf8(nargv[i], argv[i]);
648     }
649     code = main_utf8(argc, nargv);
650 
651     if (0) {
652 err:
653         fprintf(stderr,
654                 "Ghostscript failed to initialise due to malloc failure\n");
655         code = -1;
656     }
657 
658     if (nargv) {
659         for (i=0; i<argc; i++) {
660             free(nargv[i]);
661         }
662         free(nargv);
663     }
664 
665     return code;
666 }
667 #endif
668