xref: /plan9/sys/src/cmd/gs/src/gsiodisk.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2001 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: gsiodisk.c,v 1.2 2002/06/14 04:04:39 ray Exp $ */
18 /* %disk*% IODevice implementation for Ghostscript */
19 
20 /*
21  * This file contains the sources for implmenting the 'flat' disk
22  * file structure specified by Adobe.  This disk file structure is used
23  * to implement the I/O devices refered to as %disk0% to %disk9%.
24  *
25  * These 'disks' are implemented as a set of directories of the
26  * operating system.  The location of the directories on the operating
27  * system disk structure is defined by the /Root I/O device key.  Thus
28  * the following Postscript:
29  *	mark /Root (/gs/disk0) (%disk0) .putdevparams
30  * would use the /gs/disk0 directory for storing the data for the disk0
31  * iodevice.
32  *
33  * Due to conflicts between file name formats (name lengths, restricted
34  * characters, etc.) a 'mapping file' (map.txt) is used to translate
35  * between a logical file name and a real file name on the system.
36  * This name translation also flattens the directory structure of the
37  * logical file names.  (This may be changed in the future.  But for
38  * now it makes the implementation easier.)
39  *
40  * A special thanks is given for Noriyuki Matsushima for his creation of
41  * the original version of the mapping file logic.
42  */
43 
44 /*
45  * Mapping file:
46  *
47  * The mapping file is used to create a 'flat disk' file structure.  All file
48  * are stored in a single directory.  To accomplish this, the logical file
49  * name, including the path, are stored in a 'mapping file'.  For each file in
50  * flat disk, there is a single line entry in the mapping file.  The mapping
51  * file entry also includes a file number value.  This file number is used as
52  * the actual file name in the flat disk directory.
53  *
54  * The flat disk format and the mapping file are used to eliminate problems
55  * with trying to emulate a logical directory structure on different operating
56  * systems which have different rules about what is allowed and not allowed
57  * in a file name and path.
58  *
59  * Mapping file format:
60  *
61  * First line:
62  * FileVersion<tab>1<tab>This file is machine generated. No not edit.<newline>
63  *
64  * Remaining lines:
65  * <space>filenumber<tab>file_name<newline>
66  *
67  * The first line of the mapping file contains the text "FileVersion" followed
68  * by a tab character. Then the number 1 (ascii - 31 hex) followed by a tab
69  * and then a short message telling people to not edit the file.  This is an
70  * attempt to keep the mapping file from being corrupted.
71  *
72  * The file version number must be a "1".  This is the only version currently
73  * supported.  This line exists in case it is necessary to modify the mapping
74  * file format at some future date.  The presence of a version number allows
75  * for seamless upgrading of the file format.
76  *
77  * The format of the entry lines has been created so that the read entry
78  * routine would:
79  * 1. Grab the number (including the space if it has not already been read.)
80  * 2. Grab the tab
81  * 3. Grab everything up to a linefeed or carriage return as the file name.
82  * 4. Grab any remaining linefeeds/carriage returns. Note this step might
83  * grab the first character of the next line if there is only a single
84  * carriage return or linefeed.  Since it is not desireable to have to worry
85  * about doing an 'unget character', an extra space has been put at the
86  * beginning of each line.  The extra logic with the linefeed/carriage return
87  * is primarily there in case someone accidentally mangles the file by looking
88  * at it with a text editor.
89  *
90  * This basically allows for any file name except one that includes
91  * a zero, a carriage return, or a linefeed.  The requirement for excluding
92  * zero is that there are too many C string routines that assume zero is
93  * a string terminator (including much of the current Ghostscript code and
94  * the current map handler).  The carriage return/linefeed requirement is
95  * there simply because it is being used as a terminator for the file name.
96  * It could easily be eliminated by adding a zero as the name terminator.
97  * The only disadvantage is that the file is no longer totally printable ascii.
98  */
99 
100 #include "errno_.h"
101 #include "string_.h"
102 #include "unistd_.h"
103 #include "gx.h"
104 #include "gserrors.h"
105 #include "gp.h"
106 #include "gscdefs.h"
107 #include "gsparam.h"
108 #include "gsstruct.h"
109 #include "gxiodev.h"
110 #include "gsutil.h"
111 
112 /* Function prototypes */
113 private iodev_proc_init(iodev_diskn_init);
114 private iodev_proc_fopen(iodev_diskn_fopen);
115 private iodev_proc_delete_file(diskn_delete);
116 private iodev_proc_rename_file(diskn_rename);
117 private iodev_proc_file_status(diskn_status);
118 private iodev_proc_enumerate_files(diskn_enumerate_files);
119 private iodev_proc_enumerate_next(diskn_enumerate_next);
120 private iodev_proc_enumerate_close(diskn_enumerate_close);
121 private iodev_proc_get_params(diskn_get_params);
122 private iodev_proc_put_params(diskn_put_params);
123 iodev_proc_put_params(diskn_os_put_params);
124 
125 /* Define a diskn (%diskn%) device macro */
126 #define diskn(varname,diskname) \
127 const gx_io_device varname = \
128 { \
129     diskname, "FileSystem", \
130     {iodev_diskn_init, iodev_no_open_device, \
131      NULL /* no longer used */ , iodev_diskn_fopen, iodev_os_fclose, \
132      diskn_delete, diskn_rename, diskn_status, \
133      iodev_no_enumerate_files, /* Only until we have a root location */ \
134      diskn_enumerate_next, diskn_enumerate_close, \
135      diskn_get_params, diskn_put_params \
136     } \
137 }
138 
139 /*
140  * Define the disk0-6 (%diskn%) devices. These devices are
141  * used in the device list in gconf.c.
142  */
143 diskn(gs_iodev_disk0,"%disk0%");
144 diskn(gs_iodev_disk1,"%disk1%");
145 diskn(gs_iodev_disk2,"%disk2%");
146 diskn(gs_iodev_disk3,"%disk3%");
147 diskn(gs_iodev_disk4,"%disk4%");
148 diskn(gs_iodev_disk5,"%disk5%");
149 diskn(gs_iodev_disk6,"%disk6%");
150 /* We could have more disks, but the DynaLab font installer */
151 /* has problems with more than 7 disks */
152 #undef diskn
153 
154 typedef struct diskn_state_s {
155     int root_size;	    /* size of root buffer */
156     char * root;            /* root path pointer  */
157     gs_memory_t * memory;   /* memory structure used */
158 } diskn_state;
159 
160 gs_private_st_ptrs1(st_diskn_state, struct diskn_state_s, "diskn_state",
161     diskn_state_enum_ptrs, diskn_state_reloc_ptrs, root);
162 
163 #define MAP_FILE_NAME "map.txt"
164 #define TEMP_FILE_NAME "Tmp.txt"
165 #define MAP_FILE_VERSION 1
166 #define InitialNumber 0
167 #define BUFFER_LENGTH gp_file_name_sizeof
168 
169 typedef struct map_file_enum_s {
170     FILE *  stream;         /* stream to map file */
171     char *  pattern;        /* pattern pointer    */
172     char *  root;           /* root path pointer  */
173     gs_memory_t * memory;   /* memory structure used */
174 } map_file_enum;
175 
176 gs_private_st_ptrs2(st_map_file_enum, struct map_file_enum_s, "map_file_enum",
177     map_file_enum_enum_ptrs, map_file_enum_reloc_ptrs, pattern, root);
178 
179 private void * map_file_enum_init(gs_memory_t *, const char *, const char *);
180 private bool map_file_enum_next(void *, char *);
181 private void map_file_enum_close(void *);
182 private bool map_file_name_get(const char *, const char *, char *);
183 private void map_file_name_add(const char *, const char *);
184 private void map_file_name_ren(const char*, const char *, const char *);
185 private void map_file_name_del(const char *, const char *);
186 
187 private int
iodev_diskn_init(gx_io_device * iodev,gs_memory_t * mem)188 iodev_diskn_init(gx_io_device * iodev, gs_memory_t * mem)
189 {
190     diskn_state * pstate = gs_alloc_struct(mem, diskn_state, &st_diskn_state,
191 			   			"iodev_diskn_init(state)");
192     if (!pstate)
193         return gs_error_VMerror;
194     pstate->root_size = 0;
195     pstate->root = NULL;
196     pstate->memory = mem;
197     iodev->state = pstate;
198     return 0;
199 }
200 
201 
202 private int
iodev_diskn_fopen(gx_io_device * iodev,const char * fname,const char * access,FILE ** pfile,char * rfname,uint rnamelen)203 iodev_diskn_fopen(gx_io_device * iodev, const char *fname, const char *access,
204 	       FILE ** pfile, char *rfname, uint rnamelen)
205 {
206     char realname[gp_file_name_sizeof];
207     diskn_state * pstate = (diskn_state *)iodev->state;
208 
209     /* Exit if we do not have a root location */
210     if (!pstate->root)
211         return_error(gs_error_undefinedfilename);
212 
213     /* Remap filename (if it exists). */
214     if (!map_file_name_get((char *)pstate->root, fname, realname)) {
215         if (strchr(access, 'w')) {
216             map_file_name_add(pstate->root, fname);
217     	    map_file_name_get(pstate->root, fname, realname);
218 	}
219 	else
220             return_error(gs_error_undefinedfilename);
221     }
222 
223     return iodev_os_fopen(iodev_default, realname, access, pfile, rfname, rnamelen);
224 }
225 
226 private int
diskn_delete(gx_io_device * iodev,const char * fname)227 diskn_delete(gx_io_device * iodev, const char *fname)
228 {
229     char realname[gp_file_name_sizeof];
230     diskn_state * pstate = (diskn_state *)iodev->state;
231 
232     /* Exit if we do not have a root location */
233     if (!pstate->root)
234         return_error(gs_error_undefinedfilename);
235 
236     /* Map filename (if it exists). */
237     if (!map_file_name_get((char *)pstate->root, fname, realname))
238         return_error(gs_error_undefinedfilename);
239 
240     map_file_name_del((char *)pstate->root, fname);
241     return (unlink(realname) == 0 ? 0 : gs_error_ioerror);
242 }
243 
244 private int
diskn_rename(gx_io_device * iodev,const char * from,const char * to)245 diskn_rename(gx_io_device * iodev, const char *from, const char *to)
246 {
247     char toreal[gp_file_name_sizeof];
248     int code = 0;
249     diskn_state * pstate = (diskn_state *)iodev->state;
250 
251     /* Exit if we do not have a root location */
252     if (!pstate->root)
253         return_error(gs_error_undefinedfilename);
254 
255     /* if filenames are the same them we are done. */
256     if (strcmp(to, from) == 0)
257         return 0;
258 
259     /*
260      * If the destination file already exists, then we want to delete it.
261      */
262     if (map_file_name_get((char *)pstate->root, to, toreal)) {
263         map_file_name_del((char *)pstate->root, to);
264 	code = unlink(toreal) == 0 ? 0 : gs_error_ioerror;
265     }
266 
267     map_file_name_ren((char *)pstate->root, from, to);
268     return code;
269 }
270 
271 private int
diskn_status(gx_io_device * iodev,const char * fname,struct stat * pstat)272 diskn_status(gx_io_device * iodev, const char *fname, struct stat *pstat)
273 {
274     char realname[gp_file_name_sizeof];
275     diskn_state * pstate = (diskn_state *)iodev->state;
276 
277     /* Exit if we do not have a root location */
278     if (!pstate->root)
279         return_error(gs_error_undefinedfilename);
280 
281     /* Map filename (if it exists). */
282     if (!map_file_name_get((char *)pstate->root, fname, realname))
283         return_error(gs_error_undefinedfilename);
284 
285     return (stat((char *)realname, pstat) < 0 ? gs_error_undefinedfilename : 0);
286 }
287 
288 private file_enum *
diskn_enumerate_files_init(gx_io_device * iodev,const char * pat,uint patlen,gs_memory_t * mem)289 diskn_enumerate_files_init(gx_io_device * iodev, const char *pat, uint patlen,
290 	     gs_memory_t * mem)
291 {
292     char patstr[gp_file_name_sizeof];
293     diskn_state * pstate = (diskn_state *)iodev->state;
294 
295     memcpy(patstr, pat, patlen);	/* Copy string to buffer */
296     patstr[patlen]=0;			/* Terminate string */
297     return (file_enum *)map_file_enum_init(mem, (char *)pstate->root, patstr);
298 }
299 
300 private void
diskn_enumerate_close(file_enum * pfen)301 diskn_enumerate_close(file_enum *pfen)
302 {
303     map_file_enum_close((void *)pfen);
304 }
305 
306 private uint
diskn_enumerate_next(file_enum * pfen,char * ptr,uint maxlen)307 diskn_enumerate_next(file_enum *pfen, char *ptr, uint maxlen)
308 {
309     if (map_file_enum_next((void *)pfen, ptr))
310         return strlen(ptr);
311     /* If we did not find a file then clean up */
312     diskn_enumerate_close(pfen);
313     return ~(uint) 0;
314 }
315 
316 private int
diskn_get_params(gx_io_device * iodev,gs_param_list * plist)317 diskn_get_params(gx_io_device * iodev, gs_param_list * plist)
318 {
319     int code;
320     int i0 = 0, so = 1;
321     bool btrue = true, bfalse = false;
322     diskn_state * pstate = (diskn_state *)iodev->state;
323     bool bsearch = pstate->root != 0;
324     int BlockSize;
325     long Free, LogicalSize;
326     gs_param_string rootstring;
327 
328     /*
329      * Return fake values for BlockSize and Free, since we can't get the
330      * correct values in a platform-independent manner.
331      */
332     BlockSize = 1024;
333     LogicalSize = bsearch ? 2000000000 / BlockSize : 0;	/* about 2 Gb */
334     Free = LogicalSize * 3 / 4;			/* about 1.5 Gb */
335 
336     if (
337 	(code = param_write_bool(plist, "HasNames", &btrue)) < 0 ||
338 	(code = param_write_int(plist, "BlockSize", &BlockSize)) < 0 ||
339 	(code = param_write_long(plist, "Free", &Free)) < 0 ||
340 	(code = param_write_int(plist, "InitializeAction", &i0)) < 0 ||
341 	(code = param_write_bool(plist, "Mounted", &bsearch)) < 0 ||
342 	(code = param_write_bool(plist, "Removable", &bfalse)) < 0 ||
343 	(code = param_write_bool(plist, "Searchable", &bsearch)) < 0 ||
344 	(code = param_write_int(plist, "SearchOrder", &so)) < 0 ||
345 	(code = param_write_bool(plist, "Writeable", &bsearch)) < 0 ||
346 	(code = param_write_long(plist, "LogicalSize", &LogicalSize)) < 0
347 	)
348 	return code;
349 
350     if (pstate->root) {
351         rootstring.data = (const byte *)pstate->root;
352         rootstring.size = strlen(pstate->root);
353         rootstring.persistent = false;
354         return param_write_string(plist, "Root", &rootstring);
355     }
356     else {
357         return param_write_null(plist, "Root");
358     }
359 }
360 
361 private int
diskn_put_params(gx_io_device * iodev,gs_param_list * plist)362 diskn_put_params(gx_io_device *iodev, gs_param_list *plist)
363 {
364     gs_param_string rootstr;
365     int code;
366     diskn_state * pstate = (diskn_state *)iodev->state;
367 
368     switch (code = param_read_string(plist, "Root", &rootstr)) {
369 	case 0:
370 	    break;
371 	default:
372 	    param_signal_error(plist, "Root", code);
373 	case 1:
374 	    rootstr.data = 0;
375 	    break;
376     }
377 
378     /* Process the other device parameters */
379     code = iodev_no_put_params(iodev, plist);
380     if (code < 0)
381         return code;
382 
383     /* Process parameter changes */
384 
385     if (rootstr.data) {
386 	/* Make sure that we have room for the root string */
387 	if (!pstate->root || pstate->root_size <= rootstr.size) {
388 	    if (pstate->root)	/* The current storge is too small */
389 		gs_free_object(pstate->memory, pstate->root, "diskn(rootdir)");
390 	    pstate->root = (char *)gs_alloc_byte_array(pstate->memory,
391 	    		gp_file_name_sizeof, sizeof(char), "diskn(rootdir)");
392 	    if (!pstate->root)
393 		return gs_error_VMerror;
394 	    pstate->root_size = rootstr.size + 1;
395 	    /* Now allow enumeration of files on the disk */
396 	    iodev->procs.enumerate_files = diskn_enumerate_files_init;
397 	}
398 
399 	memcpy(pstate->root, rootstr.data, rootstr.size);
400 	pstate->root[rootstr.size] = 0;
401     }
402     return 0;
403 }
404 
405 /*
406  * Open the specified file with specified attributes.
407  *
408  * rootpath - Path to base disk location.
409  * filename - File name string
410  * attributes - File attributes string
411  * Returns - NULL if error, file structure pointer if no error
412  */
413 private FILE *
MapFileOpen(const char * rootpath,const char * filename,const char * attributes)414 MapFileOpen(const char * rootpath, const char * filename, const char * attributes)
415 {
416     char fullname[BUFFER_LENGTH];
417 
418     if (strlen(rootpath) + strlen(filename) >= BUFFER_LENGTH)
419         return NULL;
420     sprintf(fullname, "%s%s", rootpath, filename);
421     return gp_fopen(fullname, attributes);
422 }
423 
424 /*
425  * Read map file version (first line)
426  *
427  * mapfile - Mapping file structure pointer
428  * value - pointer to version number storage location
429  * Returns 1 if data read, else 0
430  */
431 private int
MapFileReadVersion(FILE * mapfile,int * value)432 MapFileReadVersion(FILE * mapfile, int * value)
433 {
434     int code = fscanf(mapfile, "FileVersion\t%d\t", value) == 1 ? 1 : 0;
435     int c;
436 
437     /* Skip comment on version line. */
438     do {
439 	c = fgetc(mapfile);
440     } while (c != EOF && c != '\n' && c != '\r');
441 
442     /* Clean up any trailing linefeeds or carriage returns */
443     while (c != EOF && (c == '\n' || c == '\r')) {
444 	c = fgetc(mapfile);
445     }
446     return code;
447 }
448 
449 /*
450  * Write a map file version (first line) into the map file
451  *
452  * stream - File structure pointer
453  * value - version number
454  */
455 private void
MapFileWriteVersion(FILE * mapfile,int value)456 MapFileWriteVersion(FILE * mapfile, int value)
457 {
458     fprintf(mapfile,
459     	"FileVersion\t%d\tThis file is machine generated.  Do not edit.\n",
460     	value);
461 }
462 
463 /*
464  * Read an entry in the map file
465  *
466  * mapfile - Mapping file structure pointer
467  * namebuf - Buffer for the file name storage
468  * value - pointer to file number storage location
469  * Returns 1 if data read, else 0
470  */
471 private int
MapFileRead(FILE * mapfile,char * namebuf,int * value)472 MapFileRead(FILE * mapfile, char * namebuf, int * value)
473 {
474     int count = 0;
475     int c;
476 
477     /* Get the file number */
478     if (fscanf(mapfile, "%d\t", value) != 1)
479     	return 0;
480 
481     /* Get the file name */
482     do {
483 	namebuf[count++] = c = fgetc(mapfile);
484     } while (count < BUFFER_LENGTH && c != EOF && c != '\n' && c != '\r');
485     namebuf[--count] = 0;    /* Terminate file name */
486 
487     /* Clean up any trailing linefeeds or carriage returns */
488     while (c != EOF && (c == '\n' || c == '\r')) {
489 	c = fgetc(mapfile);
490     }
491 
492     return count != 0 ? 1: 0;
493 }
494 
495 /*
496  * Write an entry in the map file
497  *
498  * stream - File structure pointer
499  * namebuf - Buffer for the file name storage
500  * value - file number
501  */
502 private void
MapFileWrite(FILE * mapfile,const char * namebuf,int value)503 MapFileWrite(FILE * mapfile, const char * namebuf, int value)
504 {
505     fprintf(mapfile, " %d\t%s\n", value, namebuf);
506 }
507 
508 /*
509  * Remove the specified file
510  *
511  * rootpath - Path to base disk location.
512  * filename - File name string
513  */
514 private void
MapFileUnlink(const char * rootpath,const char * filename)515 MapFileUnlink(const char * rootpath, const char * filename)
516 {
517     char fullname[BUFFER_LENGTH];
518 
519     if (strlen(rootpath) + strlen(filename) >= BUFFER_LENGTH)
520         return;
521     sprintf(fullname, "%s%s", rootpath, filename);
522     unlink(fullname);
523 }
524 
525 /*
526  * Rename the specified file to new specified name
527  *
528  * rootpath - Path to base disk location.
529  * oldfilename - Old file name string
530  * newfilename - New file name string
531  */
532 private void
MapFileRename(const char * rootpath,const char * newfilename,const char * oldfilename)533 MapFileRename(const char * rootpath, const char * newfilename, const char * oldfilename)
534 {
535     char oldfullname[BUFFER_LENGTH];
536     char newfullname[BUFFER_LENGTH];
537 
538     if (strlen(rootpath) + strlen(oldfilename) >= BUFFER_LENGTH)
539         return;
540     if (strlen(rootpath) + strlen(newfilename) >= BUFFER_LENGTH)
541         return;
542     sprintf(oldfullname, "%s%s", rootpath, oldfilename);
543     sprintf(newfullname, "%s%s", rootpath, newfilename);
544     rename(oldfullname, newfullname);
545 }
546 
547 /*
548  * MapToFile
549  *
550  * Search for mapped number from map file with requied name. If same name exist,
551  * first (lowest number) will be returned.
552  * rootpath        char*   string of the base path where in disk0 reside (C:\PS\Disk0\)
553  * name            char*   string to search pattern for full match (font\Ryumin-Light)
554  * returns	    -1 if file not found, file number if found.
555  */
556 private int
MapToFile(const char * rootpath,const char * name)557 MapToFile(const char* rootpath, const char* name)
558 {
559     FILE * mapfile;
560     int d = -1;
561     char filename[BUFFER_LENGTH];
562     int file_version;
563 
564     mapfile = MapFileOpen(rootpath, MAP_FILE_NAME, "r");
565     if (mapfile == NULL)
566         return -1;
567 
568     /* Verify the mapping file version number */
569 
570     if (MapFileReadVersion(mapfile, &file_version)
571     	&& file_version == MAP_FILE_VERSION) {
572 
573         /* Scan the file looking for the given name */
574 
575         while (MapFileRead(mapfile, filename, &d)) {
576             if (strcmp(filename, name) == 0)
577     	        break;
578             d = -1;
579         }
580     }
581     fclose(mapfile);
582     return d;
583 }
584 
585 /*
586  * map_file_enum_init
587  *
588  * create enumiate structure for a mapped file and fill pattern for search
589  * root_name       char*   string of the base path where in disk0 reside (e.g. C:\PS\Disk0\)
590  * search_pattern  char*   string to search pattern (font\*) or full match
591  *                          (e.g. font\Ryumin-Light) or NULL
592  *                          NULL means all files
593  * Returns:		    NULL if error, else pointer to enumeration structure.
594  */
595 private void *
map_file_enum_init(gs_memory_t * mem,const char * root_name,const char * search_pattern)596 map_file_enum_init(gs_memory_t * mem, const char * root_name, const char * search_pattern)
597 {
598     int file_version;
599     map_file_enum * mapfileenum = gs_alloc_struct(mem, map_file_enum, &st_map_file_enum,
600 			   				"diskn:enum_init(file_enum)");
601 
602     if (mapfileenum == NULL)
603         return NULL;
604     memset(mapfileenum, 0, sizeof(map_file_enum));
605     mapfileenum->memory = mem;
606 
607     if (search_pattern) {
608         mapfileenum->pattern = (char *)gs_alloc_bytes(mem, strlen(search_pattern) + 1,
609 							"diskn:enum_init(pattern)");
610         if (mapfileenum->pattern == NULL) {
611 	    map_file_enum_close((file_enum *) mapfileenum);
612             return NULL;
613         }
614         strcpy(mapfileenum->pattern, search_pattern);
615     }
616 
617     mapfileenum->root = (char *)gs_alloc_bytes(mem, strlen(root_name) + 1,
618     						"diskn:enum_init(root)");
619     if (mapfileenum->root == NULL) {
620 	map_file_enum_close((file_enum *) mapfileenum);
621         return NULL;
622     }
623 
624     if (strlen(root_name) >= BUFFER_LENGTH)
625         return NULL;
626     strcpy(mapfileenum->root, root_name);
627     mapfileenum->stream = MapFileOpen(root_name, MAP_FILE_NAME, "r");
628 
629     /* Check the mapping file version number */
630     if (mapfileenum->stream != NULL
631 	&& (!MapFileReadVersion(mapfileenum->stream, &file_version)
632 	    || file_version != MAP_FILE_VERSION)) {
633 	fclose(mapfileenum->stream);   /* Invalid file version */
634         mapfileenum->stream = NULL;
635     }
636 
637     return mapfileenum;
638 }
639 
640 /*
641  * map_file_enum_next
642  *
643  * enum_mem        void*   pointer for map file enum structure
644  * search_pattern  char*   string array for next target
645  */
646 private bool
map_file_enum_next(void * enum_mem,char * target)647 map_file_enum_next(void * enum_mem, char* target)
648 {
649     int d = -1;
650     map_file_enum * mapfileenum;
651 
652     if (enum_mem == NULL)
653 	return false;
654 
655     mapfileenum = (map_file_enum*)enum_mem;
656     if (mapfileenum->stream == NULL)
657 	return false;
658 
659     if (mapfileenum->pattern) {
660         /*  Search for next entry that matches pattern */
661         while (MapFileRead(mapfileenum->stream, target, &d)) {
662             if (string_match((byte *)target, strlen(target),
663 			     (byte *)mapfileenum->pattern,
664 			     strlen(mapfileenum->pattern), 0))
665 		return true;
666 	}
667     }
668     else {
669         /*  Just get next */
670         if (MapFileRead(mapfileenum->stream, target, &d))
671             return true;
672     }
673     return false;
674 }
675 
676 /*
677  * map_file_enum_close
678  *
679  * cleans up after an enumeration, this may only be called
680  * if map_file_enum_init did not fail
681  */
682 private void
map_file_enum_close(void * enum_mem)683 map_file_enum_close(void * enum_mem)
684 {
685     map_file_enum * mapfileenum = (map_file_enum *) enum_mem;
686     gs_memory_t * mem = mapfileenum->memory;
687 
688     if (mapfileenum->stream)
689         fclose(mapfileenum->stream);
690     if (mapfileenum->root)
691         gs_free_object(mem, mapfileenum->root, "diskn_enum_init(root)");
692     if (mapfileenum->pattern)
693 	gs_free_object(mem, mapfileenum->pattern, "diskn_enum_init(pattern)");
694     gs_free_object(mem, mapfileenum, "diskn_enum_init(mapfileenum)");
695 }
696 
697 /*
698  * map_file_name_get
699  *
700  * maps the psname(Fname) to the osname using concatening the root_name and id
701  * Id will be a lowest one if same psname exists more than one in map file. See MapToFile
702  * for detail.
703  * root_name       char*   string of the base path where in disk0 reside
704  *                         (e.g.C:\PS\Disk0\)
705  * Fname           char*   name of the entry to find in the map
706  * osname          char*   resulting os specific path to the file
707  */
708 private bool
map_file_name_get(const char * root_name,const char * Fname,char * osname)709 map_file_name_get(const char * root_name, const char * Fname, char * osname)
710 {
711     int d = MapToFile(root_name, Fname);
712 
713     if (d != -1) {
714 	/* 20 characters are enough for even a 64 bit integer */
715         if ((strlen(root_name) + 20) < BUFFER_LENGTH) {
716 	    sprintf(osname, "%s%d", root_name, d);
717 	    return true;
718 	}
719     }
720 
721     *osname = 0;
722     return false;
723 }
724 
725 /*
726  * map_file_name_del
727  *
728  * Deletes Fname from the mapping table and does not delete the actual file
729  * If same Fname exists, all same Fname will be deleted
730  * root_name       char*   string of the base path where in disk0 reside (C:\PS\Disk0\)
731  * Fname           char*   name of the entry to add to the map
732  */
733 private void
map_file_name_del(const char * root_name,const char * Fname)734 map_file_name_del(const char * root_name, const char * Fname)
735 {
736     /*  search for target entry */
737     int d = MapToFile(root_name, Fname);
738     int file_version;
739 
740     if (d != -1) {			 /* if the file exists ... */
741         char    name[BUFFER_LENGTH];
742         FILE*   newMap;
743         FILE*   oldMap;
744 
745 	/* Open current map file and a working file */
746 
747         MapFileUnlink(root_name, TEMP_FILE_NAME );
748         newMap = MapFileOpen(root_name, TEMP_FILE_NAME, "w");
749         if (newMap == NULL)
750 	    return;
751         oldMap = MapFileOpen(root_name, MAP_FILE_NAME, "r");
752         if (oldMap != NULL && (!MapFileReadVersion(oldMap, &file_version)
753 	    || file_version != MAP_FILE_VERSION)) {
754             fclose(oldMap);
755 	    oldMap= NULL;
756 	}
757         if (oldMap == NULL) {
758             fclose(newMap);
759             MapFileUnlink(root_name, TEMP_FILE_NAME);
760 	    return;
761         }
762 
763 	/* Copy every line of the map file except the one with given name */
764 
765         MapFileWriteVersion(newMap, MAP_FILE_VERSION);
766         while (MapFileRead(oldMap, name, &d))
767             if (strcmp(name, Fname))
768                 MapFileWrite(newMap, name, d);
769         fclose(newMap);
770         fclose(oldMap);
771         MapFileUnlink(root_name, MAP_FILE_NAME);
772         MapFileRename(root_name, MAP_FILE_NAME, TEMP_FILE_NAME);
773     }
774 }
775 
776 /*
777  * map_file_add
778  *
779  * adds Fname to the mapping table and does not create an unique new empty file
780  * If same Fname exists, new Fname will not be added.
781  * root_name       char*   string of the base path where in disk0 reside (C:\PS\Disk0\)
782  * Fname           char*   name of the entry to add to the map
783  */
784 private void
map_file_name_add(const char * root_name,const char * Fname)785 map_file_name_add(const char * root_name, const char * Fname)
786 {
787     /*
788      * add entry to map file
789      * entry number is one greater than biggest number
790      */
791     char    name[BUFFER_LENGTH];
792     int d;
793     int dmax = -1;
794     int file_version;
795     FILE*   newMap;
796     FILE*   oldMap;
797 
798     oldMap = MapFileOpen(root_name, MAP_FILE_NAME, "r");
799     if (oldMap != NULL && (!MapFileReadVersion(oldMap, &file_version)
800 	|| file_version != MAP_FILE_VERSION)) {
801         fclose(oldMap);
802 	oldMap = NULL;
803     }
804     if (oldMap == NULL) {
805         oldMap = MapFileOpen(root_name, MAP_FILE_NAME, "w");
806 	if (!oldMap)
807 	    return;
808         MapFileWriteVersion(oldMap, MAP_FILE_VERSION);
809         MapFileWrite(oldMap, Fname, InitialNumber);
810         fclose(oldMap);
811     }
812     else {
813         MapFileUnlink(root_name, TEMP_FILE_NAME);
814         newMap = MapFileOpen(root_name, TEMP_FILE_NAME, "w");
815         if (newMap != NULL) {
816             MapFileWriteVersion(newMap, MAP_FILE_VERSION);
817             while (MapFileRead(oldMap, name, &d)) {
818         	MapFileWrite(newMap, name, d);
819                 if (dmax < d)
820                     dmax = d;
821             }
822 
823             dmax += 1;
824             MapFileWrite(newMap, Fname, dmax);
825             fclose(newMap);
826             fclose(oldMap);
827             MapFileUnlink(root_name, MAP_FILE_NAME);
828             MapFileRename(root_name, MAP_FILE_NAME, TEMP_FILE_NAME);
829         }
830     }
831 }
832 
833 /*
834  * map_file_name_ren
835  *
836  * renames the oldname into newname in the mapping table. newname must not exist and must be
837  * checked by the caller. If same name exist, all same name will be renamed.
838  * root_name       char*   string of the base path where in disk0 reside (C:\PS\Disk0\)
839  * oldname         char*   name currently existing in the map
840  * newname         char*   name to change the entry indicated by oldname into
841  */
842 private void
map_file_name_ren(const char * root_name,const char * oldname,const char * newname)843 map_file_name_ren(const char* root_name, const char * oldname, const char * newname)
844 {
845     /*  search for target entry */
846 
847     int d = MapToFile(root_name, oldname);
848     int file_version;
849 
850     if (d != -1) { 		/* if target exists ... */
851         char    name[BUFFER_LENGTH];
852         FILE*   newMap;
853         FILE*   oldMap;
854 
855 	/* Open current map file and a working file */
856 
857         MapFileUnlink(root_name, TEMP_FILE_NAME );
858         newMap = MapFileOpen(root_name, TEMP_FILE_NAME, "w");
859         if (newMap == NULL)
860 	    return;
861         oldMap = MapFileOpen(root_name, MAP_FILE_NAME, "r");
862         if (oldMap != NULL && (!MapFileReadVersion(oldMap, &file_version)
863 	    || file_version != MAP_FILE_VERSION)) {
864             fclose(oldMap);
865 	    oldMap= NULL;
866 	}
867         if (oldMap == NULL) {
868             fclose(newMap);
869             MapFileUnlink(root_name, TEMP_FILE_NAME);
870 	    return;
871         }
872 
873 	/* Now copy data from old to new, change file name when found */
874 
875         MapFileWriteVersion(newMap, MAP_FILE_VERSION);  /* Copy the version number */
876         while (MapFileRead(oldMap, name, &d))
877             if (strcmp(name, oldname))
878                 MapFileWrite(newMap, name, d);
879             else
880                 MapFileWrite(newMap, newname, d);
881         fclose(newMap);
882         fclose(oldMap);
883         MapFileUnlink(root_name, MAP_FILE_NAME);
884         MapFileRename(root_name, MAP_FILE_NAME, TEMP_FILE_NAME);
885     }
886 }
887