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