xref: /plan9/sys/src/cmd/gs/src/gp_macio.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989-2003 artofcode, LLC.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gp_macio.c,v 1.37 2004/12/09 03:47:52 giles Exp $ */
18 
19 #ifndef __CARBON__
20 //#include "MacHeaders"
21 #include <Palettes.h>
22 #include <Aliases.h>
23 #include <Quickdraw.h>
24 #include <QDOffscreen.h>
25 #include <AppleEvents.h>
26 #include <Fonts.h>
27 #include <Controls.h>
28 #include <Script.h>
29 #include <Timer.h>
30 #include <Folders.h>
31 #include <Resources.h>
32 #include <Sound.h>
33 #include <ToolUtils.h>
34 #include <Menus.h>
35 #include <LowMem.h>
36 #include <Devices.h>
37 #include <Scrap.h>
38 #include <StringCompare.h>
39 #include <Gestalt.h>
40 #include <Folders.h>
41 #include <Files.h>
42 #include <Fonts.h>
43 #include <FixMath.h>
44 #include <Resources.h>
45 #else
46 #include <Carbon.h>
47 #include <CoreServices.h>
48 #endif /* __CARBON__ */
49 
50 #include "stdio_.h"
51 #include "math_.h"
52 #include "string_.h"
53 #include <stdlib.h>
54 #include <stdarg.h>
55 #include <console.h>
56 
57 #include "gx.h"
58 #include "gp.h"
59 #include "gpmisc.h"
60 #include "gxdevice.h"
61 
62 #include "gp_mac.h"
63 
64 #include "stream.h"
65 #include "gxiodev.h"			/* must come after stream.h */
66 //#include "gp_macAE.h"
67 #include "gsdll.h"
68 
69 //HWND hwndtext;
70 
71 
72 extern void
convertSpecToPath(FSSpec * s,char * p,int pLen)73 convertSpecToPath(FSSpec * s, char * p, int pLen)
74 {
75 	OSStatus	err = noErr;
76 	CInfoPBRec	params;
77 	Str255		dirName;
78 	int		totLen = 0, dirLen = 0;
79 
80 	memcpy(p, s->name + 1, s->name[0]);
81 	totLen += s->name[0];
82 
83 	params.dirInfo.ioNamePtr = dirName;
84 	params.dirInfo.ioVRefNum = s->vRefNum;
85 	params.dirInfo.ioDrParID = s->parID;
86 	params.dirInfo.ioFDirIndex = -1;
87 
88 	do {
89 		params.dirInfo.ioDrDirID = params.dirInfo.ioDrParID;
90 		err = PBGetCatInfoSync(&params);
91 
92 		if ((err != noErr) || (totLen + dirName[0] + 2 > pLen)) {
93 			p[0] = 0;
94 			return;
95 		}
96 
97 		dirName[++dirName[0]] = ':';
98 		memmove(p + dirName[0], p, totLen);
99 		memcpy(p, dirName + 1, dirName[0]);
100 		totLen += dirName[0];
101 	} while (params.dirInfo.ioDrParID != fsRtParID);
102 
103 	p[totLen] = 0;
104 
105 	return;
106 }
107 
108 OSErr
convertPathToSpec(const char * path,const int pathlength,FSSpec * spec)109 convertPathToSpec(const char *path, const int pathlength, FSSpec * spec)
110 {
111 	Str255 filename;
112 
113 	/* path must be shorter than 255 bytes */
114 	if (pathlength > 254) return bdNamErr;
115 
116 	*filename = pathlength;
117 	memcpy(filename + 1, path, pathlength);
118 
119 	return FSMakeFSSpec(0, 0, filename, spec);
120 }
121 
122 /* ------ File name syntax ------ */
123 
124 /* Define the character used for separating file names in a list. */
125 const char gp_file_name_list_separator = ',';
126 
127 /* Define the default scratch file name prefix. */
128 const char gp_scratch_file_name_prefix[] = "tempgs_";
129 
130 /* Define the name of the null output file. */
131 const char gp_null_file_name[] = "????";
132 
133 /* Define the name that designates the current directory. */
134 extern const char gp_current_directory_name[] = ":";
135 
136 int fake_stdin = 0;
137 
138 
139 /* Do platform-dependent initialization */
140 
141 void
setenv(const char * env,char * p)142 setenv(const char * env, char *p) {
143 //	if ( strcmp(env,"outfile") == 0) {
144 //	   sprintf((char *)&g_fcout[0],"%s",p);
145 //	}
146 }
147 
148 char *
getenv(const char * env)149 getenv(const char * env) {
150 
151 	char 			*p;
152 	FSSpec			pFile;
153 	OSErr			err = 0;
154 	char			fpath[256]="";
155 
156 	if ( strcmp(env,"GS_LIB") == 0) {
157 
158 	    	pFile.name[0] = 0;
159 	    	err = FindFolder(kOnSystemDisk, kApplicationSupportFolderType, kDontCreateFolder,
160 			 					&pFile.vRefNum, &pFile.parID);
161 
162 			if (err != noErr) goto failed;
163 
164 //		FSMakeFSSpec(pFile.vRefNum, pFile.parID,thepfname, &pfile);
165 		convertSpecToPath(&pFile, fpath, 256);
166 //		sprintf(fpath,"%s",fpath);
167 		p = (char*)malloc((size_t) ( 4*strlen(fpath) + 40));
168 		sprintf(p,"%s,%sGhostscript:lib,%sGhostscript:fonts",
169 						(char *)&fpath[0],(char *)&fpath[0],
170 						(char *)&fpath[0] );
171 
172 		return p;
173 failed:
174 
175 		return NULL;
176 	} else
177 	    return NULL;
178 
179 }
180 
181 /* ====== Substitute for stdio ====== */
182 
183 /* Forward references */
184 private void mac_std_init(void);
185 private stream_proc_process(mac_stdin_read_process);
186 private stream_proc_process(mac_stdout_write_process);
187 private stream_proc_process(mac_stderr_write_process);
188 private stream_proc_available(mac_std_available);
189 
190 /* Use a pseudo IODevice to get mac_stdio_init called at the right time. */
191 /* This is bad architecture; we'll fix it later. */
192 private iodev_proc_init(mac_stdio_init);
193 const gx_io_device gs_iodev_macstdio =
194 {
195     "macstdio", "Special",
196     {mac_stdio_init, iodev_no_open_device,
197      iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,
198      iodev_no_delete_file, iodev_no_rename_file,
199      iodev_no_file_status, iodev_no_enumerate_files
200     }
201 };
202 
203 /* Do one-time initialization */
204 private int
mac_stdio_init(gx_io_device * iodev,gs_memory_t * mem)205 mac_stdio_init(gx_io_device * iodev, gs_memory_t * mem)
206 {
207     mac_std_init();		/* redefine stdin/out/err to our window routines */
208     return 0;
209 }
210 
211 /* Define alternate 'open' routines for our stdin/out/err streams. */
212 
213 extern const gx_io_device gs_iodev_stdin;
214 private int
mac_stdin_open(gx_io_device * iodev,const char * access,stream ** ps,gs_memory_t * mem)215 mac_stdin_open(gx_io_device * iodev, const char *access, stream ** ps,
216 	       gs_memory_t * mem)
217 {
218     int code = gs_iodev_stdin.procs.open_device(iodev, access, ps, mem);
219     stream *s = *ps;
220 
221     if (code != 1)
222 	return code;
223     s->procs.process = mac_stdin_read_process;
224     s->procs.available = mac_std_available;
225     s->file = NULL;
226     return 0;
227 }
228 
229 extern const gx_io_device gs_iodev_stdout;
230 private int
mac_stdout_open(gx_io_device * iodev,const char * access,stream ** ps,gs_memory_t * mem)231 mac_stdout_open(gx_io_device * iodev, const char *access, stream ** ps,
232 		gs_memory_t * mem)
233 {
234     int code = gs_iodev_stdout.procs.open_device(iodev, access, ps, mem);
235     stream *s = *ps;
236 
237     if (code != 1)
238 	return code;
239     s->procs.process = mac_stdout_write_process;
240     s->procs.available = mac_std_available;
241     s->file = NULL;
242     return 0;
243 }
244 
245 extern const gx_io_device gs_iodev_stderr;
246 private int
mac_stderr_open(gx_io_device * iodev,const char * access,stream ** ps,gs_memory_t * mem)247 mac_stderr_open(gx_io_device * iodev, const char *access, stream ** ps,
248 		gs_memory_t * mem)
249 {
250     int code = gs_iodev_stderr.procs.open_device(iodev, access, ps, mem);
251     stream *s = *ps;
252 
253     if (code != 1)
254 	return code;
255     s->procs.process = mac_stderr_write_process;
256     s->procs.available = mac_std_available;
257     s->file = NULL;
258     return 0;
259 }
260 
261 /* Patch stdin/out/err to use our windows. */
262 private void
mac_std_init(void)263 mac_std_init(void)
264 {
265     /* If stdxxx is the console, replace the 'open' routines, */
266     /* which haven't gotten called yet. */
267 
268 //    if (gp_file_is_console(gs_stdin))
269 	gs_findiodevice((const byte *)"%stdin", 6)->procs.open_device =
270 	    mac_stdin_open;
271 
272 //    if (gp_file_is_console(gs_stdout))
273 	gs_findiodevice((const byte *)"%stdout", 7)->procs.open_device =
274 	    mac_stdout_open;
275 
276 //    if (gp_file_is_console(gs_stderr))
277 	gs_findiodevice((const byte *)"%stderr", 7)->procs.open_device =
278 	    mac_stderr_open;
279 }
280 
281 
282 private int
mac_stdin_read_process(stream_state * st,stream_cursor_read * ignore_pr,stream_cursor_write * pw,bool last)283 mac_stdin_read_process(stream_state *st, stream_cursor_read *ignore_pr,
284   stream_cursor_write *pw, bool last)
285 {
286     uint count = pw->limit - pw->ptr;
287     /* callback to get more input */
288     if (pgsdll_callback == NULL) return EOFC;
289     count = (*pgsdll_callback) (GSDLL_STDIN, (char*)pw->ptr + 1, count);
290 	pw->ptr += count;
291 	return 1;
292 }
293 
294 
295 private int
mac_stdout_write_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * ignore_pw,bool last)296 mac_stdout_write_process(stream_state *st, stream_cursor_read *pr,
297   stream_cursor_write *ignore_pw, bool last)
298 {	uint count = pr->limit - pr->ptr;
299 
300     if (pgsdll_callback == NULL) return EOFC;
301     (*pgsdll_callback) (GSDLL_STDOUT, (char *)(pr->ptr + 1), count);
302 	pr->ptr = pr->limit;
303 	return 0;
304 }
305 
306 private int
mac_stderr_write_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * ignore_pw,bool last)307 mac_stderr_write_process(stream_state *st, stream_cursor_read *pr,
308   stream_cursor_write *ignore_pw, bool last)
309 {	uint count = pr->limit - pr->ptr;
310 
311     if (pgsdll_callback == NULL) return EOFC;
312     (*pgsdll_callback) (GSDLL_STDOUT, (char *)(pr->ptr + 1), count);
313 	pr->ptr = pr->limit;
314 	return 0;
315 }
316 
317 private int
mac_std_available(register stream * s,long * pl)318 mac_std_available(register stream * s, long *pl)
319 {
320     *pl = -1;		// EOF, since we can't do it
321     return 0;		// OK
322 }
323 
324 /* ------ Printer accessing ------ */
325 
326 /* These should NEVER be called. */
327 
328 /* Open a connection to a printer.  A null file name means use the */
329 /* standard printer connected to the machine, if any. */
330 /* "|command" opens an output pipe. */
331 /* Return NULL if the connection could not be opened. */
332 
333 FILE *
gp_open_printer(char * fname,int binary_mode)334 gp_open_printer (char *fname, int binary_mode)
335 {
336     if (strlen(fname) == 0)
337         return gp_open_scratch_file(gp_scratch_file_name_prefix, fname, binary_mode ? "wb" : "w");
338     else
339         return gp_fopen(fname, binary_mode ? "wb" : "b");
340 }
341 
342 /* Close the connection to the printer. */
343 
344 void
gp_close_printer(FILE * pfile,const char * fname)345 gp_close_printer (FILE *pfile, const char *fname)
346 {
347     fclose(pfile);
348 }
349 
350 
351 /* Define whether case is insignificant in file names. */
352 /* OBSOLETE
353 const int gp_file_names_ignore_case = 1;
354 */
355 
356 /* Define the string to be concatenated with the file mode */
357 /* for opening files without end-of-line conversion. */
358 const char gp_fmode_binary_suffix[] = "b";
359 
360 /* Define the file modes for binary reading or writing. */
361 const char gp_fmode_rb[] = "rb";
362 const char gp_fmode_wb[] = "wb";
363 
364 
365 /* Set a file into binary or text mode. */
366 int
gp_setmode_binary(FILE * pfile,bool binary)367 gp_setmode_binary(FILE *pfile, bool binary)
368 {	return 0;	/* Noop under VMS */
369 }
370 
371 
372 /* Create and open a scratch file with a given name prefix. */
373 /* Write the actual file name at fname. */
374 
375 FILE *
gp_open_scratch_file(const char * prefix,char fname[gp_file_name_sizeof],const char * mode)376 gp_open_scratch_file (const char *prefix, char fname[gp_file_name_sizeof], const char *mode)
377 {
378     char thefname[256];
379     Str255 thepfname;
380     OSErr myErr;
381     short foundVRefNum;
382     long foundDirID;
383     FSSpec fSpec;
384     FILE *f;
385     int prefix_length = strlen(prefix);
386 
387     if (prefix_length > gp_file_name_sizeof) return NULL;
388     strcpy (fname, (char *) prefix);
389       {
390 	char newName[50];
391 
392 	tmpnam (newName);
393 	if ( prefix_length + strlen(newName) > gp_file_name_sizeof ) return NULL;
394 	strcat (fname, newName);
395       }
396 
397    if ( strlen(fname) > 255 ) return NULL;
398    if ( strrchr(fname,':') == NULL ) {
399        memmove((char*)&thepfname[1],(char *)&fname[0],strlen(fname));
400 	   thepfname[0]=strlen(fname);
401 		myErr = FindFolder(kOnSystemDisk,kTemporaryFolderType,kCreateFolder,
402 			&foundVRefNum, &foundDirID);
403 		if ( myErr != noErr ) {
404 			eprintf("Can't find temp folder.\n");
405 			return (NULL);
406 		}
407 		FSMakeFSSpec(foundVRefNum, foundDirID,thepfname, &fSpec);
408 		convertSpecToPath(&fSpec, thefname, sizeof(thefname) - 1);
409 		sprintf(fname,"%s",thefname);
410    } else {
411        sprintf((char*)&thefname[0],"%s\0",fname);
412        memmove((char*)&thepfname[1],(char *)&thefname[0],strlen(thefname));
413 	   thepfname[0]=strlen(thefname);
414    }
415 
416     f = gp_fopen (thefname, mode);
417     if (f == NULL)
418 	eprintf1("**** Could not open temporary file %s\n", fname);
419     return f;
420 }
421 
422 /* read a resource and copy the data into a buffer */
423 /* we don't have access to an allocator, nor any context for local  */
424 /* storage, so we implement the following idiom: we return the size */
425 /* of the requested resource and copy the data into buf iff it's    */
426 /* non-NULL. Thus, the caller can pass NULL for buf the first time, */
427 /* allocate the appropriate sized buffer, and then call us a second */
428 /* time to actually transfer the data.                              */
429 int
gp_read_macresource(byte * buf,const char * fname,const uint type,const ushort id)430 gp_read_macresource(byte *buf, const char *fname, const uint type, const ushort id)
431 {
432     Handle resource = NULL;
433     SInt32 size = 0;
434     FSSpec spec;
435     SInt16 fileref;
436     OSErr result;
437 
438     /* open file */
439     result = convertPathToSpec(fname, strlen(fname), &spec);
440     if (result != noErr) goto fin;
441     fileref = FSpOpenResFile(&spec, fsRdPerm);
442     if (fileref == -1) goto fin;
443 
444     if_debug1('s', "[s] loading resource from fileref %d\n", fileref);
445 
446     /* load resource */
447     resource = Get1Resource((ResType)type, (SInt16)id);
448     if (resource == NULL) goto fin;
449 
450     /* allocate res */
451     /* GetResourceSize() is probably good enough */
452     //size = GetResourceSizeOnDisk(resource);
453     size = GetMaxResourceSize(resource);
454 
455     if_debug1('s', "[s] resource size on disk is %d bytes\n", size);
456 
457     /* if we don't have a buffer to fill, just return */
458     if (buf == NULL) goto fin;
459 
460     /* otherwise, copy resource into res from handle */
461     HLock(resource);
462     memcpy(buf, *resource, size);
463     HUnlock(resource);
464 
465 fin:
466     /* free resource, if necessary */
467     ReleaseResource(resource);
468     CloseResFile(fileref);
469 
470     return (size);
471 }
472 
473 /* return a list of font names and corresponding paths from
474  * the native system locations
475  */
gp_native_fontmap(char * names[],char * paths[],int * count)476 int gp_native_fontmap(char *names[], char *paths[], int *count)
477 {
478     return 0;
479 }
480 
481 /* ------ File enumeration ------ */
482 
483 /****** THIS IS NOT SUPPORTED ON MACINTOSH SYSTEMS. ******/
484 
485 struct file_enum_s {
486 	char *pattern;
487 	int first_time;
488 	gs_memory_t *memory;
489 };
490 
491 /* Initialize an enumeration.  NEEDS WORK ON HANDLING * ? \. */
492 
493 file_enum *
gp_enumerate_files_init(const char * pat,uint patlen,gs_memory_t * memory)494 gp_enumerate_files_init (const char *pat, uint patlen, gs_memory_t *memory)
495 
496 {	file_enum *pfen =
497 		(file_enum *)gs_alloc_bytes(memory, sizeof(file_enum), "gp_enumerate_files");
498 	char *pattern;
499 	if ( pfen == 0 ) return 0;
500 	pattern =
501 		(char *)gs_alloc_bytes(memory, patlen + 1, "gp_enumerate_files(pattern)");
502 	if ( pattern == 0 ) return 0;
503 	memcpy(pattern, pat, patlen);
504 	pattern[patlen] = 0;
505 	pfen->pattern = pattern;
506 	pfen->memory = memory;
507 	pfen->first_time = 1;
508 	return pfen;
509 }
510 
511 /* Enumerate the next file. */
512 
513 uint
gp_enumerate_files_next(file_enum * pfen,char * ptr,uint maxlen)514 gp_enumerate_files_next (file_enum *pfen, char *ptr, uint maxlen)
515 
516 {	if ( pfen->first_time )
517 	   {	pfen->first_time = 0;
518 	   }
519 	return -1;
520 }
521 
522 /* Clean up the file enumeration. */
523 
524 void
gp_enumerate_files_close(file_enum * pfen)525 gp_enumerate_files_close (file_enum *pfen)
526 
527 {
528 	gs_free_object(pfen->memory, pfen->pattern, "gp_enumerate_files_close(pattern)");
529 	gs_free_object(pfen->memory, (char *)pfen, "gp_enumerate_files_close");
530 }
531 
532 FILE *
gp_fopen(const char * fname,const char * mode)533 gp_fopen (const char * fname, const char * mode) {
534 
535    char thefname[256];
536    FILE *fid;
537 
538 //sprintf((char*)&thefname[0],"\n%s\n",fname);
539 //(*pgsdll_callback) (GSDLL_STDOUT, thefname, strlen(fname));
540    if ( strrchr(fname,':') == NULL )
541 //      sprintf((char *)&thefname[0],"%s%s\0",g_homeDir,fname);
542       sprintf((char *)&thefname[0],"%s%s\0","",fname);
543    else
544        sprintf((char*)&thefname[0],"%s\0",fname);
545 
546    fid = fopen(thefname,mode);
547 
548    return fid;
549 
550 }
551 
552 FILE *
popen(const char * fname,const char * mode)553 popen (const char * fname, const char * mode ) {
554 	return gp_fopen (fname,  mode);
555 }
556 
557 int
pclose(FILE * pipe)558 pclose (FILE * pipe ) {
559 	return fclose (pipe);
560 }
561 
562 /* -------------- Helpers for gp_file_name_combine_generic ------------- */
563 
564 #ifdef __CARBON__
565 
566 /* compare an HFSUnitStr255 with a C string */
compare_UniStr(HFSUniStr255 u,const char * c,uint len)567 static int compare_UniStr(HFSUniStr255 u, const char *c, uint len)
568 {
569 	int i,searchlen,unichar;
570 	searchlen = min(len,u.length);
571 	for (i = 0; i < searchlen; i++) {
572 	  unichar = u.unicode[i];
573 	  /* punt on wide characters. we should really convert */
574 	  if (unichar & !0xFF) return -1;
575 	  /* otherwise return the the index of the first non-matching character */
576 	  if (unichar != c[i]) break;
577 	}
578 	/* return the offset iff we matched the whole volume name */
579 	return (i == u.length) ? i : 0;
580 }
581 
gp_file_name_root(const char * fname,uint len)582 uint gp_file_name_root(const char *fname, uint len)
583 {
584 	OSErr err = noErr;
585    	HFSUniStr255 volumeName;
586    	FSRef rootDirectory;
587    	int index, match;
588 
589     if (len > 0 && fname[0] == ':')
590 		return 0; /* A relative path, no root. */
591 
592 	/* iterate over mounted volumes and compare our path */
593 	index = 1;
594 	while (err == noErr) {
595 		err = FSGetVolumeInfo (kFSInvalidVolumeRefNum, index,
596 			NULL, kFSVolInfoNone, NULL, /* not interested in these fields */
597 			&volumeName, &rootDirectory);
598 		if (err == nsvErr) return 0; /* no more volumes */
599 		if (err == noErr) {
600 			match = compare_UniStr(volumeName, fname, len);
601 			if (match > 0) {
602     			/* include the separator if it's present  */
603 				if (fname[match] == ':') return match + 1;
604 				return match;
605 			}
606 		}
607 		index++;
608 	}
609 
610 	/* nothing matched */
611     return 0;
612 }
613 
614 #else /* Classic MacOS */
615 
616 /* FSGetVolumeInfo requires carbonlib or macos >= 9
617    we essentially leave this unimplemented on Classic */
gp_file_name_root(const char * fname,uint len)618 uint gp_file_name_root(const char *fname, uint len)
619 {
620 	return 0;
621 }
622 
623 #endif /* __CARBON__ */
624 
625 
gs_file_name_check_separator(const char * fname,int len,const char * item)626 uint gs_file_name_check_separator(const char *fname, int len, const char *item)
627 {   if (len > 0) {
628 	if (fname[0] == ':') {
629 	    if (fname == item + 1 && item[0] == ':')
630 		return 1; /* It is a separator after parent. */
631 	    if (len > 1 && fname[1] == ':')
632 		return 0; /* It is parent, not a separator. */
633 	    return 1;
634 	}
635     } else if (len < 0) {
636 	if (fname[-1] == ':')
637 	    return 1;
638     }
639     return 0;
640 }
641 
gp_file_name_is_parent(const char * fname,uint len)642 bool gp_file_name_is_parent(const char *fname, uint len)
643 {   return len == 1 && fname[0] == ':';
644 }
645 
gp_file_name_is_current(const char * fname,uint len)646 bool gp_file_name_is_current(const char *fname, uint len)
647 {   return (len == 0) || (len == 1 && fname[0] == ':');
648 }
649 
gp_file_name_separator(void)650 const char *gp_file_name_separator(void)
651 {   return ":";
652 }
653 
gp_file_name_directory_separator(void)654 const char *gp_file_name_directory_separator(void)
655 {   return ":";
656 }
657 
gp_file_name_parent(void)658 const char *gp_file_name_parent(void)
659 {   return "::";
660 }
661 
gp_file_name_current(void)662 const char *gp_file_name_current(void)
663 {   return ":";
664 }
665 
gp_file_name_is_partent_allowed(void)666 bool gp_file_name_is_partent_allowed(void)
667 {   return true;
668 }
669 
gp_file_name_is_empty_item_meanful(void)670 bool gp_file_name_is_empty_item_meanful(void)
671 {   return true;
672 }
673 
674 gp_file_name_combine_result
gp_file_name_combine(const char * prefix,uint plen,const char * fname,uint flen,bool no_sibling,char * buffer,uint * blen)675 gp_file_name_combine(const char *prefix, uint plen, const char *fname, uint flen,
676 		    bool no_sibling, char *buffer, uint *blen)
677 {
678     return gp_file_name_combine_generic(prefix, plen,
679 	    fname, flen, no_sibling, buffer, blen);
680 }
681 
682 // FIXME: there must be a system util for this!
MacStr2c(char * pstring)683 static char *MacStr2c(char *pstring)
684 {
685 	char *cstring;
686 	int len = (pstring[0] < 256) ? pstring[0] : 255;
687 
688 	if (len == 0) return NULL;
689 
690 	cstring = malloc(len + 1);
691 	if (cstring != NULL) {
692 		memcpy(cstring, &(pstring[1]), len);
693 		cstring[len] = '\0';
694 	}
695 
696 	return(cstring);
697 }
698 
699 /* ------ Font enumeration ------ */
700 
701  /* This is used to query the native os for a list of font names and
702   * corresponding paths. The general idea is to save the hassle of
703   * building a custom fontmap file
704   */
705 
706 typedef struct {
707     int size, style, id;
708 } fond_entry;
709 
710 typedef struct {
711     int entries;
712     fond_entry *refs;
713 } fond_table;
714 
fond_table_new(int entries)715 static fond_table *fond_table_new(int entries)
716 {
717     fond_table *table = malloc(sizeof(fond_table));
718     if (table != NULL) {
719         table->entries = entries;
720         table->refs = malloc(entries * sizeof(fond_entry));
721         if (table->refs == NULL) { free(table); table = NULL; }
722     }
723     return table;
724 }
725 
fond_table_free(fond_table * table)726 static void fond_table_free(fond_table *table)
727 {
728     if (table != NULL) {
729         if (table->refs) free(table->refs);
730         free(table);
731     }
732 }
733 
fond_table_grow(fond_table * table,int entries)734 static fond_table *fond_table_grow(fond_table *table, int entries)
735 {
736     if (table == NULL) {
737         table = fond_table_new(entries);
738     } else {
739         table->entries += entries;
740         table->refs = realloc(table->refs, table->entries * sizeof(fond_entry));
741     }
742     return table;
743 }
744 
get_int16(unsigned char * p)745 static int get_int16(unsigned char *p) {
746     return (p[0]&0xFF)<<8 | (p[1]&0xFF);
747 }
748 
get_int32(unsigned char * p)749 static int get_int32(unsigned char *p) {
750     return (p[0]&0xFF)<<24 | (p[1]&0xFF)<<16 | (p[2]&0xFF)<<8 | (p[3]&0xFF);
751 }
752 
753 /* parse and summarize FOND resource information */
parse_fond(FSSpec * spec)754 static fond_table * parse_fond(FSSpec *spec)
755 {
756     OSErr result = noErr;
757     FSRef specref;
758     SInt16 ref;
759     Handle fond = NULL;
760     unsigned char *res;
761     fond_table *table = NULL;
762     int i,j, count, n, start;
763 
764 	/* FSpOpenResFile will fail for data fork resource (.dfont) files.
765 	   FSOpenResourceFile can open either, but cannot handle broken resource
766 	   maps, as often occurs in font files (the suitcase version of Arial,
767 	   for example) Thus, we try one, and then the other. */
768 
769     result = FSpMakeFSRef(spec,&specref);
770 #ifdef __CARBON__
771    	if (result == noErr)
772    		result = FSOpenResourceFile(&specref, 0, NULL, fsRdPerm, &ref);
773 #else
774 	result = bdNamErr; /* simulate failure of the carbon routine above */
775 #endif
776     if (result != noErr) {
777 	    ref = FSpOpenResFile(spec, fsRdPerm);
778 	    result = ResError();
779 	}
780     if (result != noErr || ref <= 0) {
781     	char path[256];
782     	convertSpecToPath(spec, path, 256);
783       	dlprintf2("unable to open resource file '%s' for font enumeration (error %d)\n",
784       		path, result);
785       	goto fin;
786     }
787 
788     /* we've opened the font file, now loop over the FOND resource(s)
789        and construct a table of the font references */
790 
791     start = 0; /* number of entries so far */
792     UseResFile(ref);
793     count = Count1Resources('FOND');
794     for (i = 0; i < count; i++) {
795         fond = Get1IndResource('FOND', i+1);
796         if (fond == NULL) {
797             result = ResError();
798             goto fin;
799         }
800 
801         /* The FOND resource structure corresponds to the FamRec and AsscEntry
802            data structures documented in the FontManager reference. However,
803            access to these types is deprecated in Carbon. We therefore access the
804            data by direct offset in the hope that the resource format will not change
805            even if api access to the in-memory versions goes away. */
806         HLock(fond);
807         res = *fond + 52; /* offset to association table */
808         n = get_int16(res) + 1;	res += 2;
809 		table = fond_table_grow(table, n);
810         for (j = start; j < start + n; j++ ) {
811             table->refs[j].size = get_int16(res); res += 2;
812             table->refs[j].style = get_int16(res); res += 2;
813             table->refs[j].id = get_int16(res); res += 2;
814         }
815         start += n;
816         HUnlock(fond);
817     }
818 fin:
819     CloseResFile(ref);
820     return table;
821 }
822 
823 /* FIXME: should check for uppercase as well */
is_ttf_file(const char * path)824 static int is_ttf_file(const char *path)
825 {
826     int len = strlen(path);
827     return !memcmp(path+len-4,".ttf",4);
828 }
is_otf_file(const char * path)829 static int is_otf_file(const char *path)
830 {
831     int len = strlen(path);
832     return !memcmp(path+len-4,".otf",4);
833 }
834 
strip_char(char * string,int len,const int c)835 static void strip_char(char *string, int len, const int c)
836 {
837     char *bit;
838     len += 1;
839     while(bit = strchr(string,' ')) {
840         memmove(bit, bit + 1, string + len - bit - 1);
841     }
842 }
843 
844 /* get the macos name for the font instance and mangle it into a PS
845    fontname */
makePSFontName(FMFontFamily Family,FMFontStyle Style)846 static char *makePSFontName(FMFontFamily Family, FMFontStyle Style)
847 {
848 	Str255 Name;
849 	OSStatus result;
850 	int length;
851 	char *stylename, *fontname;
852 	char *psname;
853 
854 	result = FMGetFontFamilyName(Family, Name);
855 	if (result != noErr) return NULL;
856 	fontname = MacStr2c(Name);
857 	if (fontname == NULL) return NULL;
858 	strip_char(fontname, strlen(fontname), ' ');
859 
860 	switch (Style) {
861 		case 0: stylename=""; break;;
862 		case 1: stylename="Bold"; break;;
863 		case 2: stylename="Italic"; break;;
864 		case 3: stylename="BoldItalic"; break;;
865 		default: stylename="Unknown"; break;;
866 	}
867 
868 	length = strlen(fontname) + strlen(stylename) + 2;
869 	psname = malloc(length);
870 	if (Style != 0)
871 		snprintf(psname, length, "%s-%s", fontname, stylename);
872 	else
873 		snprintf(psname, length, "%s", fontname);
874 
875 	free(fontname);
876 
877 	return psname;
878 }
879 
880 typedef struct {
881     int count;
882     FMFontIterator Iterator;
883     char *name;
884     char *path;
885     FSSpec last_container;
886     char *last_container_path;
887     fond_table *last_table;
888 } fontenum_t;
889 
gp_enumerate_fonts_init(gs_memory_t * mem)890 void *gp_enumerate_fonts_init(gs_memory_t *mem)
891 {
892     fontenum_t *state = gs_alloc_bytes(mem, sizeof(fontenum_t),
893 	"macos font enumerator state");
894 	FMFontIterator *Iterator = &state->Iterator;
895 	OSStatus result;
896 
897     if (state != NULL) {
898 		state->count = 0;
899 		state->name = NULL;
900 		state->path = NULL;
901 		result = FMCreateFontIterator(NULL, NULL,
902 			kFMLocalIterationScope, Iterator);
903 		if (result != noErr) return NULL;
904 		memset(&state->last_container, 0, sizeof(FSSpec));
905 		state->last_container_path = NULL;
906 		state->last_table = NULL;
907     }
908 
909     return (void *)state;
910 }
911 
gp_enumerate_fonts_free(void * enum_state)912 void gp_enumerate_fonts_free(void *enum_state)
913 {
914     fontenum_t *state = (fontenum_t *)enum_state;
915 	FMFontIterator *Iterator = &state->Iterator;
916 
917 	FMDisposeFontIterator(Iterator);
918 
919     /* free any malloc'd stuff here */
920     if (state->name) free(state->name);
921     if (state->path) free(state->path);
922     if (state->last_container_path) free(state->last_container_path);
923     if (state->last_table) fond_table_free(state->last_table);
924     /* the garbage collector will take care of the struct itself */
925 
926 }
927 
gp_enumerate_fonts_next(void * enum_state,char ** fontname,char ** path)928 int gp_enumerate_fonts_next(void *enum_state, char **fontname, char **path)
929 {
930     fontenum_t *state = (fontenum_t *)enum_state;
931 	FMFontIterator *Iterator = &state->Iterator;
932 	FMFont Font;
933 	FourCharCode Format;
934 	FMFontFamily FontFamily;
935 	FMFontStyle Style;
936 	FSSpec FontContainer;
937 	char type[5];
938 	char fontpath[256];
939 	char *psname;
940 	fond_table *table = NULL;
941 	OSStatus result;
942 
943 	result = FMGetNextFont(Iterator, &Font);
944     if (result != noErr) return 0; /* no more fonts */
945 
946 	result = FMGetFontFormat(Font, &Format);
947 	type[0] = ((char*)&Format)[0];
948 	type[1] = ((char*)&Format)[1];
949 	type[2] = ((char*)&Format)[2];
950 	type[3] = ((char*)&Format)[3];
951 	type[4] = '\0';
952 
953  	FMGetFontFamilyInstanceFromFont(Font, &FontFamily, &Style);
954     if (state->name) free (state->name);
955 
956     psname = makePSFontName(FontFamily, Style);
957     if (psname == NULL) {
958 		state->name = strdup("GSPlaceHolder");
959 	} else {
960 		state->name = psname;
961 	}
962 
963 	result = FMGetFontContainer(Font, &FontContainer);
964 	if (!memcmp(&FontContainer, &state->last_container, sizeof(FSSpec))) {
965 		/* we have cached data on this file */
966 		strncpy(fontpath, state->last_container_path, 256);
967 		table = state->last_table;
968 	} else {
969 		convertSpecToPath(&FontContainer, fontpath, 256);
970 		if (!is_ttf_file(fontpath) && !is_otf_file(fontpath))
971 	    	table = parse_fond(&FontContainer);
972 	    /* cache data on the new font file */
973 	    memcpy(&state->last_container, &FontContainer, sizeof(FSSpec));
974 	    if (state->last_container_path) free (state->last_container_path);
975 		state->last_container_path = strdup(fontpath);
976 		if (state->last_table) fond_table_free(state->last_table);
977 		state->last_table = table;
978 	}
979 
980 	if (state->path) {
981 		free(state->path);
982 		state->path = NULL;
983 	}
984     if (table != NULL) {
985     	int i;
986     	for (i = 0; i < table->entries; i++) {
987             if (table->refs[i].size == 0) { /* ignore non-scalable fonts */
988                 if (table->refs[i].style == Style) {
989                     int len = strlen(fontpath) + strlen("%macresource%#sfnt+") + 6;
990                 	state->path = malloc(len);
991                     snprintf(state->path, len, "%%macresource%%%s#sfnt+%d",
992                         fontpath, table->refs[i].id);
993                     break;
994                 }
995             }
996         }
997     } else {
998         /* regular font file */
999         state->path = strdup(fontpath);
1000     }
1001     if (state->path == NULL) {
1002     	/* no matching font was found in the FOND resource table. this usually */
1003     	/* means an LWFN file, which we don't handle yet. */
1004     	/* we still specify these with a %macresource% path, but no res id */
1005     	/* TODO: check file type */
1006     	int len = strlen(fontpath) + strlen("%macresource%#POST") + 1;
1007     	state->path = malloc(len);
1008     	snprintf(state->path, len, "%%macresource%%%s#POST", fontpath);
1009     }
1010 #ifdef DEBUG
1011     dlprintf2("fontenum: returning '%s' in '%s'\n", state->name, state->path);
1012 #endif
1013     *fontname = state->name;
1014     *path = state->path;
1015 
1016 	state->count += 1;
1017 	return 1;
1018 }
1019 
1020