1 /* Copyright (C) 2001-2006 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, modified
8 or distributed except as expressly authorized under the terms of that
9 license. Refer to licensing information at http://www.artifex.com/
10 or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11 San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
12 */
13
14 /* $Id: gp_vms.c 8250 2007-09-25 13:31:24Z giles $ */
15 /* VAX/VMS specific routines for Ghostscript */
16
17 #include "string_.h"
18 #include "memory_.h"
19 #include "gx.h"
20 #include "gp.h"
21 #include "gpmisc.h"
22 #include "gsstruct.h"
23 #include <stat.h>
24 #include <stdlib.h> /* for exit() with some compiler versions */
25 #include <errno.h> /* for exit() with other compiler versions */
26 #include <unixio.h>
27
28 extern char *getenv(const char *);
29
30 /* Apparently gcc doesn't allow extra arguments for fopen: */
31 #ifdef VMS /* DEC C */
32 # define fopen_VMS fopen
33 #else /* gcc */
34 # define fopen_VMS(name, mode, m1, m2) fopen(name, mode)
35 #endif
36
37
38 /* VMS string descriptor structure */
39 #define DSC$K_DTYPE_T 14
40 #define DSC$K_CLASS_S 1
41 struct dsc$descriptor_s {
42 unsigned short dsc$w_length;
43 unsigned char dsc$b_dtype;
44 unsigned char dsc$b_class;
45 char *dsc$a_pointer;
46 };
47 typedef struct dsc$descriptor_s descrip;
48
49 /* VMS RMS constants */
50 #define RMS_IS_ERROR_OR_NMF(rmsv) (((rmsv) & 1) == 0)
51 #define RMS$_NMF 99018
52 #define RMS$_NORMAL 65537
53 #define NAM$C_MAXRSS 255
54
55 struct file_enum_s {
56 uint context, length;
57 descrip pattern;
58 gs_memory_t *memory;
59 };
60 gs_private_st_ptrs1(st_file_enum, struct file_enum_s, "file_enum",
61 file_enum_enum_ptrs, file_enum_reloc_ptrs, pattern.dsc$a_pointer);
62
63 extern uint
64 LIB$FIND_FILE(descrip *, descrip *, uint *, descrip *, descrip *,
65 uint *, uint *),
66 LIB$FIND_FILE_END(uint *),
67 SYS$FILESCAN(descrip *, uint *, uint *),
68 SYS$PUTMSG(uint *, int (*)(), descrip *, uint);
69
70 static uint
strlength(char * str,uint maxlen,char term)71 strlength(char *str, uint maxlen, char term)
72 {
73 uint i = 0;
74
75 while (i < maxlen && str[i] != term)
76 i++;
77 return i;
78 }
79
80 /* Do platform-dependent initialization. */
81 void
gp_init(void)82 gp_init(void)
83 {
84 }
85
86 /* Do platform-dependent cleanup. */
87 void
gp_exit(int exit_status,int code)88 gp_exit(int exit_status, int code)
89 {
90 }
91
92 /* Exit the program. */
93 void
gp_do_exit(int exit_status)94 gp_do_exit(int exit_status)
95 { /* The program returns exit_status = 0 for OK, 1 for failure; */
96 /* VMS has different conventions. */
97 switch (exit_status) {
98 case 0:
99 exit(exit_OK);
100 case 1:
101 exit(exit_FAILED);
102 }
103 exit(exit_status);
104 }
105
106 /* ------ Date and time ------ */
107
108 /* Read the current time (in seconds since Jan. 1, 1980) */
109 /* and fraction (in nanoseconds). */
110 void
gp_get_realtime(long * pdt)111 gp_get_realtime(long *pdt)
112 {
113 struct {
114 uint _l0, _l1;
115 } binary_date, now, difference;
116 long LIB$EDIV(), LIB$SUBX(), SYS$BINTIM(), SYS$GETTIM();
117 long units_per_second = 10000000;
118 char *jan_1_1980 = "1-JAN-1980 00:00:00.00";
119 descrip str_desc;
120
121 /* For those interested, Wednesday, November 17, 1858 is the base
122 of the Modified Julian Day system adopted by the Smithsonian
123 Astrophysical Observatory in 1957 for satellite tracking. (The
124 year 1858 preceded the oldest star catalog in use at the
125 observatory.) VMS uses quadword time stamps which are offsets
126 in 100 nanosecond units from November 17, 1858. With a 63-bit
127 absolute time representation (sign bit must be clear), VMS will
128 have no trouble with time until 31-JUL-31086 02:48:05.47. */
129
130 /* Convert January 1, 1980 into a binary absolute time */
131 str_desc.dsc$w_length = strlen(jan_1_1980);
132 str_desc.dsc$a_pointer = jan_1_1980;
133 (void)SYS$BINTIM(&str_desc, &binary_date);
134
135 /* Compute number of 100 nanosecond units since January 1, 1980. */
136 (void)SYS$GETTIM(&now);
137 (void)LIB$SUBX(&now, &binary_date, &difference);
138
139 /* Convert to seconds and nanoseconds. */
140 (void)LIB$EDIV(&units_per_second, &difference, &pdt[0], &pdt[1]);
141 pdt[1] *= 100;
142 }
143
144 /* Read the current user CPU time (in seconds) */
145 /* and fraction (in nanoseconds). */
146 void
gp_get_usertime(long * pdt)147 gp_get_usertime(long *pdt)
148 {
149 gp_get_realtime(pdt); /* Use an approximation for now. */
150 }
151
152
153 /* ------ Persistent data cache ------*/
154
155 /* insert a buffer under a (type, key) pair */
gp_cache_insert(int type,byte * key,int keylen,void * buffer,int buflen)156 int gp_cache_insert(int type, byte *key, int keylen, void *buffer, int buflen)
157 {
158 /* not yet implemented */
159 return 0;
160 }
161
162 /* look up a (type, key) in the cache */
gp_cache_query(int type,byte * key,int keylen,void ** buffer,gp_cache_alloc alloc,void * userdata)163 int gp_cache_query(int type, byte* key, int keylen, void **buffer,
164 gp_cache_alloc alloc, void *userdata)
165 {
166 /* not yet implemented */
167 return -1;
168 }
169
170 /* ------ Screen management ------ */
171
172 /* Get the environment variable that specifies the display to use. */
173 const char *
gp_getenv_display(void)174 gp_getenv_display(void)
175 {
176 return getenv("DECW$DISPLAY");
177 }
178
179 /* ------ Printer accessing ------ */
180
181 /* Open a connection to a printer. A null file name means use the */
182 /* standard printer connected to the machine, if any. */
183 /* Return NULL if the connection could not be opened. */
184 FILE *
gp_open_printer(char fname[gp_file_name_sizeof],int binary_mode)185 gp_open_printer(char fname[gp_file_name_sizeof], int binary_mode)
186 {
187 if (strlen(fname) == 0)
188 return 0;
189 if (binary_mode) { /*
190 * Printing must be done exactly byte to byte,
191 * using "passall". However the standard VMS symbiont
192 * does not treat stream-LF files correctly in this respect,
193 * but throws away \n characters. Giving the file
194 * the record type "undefined", but accessing it as a
195 * normal stream-LF file does the trick.
196 */
197 return fopen_VMS(fname, "w", "rfm = udf", "ctx = stm");
198 } else { /* Open as a normal text stream file. */
199 return fopen_VMS(fname, "w", "rfm = var", "rat = cr");
200 }
201 }
202
203 /* Close the connection to the printer. */
204 void
gp_close_printer(FILE * pfile,const char * fname)205 gp_close_printer(FILE * pfile, const char *fname)
206 {
207 fclose(pfile);
208 }
209
210 /* ------ File naming and accessing ------ */
211
212 /* Define the character used for separating file names in a list. */
213 const char gp_file_name_list_separator = ',';
214
215 /* Define the default scratch file name prefix. */
216 const char gp_scratch_file_name_prefix[] = "_temp_";
217
218 /* Define the name of the null output file. */
219 const char gp_null_file_name[] = "NLA0:";
220
221 /* Define the name that designates the current directory. */
222 const char gp_current_directory_name[] = "[]";
223
224 /* Define the string to be concatenated with the file mode */
225 /* for opening files without end-of-line conversion. */
226 const char gp_fmode_binary_suffix[] = "";
227
228 /* Define the file modes for binary reading or writing. */
229 const char gp_fmode_rb[] = "r";
230 const char gp_fmode_wb[] = "w";
231
232 /* Create and open a scratch file with a given name prefix. */
233 /* Write the actual file name at fname. */
234 FILE *
gp_open_scratch_file(const char * prefix,char fname[gp_file_name_sizeof],const char * mode)235 gp_open_scratch_file(const char *prefix, char fname[gp_file_name_sizeof],
236 const char *mode)
237 {
238 FILE *f;
239 char tmpdir[gp_file_name_sizeof];
240 int tdlen = gp_file_name_sizeof;
241 int flen[1];
242
243 if (!gp_file_name_is_absolute(prefix, strlen(prefix)) &&
244 gp_gettmpdir(tmpdir, &tdlen) == 0) {
245 flen[0] = gp_file_name_sizeof;
246 if (gp_file_name_combine(tmpdir, tdlen, prefix, strlen(prefix),
247 false, fname, flen ) != gp_combine_success ) {
248 return NULL;
249 }
250 fname[ *flen ] = 0;
251 } else {
252 strcpy(fname, prefix);
253 }
254 if (strlen(fname) + 6 >= gp_file_name_sizeof)
255 return 0; /* file name too long */
256 strcat(fname, "XXXXXX");
257 mktemp(fname);
258 f = fopen(fname, mode);
259
260 if (f == NULL)
261 eprintf1("**** Could not open temporary file %s\n", fname);
262 return f;
263 }
264
265 /* Open a file with the given name, as a stream of uninterpreted bytes. */
266 /* We have to do something special if the file was FTP'ed in binary mode. */
267 /* Unfortunately, only DEC C supports the extra arguments to fopen. */
268 FILE *
gp_fopen(const char * fname,const char * mode)269 gp_fopen(const char *fname, const char *mode)
270 {
271 #ifdef __DECC
272 #define FAB$C_FIX 1
273 stat_t buffer;
274
275 if (stat((char *)fname, &buffer) == 0)
276 if (buffer.st_fab_rfm == FAB$C_FIX)
277 return fopen(fname, mode, "rfm=stmlf", "ctx=stm");
278 #endif
279 return fopen(fname, mode);
280 }
281
282 /* Set a file into binary or text mode. */
283 int
gp_setmode_binary(FILE * pfile,bool binary)284 gp_setmode_binary(FILE * pfile, bool binary)
285 {
286 return 0; /* Noop under VMS */
287 }
288
289 /* ------ Wild card file search procedures ------ */
290
291 static void
gp_free_enumeration(file_enum * pfen)292 gp_free_enumeration(file_enum * pfen)
293 {
294 if (pfen) {
295 LIB$FIND_FILE_END(&pfen->context);
296 gs_free_object(pfen->memory, pfen->pattern.dsc$a_pointer,
297 "GP_ENUM(pattern)");
298 gs_free_object(pfen->memory, pfen,
299 "GP_ENUM(file_enum)");
300 }
301 }
302
303 /* Begin an enumeration. See gp.h for details. */
304
305 file_enum *
gp_enumerate_files_init(const char * pat,uint patlen,gs_memory_t * mem)306 gp_enumerate_files_init(const char *pat, uint patlen, gs_memory_t * mem)
307 {
308 file_enum *pfen;
309 uint i, len;
310 char *c, *newpat;
311 bool dot_in_filename = false;
312
313 pfen = gs_alloc_struct(mem, file_enum, &st_file_enum,
314 "GP_ENUM(file_enum)");
315 newpat = (char *)gs_alloc_bytes(mem, patlen + 2, "GP_ENUM(pattern)");
316 if (pfen == 0 || newpat == 0) {
317 gs_free_object(mem, newpat, "GP_ENUM(pattern)");
318 gs_free_object(mem, pfen, "GP_ENUM(file_enum)");
319 return (file_enum *) 0;
320 }
321 /* Copy the pattern removing backslash quoting characters and
322 * transforming unquoted question marks, '?', to percent signs, '%'.
323 * (VAX/VMS uses the wildcard '%' to represent exactly one character
324 * and '*' to represent zero or more characters. Any combination and
325 * number of interspersed wildcards is permitted.)
326 *
327 * Since VMS requires "*.*" to actually return all files, we add a
328 * special check for a path ending in "*" and change it into "*.*"
329 * if a "." wasn't part of the file spec. Thus "[P.A.T.H]*" becomes
330 * "[P.A.T.H]*.*" but "[P.A.T.H]*.*" or "[P.A.T.H]*.X*" are unmodified.
331 */
332 c = newpat;
333 for (i = 0; i < patlen; pat++, i++)
334 switch (*pat) {
335 case '?':
336 *c++ = '%';
337 break;
338 case '\\':
339 i++;
340 if (i < patlen)
341 *c++ = *++pat;
342 break;
343 case '.':
344 case ']':
345 dot_in_filename = *pat == '.';
346 default:
347 *c++ = *pat;
348 break;
349 }
350 /* Check for trailing "*" and see if we need to add ".*" */
351 if (pat[-1] == '*' && !dot_in_filename) {
352 *c++ = '.';
353 *c++ = '*';
354 }
355 len = c - newpat;
356
357 /* Pattern may not exceed 255 characters */
358 if (len > 255) {
359 gs_free_object(mem, newpat, "GP_ENUM(pattern)");
360 gs_free_object(mem, pfen, "GP_ENUM(file_enum)");
361 return (file_enum *) 0;
362 }
363 pfen->context = 0;
364 pfen->length = patlen;
365 pfen->pattern.dsc$w_length = len;
366 pfen->pattern.dsc$b_dtype = DSC$K_DTYPE_T;
367 pfen->pattern.dsc$b_class = DSC$K_CLASS_S;
368 pfen->pattern.dsc$a_pointer = newpat;
369 pfen->memory = mem;
370
371 return pfen;
372 }
373
374 /* Return the next file name in the enumeration. The client passes in */
375 /* a scratch string and a max length. If the name of the next file fits, */
376 /* the procedure returns the length. If it doesn't fit, the procedure */
377 /* returns max length +1. If there are no more files, the procedure */
378 /* returns -1. */
379
380 uint
gp_enumerate_files_next(file_enum * pfen,char * ptr,uint maxlen)381 gp_enumerate_files_next(file_enum * pfen, char *ptr, uint maxlen)
382 {
383 char *c, filnam[NAM$C_MAXRSS];
384 descrip result =
385 {NAM$C_MAXRSS, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
386 uint i, len;
387
388 result.dsc$a_pointer = filnam;
389
390 /* Find the next file which matches the pattern */
391 i = LIB$FIND_FILE(&pfen->pattern, &result, &pfen->context,
392 (descrip *) 0, (descrip *) 0, (uint *) 0, (uint *) 0);
393
394 /* Check the return status */
395 if (RMS_IS_ERROR_OR_NMF(i)) {
396 gp_free_enumeration(pfen);
397 return (uint)(-1);
398 } else if ((len = strlength(filnam, NAM$C_MAXRSS, ' ')) > maxlen)
399 return maxlen + 1;
400
401 /* Copy the returned filename over to the input string ptr */
402 c = ptr;
403 for (i = 0; i < len; i++)
404 *c++ = filnam[i];
405
406 return len;
407 }
408
409 /* Clean up a file enumeration. This is only called to abandon */
410 /* an enumeration partway through: ...next should do it if there are */
411 /* no more files to enumerate. This should deallocate the file_enum */
412 /* structure and any subsidiary structures, strings, buffers, etc. */
413
414 void
gp_enumerate_files_close(file_enum * pfen)415 gp_enumerate_files_close(file_enum * pfen)
416 {
417 gp_free_enumeration(pfen);
418 }
419
420 const char *
gp_strerror(int errnum)421 gp_strerror(int errnum)
422 {
423 return NULL;
424 }
425
426 /* -------------- Helpers for gp_file_name_combine_generic ------------- */
427
gp_file_name_root(const char * fname,uint len)428 uint gp_file_name_root(const char *fname, uint len)
429 {
430 /*
431 * The root for device:[root.][directory.subdirectory]filename.extension;version
432 * is device:[root.][
433 * The root for device:[directory.subdirectory]filename.extension;version
434 * is device:[
435 * The root for logical:filename.extension;version
436 * is logical:
437 */
438 int i, j;
439
440 if (len == 0)
441 return 0;
442 /* Search for ':' */
443 for (i = 0; i < len; i++)
444 if (fname[i] == ':')
445 break;
446 if (i == len)
447 return 0; /* No root. */
448 if (fname[i] == ':')
449 i++;
450 if (i == len || fname[i] != '[')
451 return i;
452 /* Search for ']' */
453 i++;
454 for (j = i; j < len; j++)
455 if (fname[j] == ']')
456 break;
457 if (j == len)
458 return i; /* No ']'. Allowed as a Ghostscript specifics. */
459 j++;
460 if (j == len)
461 return i; /* Appending "device:[directory.subdirectory]" with "filename.extension;version". */
462 if (fname[j] != '[')
463 return i; /* Can't append anything, but pass through for checking an absolute path. */
464 return j + 1; /* device:[root.][ */
465 }
466
gs_file_name_check_separator(const char * fname,int len,const char * item)467 uint gs_file_name_check_separator(const char *fname, int len, const char *item)
468 {
469 if (len > 0) {
470 /*
471 * Ghostscript specifics : an extended syntax like Mac OS.
472 * We intentionally don't consider ':' and '[' as separators
473 * in forward search, see gp_file_name_combine.
474 */
475 if (fname[0] == ']')
476 return 1; /* It is a file separator. */
477 if (fname[0] == '.')
478 return 1; /* It is a directory separator. */
479 if (fname[0] == '-') {
480 if (fname == item + 1 && item[0] == '-')
481 return 1; /* Two or more parents, cut the first one. */
482 return 1;
483 }
484 } else if (len < 0) {
485 if (fname[-1] == '.' || fname[-1] == ':' || fname[-1] == '[')
486 return 1;
487 }
488 return 0;
489 }
490
gp_file_name_is_parent(const char * fname,uint len)491 bool gp_file_name_is_parent(const char *fname, uint len)
492 { /* Ghostscript specifics : an extended syntax like Mac OS. */
493 return len == 1 && fname[0] == '-';
494 }
495
gp_file_name_is_current(const char * fname,uint len)496 bool gp_file_name_is_current(const char *fname, uint len)
497 { /* Ghostscript specifics : an extended syntax like Mac OS. */
498 return len == 0;
499 }
500
gp_file_name_separator(void)501 const char *gp_file_name_separator(void)
502 { return "]";
503 }
504
gp_file_name_directory_separator(void)505 const char *gp_file_name_directory_separator(void)
506 { return ".";
507 }
508
gp_file_name_parent(void)509 const char *gp_file_name_parent(void)
510 { return "-";
511 }
512
gp_file_name_current(void)513 const char *gp_file_name_current(void)
514 { return "";
515 }
516
gp_file_name_is_partent_allowed(void)517 bool gp_file_name_is_partent_allowed(void)
518 { return false;
519 }
520
gp_file_name_is_empty_item_meanful(void)521 bool gp_file_name_is_empty_item_meanful(void)
522 { return true;
523 }
524
525 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)526 gp_file_name_combine(const char *prefix, uint plen, const char *fname, uint flen,
527 bool no_sibling, char *buffer, uint *blen)
528 {
529 /*
530 * Reduce it to the general case.
531 *
532 * Implementation restriction : fname must not contain a part of
533 * "device:[root.]["
534 */
535 uint rlen, flen1 = flen, plen1 = plen;
536 const char *fname1 = fname;
537
538 if ( plen > 0 && prefix[plen-1] == '\0' )
539 plen--;
540
541 if (plen == 0 && flen == 0) {
542 /* Not sure that we need this case. */
543 if (*blen == 0)
544 return gp_combine_small_buffer;
545 buffer[0] = '.';
546 *blen = 1;
547 }
548 rlen = gp_file_name_root(fname, flen);
549 if (rlen > 0 || plen == 0 || flen == 0) {
550 if (rlen == 0 && plen != 0) {
551 fname1 = prefix;
552 flen1 = plen;
553 }
554 if (flen1 + 1 > *blen)
555 return gp_combine_small_buffer;
556 memcpy(buffer, fname1, flen1);
557 buffer[flen1] = 0;
558 *blen = flen1;
559 return gp_combine_success;
560 }
561
562 if ( prefix[plen - 1] == ']' && fname[ 0 ] == '-' )
563 {
564 memcpy(buffer, prefix, plen - 1 );
565 fname1 = fname + 1;
566 flen1 = flen - 1;
567 memcpy(buffer + plen - 1 , fname1, flen1);
568 memcpy(buffer + plen + flen1 - 1 , "]" , 1 );
569 buffer[plen + flen1] = 0;
570 *blen = plen + flen1;
571 return gp_combine_success;
572 }
573
574 if ( prefix[plen - 1] == ':' || (prefix[plen - 1] == ']' &&
575 memchr(fname, ']', flen) == 0) )
576 {
577 /* Just concatenate. */
578 if (plen + flen + 1 > *blen)
579 return gp_combine_small_buffer;
580 memcpy(buffer, prefix, plen);
581 memcpy(buffer + plen, fname, flen);
582 buffer[plen + flen] = 0;
583 *blen = plen + flen;
584 return gp_combine_success;
585 }
586 if ( memchr( prefix , '[' , plen ) == 0 &&
587 memchr( prefix , '.' , plen ) == 0 )
588 {
589 char* tmp_prefix;
590 int tmp_plen;
591
592 if ( prefix[0] == '/' )
593 {
594 tmp_prefix = prefix + 1;
595 tmp_plen = plen - 1;
596 }
597 else
598 {
599 tmp_prefix = prefix;
600 tmp_plen = plen;
601 }
602 if ( tmp_plen + flen + 2 > *blen)
603 return gp_combine_small_buffer;
604 memcpy(buffer, tmp_prefix, tmp_plen);
605 memcpy(buffer + tmp_plen , ":" , 1 );
606 memcpy(buffer + tmp_plen + 1, fname, flen);
607 if ( memchr( fname , '.' , flen ) != 0 )
608 {
609 buffer[ tmp_plen + flen + 1] = 0;
610 *blen = tmp_plen + flen + 1;
611 }
612 else
613 {
614 memcpy(buffer + tmp_plen + flen + 1 , "." , 1 );
615 buffer[ tmp_plen + flen + 2] = 0;
616 *blen = tmp_plen + flen + 2;
617 }
618 return gp_combine_success;
619 }
620 if (prefix[plen - 1] != ']' && fname[0] == '[')
621 return gp_combine_cant_handle;
622 /* Unclose "][" :*/
623 if (fname[0] == '[') {
624 fname1 = fname + 1;
625 flen1 = flen - 1;
626 }
627 if (prefix[plen - 1] == ']')
628 plen1 = plen - 1;
629 return gp_file_name_combine_generic(prefix, plen1,
630 fname1, flen1, no_sibling, buffer, blen);
631 }
632
633 /* ------ Font enumeration ------ */
634
635 /* This is used to query the native os for a list of font names and
636 * corresponding paths. The general idea is to save the hassle of
637 * building a custom fontmap file.
638 */
639
gp_enumerate_fonts_init(gs_memory_t * mem)640 void *gp_enumerate_fonts_init(gs_memory_t *mem)
641 {
642 return NULL;
643 }
644
gp_enumerate_fonts_next(void * enum_state,char ** fontname,char ** path)645 int gp_enumerate_fonts_next(void *enum_state, char **fontname, char **path)
646 {
647 return 0;
648 }
649
gp_enumerate_fonts_free(void * enum_state)650 void gp_enumerate_fonts_free(void *enum_state)
651 {
652 }
653
654 /* --------- 64 bit file access ----------- */
655 /* fixme: Not implemented yet.
656 * Currently we stub it with 32 bits access.
657 */
658
gp_fopen_64(const char * filename,const char * mode)659 FILE *gp_fopen_64(const char *filename, const char *mode)
660 {
661 return fopen(filename, mode);
662 }
663
gp_open_scratch_file_64(const char * prefix,char fname[gp_file_name_sizeof],const char * mode)664 FILE *gp_open_scratch_file_64(const char *prefix,
665 char fname[gp_file_name_sizeof],
666 const char *mode)
667 {
668 return gp_open_scratch_file(prefix, fname, mode);
669 }
670
gp_open_printer_64(char fname[gp_file_name_sizeof],int binary_mode)671 FILE *gp_open_printer_64(char fname[gp_file_name_sizeof], int binary_mode)
672 {
673 return gp_open_printer(fname, binary_mode);
674 }
675
gp_ftell_64(FILE * strm)676 int64_t gp_ftell_64(FILE *strm)
677 {
678 return ftell(strm);
679 }
680
gp_fseek_64(FILE * strm,int64_t offset,int origin)681 int gp_fseek_64(FILE *strm, int64_t offset, int origin)
682 {
683 long offset1 = (long)offset;
684
685 if (offset != offset1)
686 return -1;
687 return fseek(strm, offset1, origin);
688 }
689