Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtHelp / il / ilefs.c
1 /* $XConsortium: ilefs.c /main/3 1995/10/23 15:47:01 rswiston $ */
2 /**---------------------------------------------------------------------
3 ***     
4 ***    (c)Copyright 1992 Hewlett-Packard Co.
5 ***    
6 ***                             RESTRICTED RIGHTS LEGEND
7 ***    Use, duplication, or disclosure by the U.S. Government is subject to
8 ***    restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in
9 ***    Technical Data and Computer Software clause in DFARS 252.227-7013.
10 ***                             Hewlett-Packard Company
11 ***                             3000 Hanover Street
12 ***                             Palo Alto, CA 94304 U.S.A.
13 ***    Rights for non-DOD U.S. Government Departments and Agencies are as set
14 ***    forth in FAR 52.227-19(c)(1,2).
15 ***
16 ***-------------------------------------------------------------------*/
17
18 #include <string.h>
19
20 #include "ilint.h"
21 #include "ilcontext.h"
22 #include "ilefs.h"
23 #include "ilpipeint.h"
24 #include "ilerrors.h"
25
26     /*  Beginning of file and file type recs - separate so list heads are smaller. */
27 typedef struct {
28     ilObjectRec             o;                  /* std header: MUST BE FIRST */
29     struct _ilEFSFileTypeRec *pNext, *pPrev;    /* forward / back ptrs */
30     } ilEFSHeaderRec, *ilEFSHeaderPtr;
31
32     /*  Private definition of an ilEFSFileType: an IL object. */
33 typedef struct _ilEFSFileTypeRec {
34     ilEFSHeaderRec          h;                  /* header: MUST BE FIRST */
35     ilEFSFileTypeInfo       info;               /* public file type info */
36     } ilEFSFileTypeRec, *ilEFSFileTypePtr;
37
38
39     /*  EFS private data, hung off of context.pAlloc[IL_CONTEXT_ALLOC_EFS]. */
40 typedef struct {
41     ilEFSHeaderRec          fileTypeHead;       /* list of active file types */
42     ilEFSHeaderRec          replacedFileTypeHead; /* list of replaced file types */
43     } ilEFSPrivateRec, *ilEFSPrivatePtr;
44
45
46     /*  Private definition of an ilEFSFile: an IL object. */
47 typedef struct {
48     ilObjectRec             o;                  /* std header: MUST BE FIRST */
49     ilEFSFileInfo           info;               /* public file type info */
50     ilEFSFileTypePtr        pFileType;          /* convenience: ptr to file type */
51     } ilEFSFileRec, *ilEFSFilePtr;
52
53
54     /*  Add _pFileType in front of element _pNextFileType */
55 #define LINK_FILE_TYPE(_pFileType, _pNextFileType) { \
56     (_pFileType)->h.pNext = (_pNextFileType); \
57     (_pFileType)->h.pPrev = (_pNextFileType)->h.pPrev; \
58     (_pFileType)->h.pPrev->h.pNext = (_pFileType); \
59     (_pNextFileType)->h.pPrev = (_pFileType); \
60     }
61
62     /*  Remove the given file type from its linked list */
63 #define UNLINK_FILE_TYPE(_pFileType) { \
64     (_pFileType)->h.pPrev->h.pNext = (_pFileType)->h.pNext; \
65     (_pFileType)->h.pNext->h.pPrev = (_pFileType)->h.pPrev; \
66     }
67
68     /*  Return true if given two strings are equal */
69 #define STRING_EQUAL(str, cmpstr) (strcmp ((str), (cmpstr)) == 0)
70
71         /*  In efsinit.c :
72             Called by the IL when EFS function is called.  Calls the individual
73             Init() function for each file type to be supported by EFS.
74         */
75 extern ilBool _ilefsInitStandardFiles (
76     ilContext               context
77     );
78
79
80         /*  ------------------------ ilInitEFS ---------------------------------- */
81         /*  Init EFS if not already inited (non-null ptr off of context.)
82             Return ptr to EFS context private or null if error.
83         */
84 static ilEFSPrivatePtr ilInitEFS (
85     ilContext               context
86     )
87 {
88 register ilEFSPrivatePtr    pPriv;
89
90         /*  If EFS file type data not present malloc and zero it, and then point
91             context pAlloc to it - if not, could recurse forever.
92         */
93     context->error = IL_OK;
94     pPriv = (ilEFSPrivatePtr)((ilContextPtr)context)->pAlloc[IL_CONTEXT_ALLOC_EFS];
95     if (!pPriv) {
96         pPriv = (ilEFSPrivatePtr)IL_MALLOC (sizeof (ilEFSPrivateRec));
97         if (!pPriv) {
98             context->error = IL_ERROR_MALLOC;
99             return (ilEFSPrivatePtr)NULL;
100             }
101         ((ilContextPtr)context)->pAlloc[IL_CONTEXT_ALLOC_EFS] = (ilPtr)pPriv;
102
103             /*  Init file type list to null. */
104         pPriv->fileTypeHead.pNext = pPriv->fileTypeHead.pPrev = 
105             (ilEFSFileTypePtr)&pPriv->fileTypeHead;
106         pPriv->replacedFileTypeHead.pNext = pPriv->replacedFileTypeHead.pPrev = 
107             (ilEFSFileTypePtr)&pPriv->replacedFileTypeHead;
108
109             /*  Call to external lib(s) to callback and add each file type */
110         if (!_ilefsInitStandardFiles (context)) {
111             IL_FREE (pPriv);
112             return (ilEFSPrivatePtr)NULL;
113             }
114         }
115     return pPriv;
116 }
117
118
119 /*  ================================== FILE TYPE CODE =============================== */
120
121         /*  ------------------------ ilFindFileType ---------------------------------- */
122         /*  Find the file type with the given "name" in the list of file types whose
123             head is pointed to by "pListHead".  Return ptr to found file type or null.
124         */
125 static ilEFSFileTypePtr ilFindFileType (
126     char                   *name,
127     ilEFSFileTypePtr        pListHead
128     )
129 {
130 register ilEFSFileTypePtr   pFileType;
131
132     pFileType = pListHead->h.pNext;
133     while (pFileType != pListHead) {
134         if (STRING_EQUAL (name, pFileType->info.name))
135             return pFileType;                   /* found, EXIT */
136         pFileType = pFileType->h.pNext;
137         }
138     return (ilEFSFileTypePtr)NULL;              /* not found, return null */
139 }
140
141
142         /*  ------------------------ ilAddFileTypeToList --------------------------- */
143         /*  Add the file type pointed to by "pFileType" to the list of file types whose
144             head is pointed to by "pListHead".  Element is placed in list based its
145             checkOrder.
146         */
147 static void ilAddFileTypeToList (
148     register ilEFSFileTypePtr pFileType,
149     ilEFSFileTypePtr        pListHead
150     )
151 {
152 register ilEFSFileTypePtr   pListFileType;
153 register int                checkOrder;
154
155     checkOrder = pFileType->info.checkOrder;
156     pListFileType = pListHead->h.pNext;
157     while (pListFileType != pListHead) {
158         if (checkOrder > pListFileType->info.checkOrder)
159             break;                              /* spot found; break */
160         pListFileType = pListFileType->h.pNext;
161         }
162     LINK_FILE_TYPE (pFileType, pListFileType)   /* insert in front of pListFileType */
163 }
164
165
166         /*  ------------------------ ilEFSAddFileType ---------------------------- */
167         /*  Public function: see spec.
168         */
169
170         /*  Object Destroy() function for file type objects. */
171 static void ilDestroyFileType (
172     register ilEFSFileTypePtr pFileType
173     )
174 {
175 ilEFSPrivatePtr             pPriv;
176 register ilEFSFileTypePtr   pReplaced;
177
178     pPriv = (ilEFSPrivatePtr)
179         ((ilContextPtr)(pFileType->h.o.p.context))->pAlloc[IL_CONTEXT_ALLOC_EFS];
180
181         /*  Remove file type from its current list.  Search the "replaced" list for a
182             file type of same name; if found, move it from replaced to active list.
183         */
184     UNLINK_FILE_TYPE (pFileType)
185     pReplaced = ilFindFileType (pFileType->info.name, 
186                                 (ilEFSFileTypePtr)&pPriv->replacedFileTypeHead);
187     if (pReplaced) {
188         UNLINK_FILE_TYPE (pReplaced)
189         ilAddFileTypeToList (pReplaced, (ilEFSFileTypePtr)&pPriv->fileTypeHead);
190         }
191 }
192
193
194 ilEFSFileType ilEFSAddFileType (
195     ilContext               context,
196     register ilEFSFileTypeInfo *pInfo,
197     void                   *pOptions
198     )
199 {
200 ilEFSPrivatePtr             pPriv;
201 int                         i;
202 register ilEFSFileTypePtr   pFileType, pReplace;
203 register unsigned long      openModes;
204
205         /* masks for defined bits in ilEFSFileTypeInfo - others invalid */
206 #define OPEN_MODE_MASKS (1<<IL_EFS_READ | 1<<IL_EFS_READ_SEQUENTIAL | 1<<IL_EFS_WRITE)
207 #define ATTRIBUTES_MASKS (IL_EFS_MULTI_PAGE_READS | IL_EFS_MULTI_PAGE_WRITES | \
208                           IL_EFS_MASK_READS | IL_EFS_MASK_WRITES | IL_EFS_SCALEABLE_READS)
209
210     if (pOptions) {
211         context->error = IL_ERROR_PAR_NOT_ZERO;
212         return (ilEFSFileType)NULL;
213         }
214
215         /*  Validate *pInfo: strings present, unused mask bits zero, functions right. */
216     if ((strlen (pInfo->name) > (IL_EFS_MAX_NAME_CHARS - 1))
217      || (strlen (pInfo->displayName) > (IL_EFS_MAX_DISPLAY_NAME_CHARS - 1))
218      || (pInfo->nExtensions > IL_EFS_MAX_EXTENSIONS))
219         goto badFileTypeInfo;
220
221     for (i = 0; i < pInfo->nExtensions; i++)
222         if (strlen (pInfo->extensions[i]) > (IL_EFS_MAX_EXTENSION_CHARS - 1))
223             goto badFileTypeInfo;
224
225     openModes = pInfo->openModes;
226     if ((openModes & ~OPEN_MODE_MASKS)
227      || (pInfo->attributes & ~ATTRIBUTES_MASKS))
228         goto badFileTypeInfo;
229
230     if (((!pInfo->Open || !pInfo->Open) && openModes)
231      || (!pInfo->Seek && (openModes & 1<<IL_EFS_READ) 
232            && (pInfo->attributes & IL_EFS_MULTI_PAGE_READS))
233      || ((!pInfo->GetPageInfo || !pInfo->ReadImage) 
234           && (openModes & (1<<IL_EFS_READ | 1<<IL_EFS_READ_SEQUENTIAL)))
235      || (!pInfo->WriteImage && (openModes & 1<<IL_EFS_WRITE)))
236         goto badFileTypeInfo;
237
238     for (i = 0; i < IL_EFS_TYPE_RESERVED_SIZE; i++)
239         if (pInfo->reserved[i] != 0)
240             goto badFileTypeInfo;
241
242         /*  Init EFS file types if not already inited. */
243     if (!(pPriv = ilInitEFS (context))) 
244         return (ilEFSFileType)NULL;
245
246         /*  Create a file type object, exit if failure. Set *pInfo into it. */
247     pFileType = (ilEFSFileTypePtr)_ilCreateObject (context, IL_EFS_FILE_TYPE, 
248                 ilDestroyFileType, sizeof (ilEFSFileTypeRec));
249     if (!pFileType) return (ilEFSFileType)NULL;
250     pFileType->info = *pInfo;
251
252         /*  Search the active list for file type with same name.  If found, remove it
253             from active list and add to replaced list, in front of any with same name.
254         */
255     pReplace = ilFindFileType (pFileType->info.name, 
256                                (ilEFSFileTypePtr)&pPriv->fileTypeHead);
257     if (pReplace) {
258         ilEFSFileTypePtr pReplaceFront;
259         UNLINK_FILE_TYPE (pReplace)
260         pReplaceFront = ilFindFileType (pFileType->info.name, 
261                                         (ilEFSFileTypePtr)&pPriv->fileTypeHead);
262         if (!pReplaceFront)
263             pReplaceFront = (ilEFSFileTypePtr)&pPriv->fileTypeHead;
264         LINK_FILE_TYPE (pReplace, pReplaceFront)
265         }
266
267         /*  Add the file type to the active list */
268     ilAddFileTypeToList (pFileType, (ilEFSFileTypePtr)&pPriv->fileTypeHead);
269
270     context->error = IL_OK;
271     return (ilEFSFileType)pFileType;
272
273         /*  goto point if invalid file type info: return error. */
274 badFileTypeInfo:
275     context->error = IL_ERROR_EFS_FILE_TYPE_INFO;
276     return (ilEFSFileType)NULL;
277 }
278
279
280         /*  ------------------------ ilEFSGetFileTypeInfo ---------------------------- */
281         /*  Public function: see spec.
282         */
283 ilBool ilEFSGetFileTypeInfo (
284     ilEFSFileType           fileType,
285     ilEFSFileTypeInfo      *pInfo
286     )
287 {
288 register ilEFSFileTypePtr   pFileType;
289
290     pFileType = (ilEFSFileTypePtr)fileType;
291     if (pFileType->h.o.p.objectType != IL_EFS_FILE_TYPE) {
292         pFileType->h.o.p.context->error = IL_ERROR_OBJECT_TYPE;
293         return FALSE;
294         }
295
296     *pInfo = pFileType->info;
297     pFileType->h.o.p.context->error = IL_OK;
298     return TRUE;
299 }
300
301
302         /*  ------------------------ ilEFSListFileTypes ------------------------------ */
303         /*  Public function: see spec.
304         */
305 ilBool ilEFSListFileTypes (
306     ilContext               context,
307      int           *pNFileTypes,
308     ilEFSFileType         **pfileTypes
309     )
310 {
311 ilEFSPrivatePtr             pPriv;
312 register int                nFileTypes;
313 register ilEFSFileTypePtr   pFileType;
314 register ilEFSFileType     *pfileType;
315
316     *pNFileTypes = 0;
317     *pfileTypes = (ilEFSFileType *)NULL;
318
319         /*  Init EFS file types if not already inited. */
320     if (!(pPriv = ilInitEFS (context)))
321         return FALSE;
322
323         /*  Count the number of file types in the list */
324     nFileTypes = 0;
325     pFileType = pPriv->fileTypeHead.pNext;
326     while (pFileType != (ilEFSFileTypePtr)&pPriv->fileTypeHead) {
327         pFileType = pFileType->h.pNext;
328         nFileTypes++;
329         }
330
331         /*  Malloc space for returned file types - use malloc(); caller uses free().
332             If no file types, still malloc something to return non-null ptr.
333         */
334     pfileType = (ilEFSFileType *)malloc ((nFileTypes > 0) ? 
335                                           nFileTypes * sizeof (ilEFSFileType) : 4);
336     if (!pfileType) {
337         context->error = IL_ERROR_MALLOC;
338         return FALSE;
339         }
340
341         /*  Return # of file types; traverse list to return ptrs to them. */
342     *pfileTypes = pfileType;
343     *pNFileTypes = nFileTypes;
344     pFileType = pPriv->fileTypeHead.pNext;
345     while (nFileTypes-- > 0) {
346         *pfileType++ = (ilEFSFileType)pFileType;
347         pFileType = pFileType->h.pNext;
348         }
349
350     context->error = IL_OK;
351     return TRUE;
352 }
353
354
355 /*  =================================== FILE CODE ================================== */
356
357
358         /*  --------------------------- ilEFSOpenFile ------------------------------- */
359         /*  Public function: see spec.
360         */
361
362         /*  Object Destroy() function for file objects. */
363 static void ilDestroyFile (
364     ilEFSFilePtr            pFile
365     )
366 {
367 register ilEFSFileTypePtr   pFileType;
368
369         /*  Get ptr to file type; if null, file not actually open yet; skip Close() */
370     pFileType = (ilEFSFileTypePtr)pFile->info.fileType;
371     if (pFileType)
372         (*pFileType->info.Close) (pFile);
373 }
374
375
376 ilEFSFile ilEFSOpen (
377     ilContext               context,
378     char                   *fileName,
379     unsigned int            openMode,
380     unsigned long           searchOptions,
381     char                   *typeName,
382     void                   *pOptions
383     )
384 {
385 ilEFSPrivatePtr             pPriv;
386 register ilEFSFilePtr       pFile;
387 register ilEFSFileTypePtr   pFileType, pListHead;
388 ilBool                      readOpen;
389 long                        nPages;
390 ilPtr                       pOpenPriv;
391 char                        extension [IL_EFS_MAX_EXTENSION_CHARS];
392
393         /*  Validate pOptions, openMode (set readOpen true if a read). */
394     context->error = IL_OK;
395     if (pOptions) {
396         context->error = IL_ERROR_PAR_NOT_ZERO;
397         return (ilEFSFileType)NULL;
398         }
399     switch (openMode) {
400       case IL_EFS_READ: 
401       case IL_EFS_READ_SEQUENTIAL: 
402           readOpen = TRUE; break;
403       case IL_EFS_WRITE: 
404           readOpen = FALSE; break;
405       default:
406         context->error = IL_ERROR_EFS_OPEN_MODE;
407         return (ilEFSFile)NULL;
408         }
409
410         /*  Init EFS file types if not already inited. */
411     if (!(pPriv = ilInitEFS (context)))
412         return (ilEFSFile)NULL;
413
414         /*  Add a file type object - goto openError to destroy it if an error later. */
415     pFile = (ilEFSFilePtr)_ilCreateObject (context, IL_EFS_FILE, ilDestroyFile, 
416                                           sizeof (ilEFSFileRec));
417     if (!pFile) return (ilEFSFile)NULL;
418
419         /*  Find pFileType for this file, searches enabled by mask in searchOptions.
420             First try typeName, if non-null.  When found at any point: if openMode
421             not supported, error - except for checking mode - else call Open() for
422             the file type.  If it returns any error other than "not mine" abort.
423         */
424     pFileType = (ilEFSFileTypePtr)NULL;
425     pListHead = (ilEFSFileTypePtr)&pPriv->fileTypeHead;
426     if (typeName && (searchOptions & IL_EFS_BY_TYPE_NAME)) {
427         pFileType = ilFindFileType (typeName, pListHead);
428         if (pFileType) {                                    /* file type found */
429             if (!(pFileType->info.openModes & (1 << openMode))) {
430                 context->error = IL_ERROR_EFS_OPEN_MODE;
431                 goto openError;
432                 }
433             pOpenPriv = (*pFileType->info.Open) (pFileType, fileName, openMode, &nPages);
434             if (!pOpenPriv) {
435                 if (context->error == IL_ERROR_EFS_NOT_MINE)
436                     pFileType = (ilEFSFileTypePtr)NULL;     /* try next search method */
437                 else goto openError;
438                 }
439             pFile->info.howFound = IL_EFS_BY_CHECKING;
440             }
441         }   /* END open by type name */
442
443
444         /*  If not found, search for extension if enabled. */
445     if (!pFileType && (searchOptions & IL_EFS_BY_EXTENSION)) {
446         char                       *pExtension;
447         register ilEFSFileTypePtr   pSearch;
448         int                         nChars;
449
450         pExtension = strrchr (fileName, '.');
451         if (pExtension) {                               /* is a "." in fileName */
452             pExtension++;                               /* point past "." */
453             nChars = strlen (pExtension);
454             if (nChars > (IL_EFS_MAX_EXTENSION_CHARS - 1))
455                 nChars = IL_EFS_MAX_EXTENSION_CHARS - 1;
456             bcopy (pExtension, extension, nChars);      /* extract "extension" */
457             extension [nChars] = 0;
458
459                 /* Search list for extension match until pFileType found or list done */
460             pSearch = pListHead->h.pNext;
461             while (!pFileType && (pSearch != pListHead)) {
462                 register int nExtensions = pSearch->info.nExtensions;
463                 while (nExtensions-- > 0)
464                     if (STRING_EQUAL (extension, pSearch->info.extensions[nExtensions])) {
465                         pFileType = pSearch;            /* extension found; quit */
466                         pFile->info.howFound = IL_EFS_BY_CHECKING;
467                         break;
468                         }
469                 pSearch = pSearch->h.pNext;
470                 }
471             if (pFileType) {
472                 if (!(pFileType->info.openModes & (1 << openMode))) {
473                     context->error = IL_ERROR_EFS_OPEN_MODE;
474                     goto openError;
475                     }
476                 pOpenPriv = (*pFileType->info.Open) (pFileType, fileName, openMode, &nPages);
477                 if (!pOpenPriv) {
478                     if (context->error == IL_ERROR_EFS_NOT_MINE)
479                         pFileType = (ilEFSFileTypePtr)NULL;
480                     else goto openError;
481                     }
482                 }
483             }   /* END have extension */
484         }       /* END open by extension */
485
486
487         /*  If not found, search by checking if a read openMode.   For each file type,
488             try open if enabled (checkOrder != 0) and openMode supported for file type.
489         */
490     if (!pFileType && readOpen && (searchOptions & IL_EFS_BY_CHECKING)) {
491         register ilEFSFileTypePtr   pSearch;
492
493         pSearch = pListHead->h.pNext;
494         while (pSearch != pListHead) {
495             if (pSearch->info.checkOrder && (pSearch->info.openModes & (1 << openMode))) {
496                 pOpenPriv = (*pSearch->info.Open) (pSearch, fileName, openMode, &nPages);
497                 if (pOpenPriv) {
498                     pFileType = pSearch;                /* found right file type; break */
499                     pFile->info.howFound = IL_EFS_BY_CHECKING;
500                     break;
501                     }
502                 else if (context->error != IL_ERROR_EFS_NOT_MINE)
503                     goto openError;                     /* some error; give up */
504                 }
505             pSearch = pSearch->h.pNext;
506             }
507         }   /* END open by checking */
508
509         /*  If not found above, error: can't find file type handler for this file. */
510     if (!pFileType) {
511         context->error = IL_ERROR_EFS_NO_FILE_TYPE;
512         goto openError;
513         }
514
515         /*  File type found.  Fill in info for the file, return pFile.  The object's
516             pPrivate is set to the ptr returned from file type's Open().
517         */
518     pFile->o.p.pPrivate = pOpenPriv;
519     pFile->info.fileType = (ilEFSFileType)pFileType;
520     pFile->info.openMode = openMode;
521     pFile->info.attributes = pFileType->info.attributes;
522     pFile->info.nPages = nPages;
523     pFile->pFileType = pFileType;
524
525     context->error = IL_OK;
526     return (ilEFSFile)pFile;                            /* success; return file */
527
528         /*  openError: goto here on error after file object created. The Open() was not
529             successful, so set fileType to null so ilDestroyFile() does not call Close().
530         */
531 openError:
532 {   ilError     error;
533     error = context->error;                 /* save error code */
534     pFile->info.fileType = (ilEFSFileType)NULL;
535     ilDestroyObject ((ilObject)pFile);
536     context->error = error;                 /* restore error code */
537     return (ilEFSFile)NULL;
538 }
539 }
540
541
542         /*  ------------------------ ilEFSGetFileInfo ---------------------------- */
543         /*  Public function: see spec.
544         */
545 ilBool ilEFSGetFileInfo (
546     ilEFSFile               file,
547     ilEFSFileInfo          *pInfo                   /* RETURNED */
548     )
549 {
550 register ilEFSFilePtr       pFile;
551
552     pFile = (ilEFSFilePtr)file;
553     if (pFile->o.p.objectType != IL_EFS_FILE) {
554         pFile->o.p.context->error = IL_ERROR_OBJECT_TYPE;
555         return FALSE;
556         }
557
558         /*  Return file info; fill in "inUse": file in use if refCount > 1 */
559     *pInfo = pFile->info;
560     pInfo->inUse = (pFile->o.refCount > 1);
561     pFile->o.p.context->error = IL_OK;
562     return TRUE;
563 }
564
565
566         /*  ------------------------ ilEFSSeek ---------------------------- */
567         /*  Public function: see spec.
568         */
569 ilBool ilEFSSeek (
570     ilEFSFile               file,
571     long                    page,
572     void                   *pOptions
573     )
574 {
575 register ilEFSFilePtr       pFile;
576
577     pFile = (ilEFSFilePtr)file;
578     if (pOptions) {
579         pFile->o.p.context->error = IL_ERROR_PAR_NOT_ZERO;
580         return FALSE;
581         }
582     if (pFile->o.p.objectType != IL_EFS_FILE) {
583         pFile->o.p.context->error = IL_ERROR_OBJECT_TYPE;
584         return FALSE;
585         }
586
587         /*  Validate that file was opened for random read; error if not. */
588     if (pFile->info.openMode != IL_EFS_READ) {
589         pFile->o.p.context->error = IL_ERROR_EFS_OPEN_MODE;
590         return FALSE;
591         }
592
593         /*  Call Seek() only if multi-page reads supported; not error if not. */
594     pFile->o.p.context->error = IL_OK;
595     if (!(pFile->info.attributes & IL_EFS_MULTI_PAGE_READS))
596         return TRUE;
597     else return (*pFile->pFileType->info.Seek) (file, page);
598 }
599
600
601         /*  ------------------------ ilEFSGetPageInfo ---------------------------- */
602         /*  Public function: see spec.
603         */
604 ilBool ilEFSGetPageInfo (
605     ilEFSFile               file,
606     ilEFSPageInfo          *pInfo
607     )
608 {
609 register ilEFSFilePtr       pFile;
610
611     pFile = (ilEFSFilePtr)file;
612     if (pFile->o.p.objectType != IL_EFS_FILE) {
613         pFile->o.p.context->error = IL_ERROR_OBJECT_TYPE;
614         return FALSE;
615         }
616
617         /*  Validate that file was opened for read; call GetPageInfo() if so. */
618     if ((pFile->info.openMode != IL_EFS_READ) 
619      && (pFile->info.openMode != IL_EFS_READ_SEQUENTIAL)) {
620         pFile->o.p.context->error = IL_ERROR_EFS_OPEN_MODE;
621         return FALSE;
622         }
623
624     pFile->o.p.context->error = IL_OK;
625     return (*pFile->pFileType->info.GetPageInfo) (file, pInfo);
626 }
627
628
629         /*  ------------------------ ilEFSReadImage ---------------------------- */
630         /*  Public function: see spec.
631         */
632 ilBool ilEFSReadImage (
633     ilPipe                  pipe,
634     ilEFSFile               file,
635     unsigned int            readMode,
636     long                    width,
637     long                    height,
638     void                   *pOptions
639     )
640 {
641 register ilEFSFilePtr       pFile;
642
643     pFile = (ilEFSFilePtr)file;
644     if (pOptions) {
645         pFile->o.p.context->error = IL_ERROR_PAR_NOT_ZERO;
646         return FALSE;
647         }
648     if ((pipe->objectType != IL_PIPE) || (pFile->o.p.objectType != IL_EFS_FILE)) {
649         pFile->o.p.context->error = IL_ERROR_OBJECT_TYPE;
650         return FALSE;
651         }
652
653         /*  Validate readMode: mask allowed only if supported by file type. */
654     switch (readMode) {
655       case IL_EFS_READ_MAIN:
656         break;
657       case IL_EFS_READ_MASK:
658         if (pFile->pFileType->info.attributes & IL_EFS_MASK_READS)
659             break;                                      /* else fall thru for error */
660       default:
661         pFile->o.p.context->error = IL_ERROR_EFS_READ_MODE;
662         return FALSE;
663         }
664
665         /*  Validate that file was opened for read. */
666     if ((pFile->info.openMode != IL_EFS_READ) 
667      && (pFile->info.openMode != IL_EFS_READ_SEQUENTIAL)) {
668         pFile->o.p.context->error = IL_ERROR_EFS_OPEN_MODE;
669         return FALSE;
670         }
671
672         /*  If pipe element added successfully inc file's refCount to mark in use,
673             and add file to list of objects to be destroyed when pipe emptied.
674         */
675     pFile->o.p.context->error = IL_OK;
676     if ((*pFile->pFileType->info.ReadImage) (pipe, file, readMode, width, height)) {
677         pFile->o.refCount++;
678         return _ilAddPipeDestroyObject (pipe, (ilObject)pFile);
679         }
680     else return FALSE;
681 }
682
683
684         /*  ------------------------ ilEFSWriteImage ---------------------------- */
685         /*  Public function: see spec.
686         */
687 ilBool ilEFSWriteImage (
688     ilPipe                  pipe,
689     ilEFSFile               file,
690     long                    xRes,
691     long                    yRes,
692     ilClientImage           maskImage,
693     void                   *pOptions
694     )
695 {
696 register ilEFSFilePtr       pFile;
697
698     pFile = (ilEFSFilePtr)file;
699     if (pOptions) {
700         pFile->o.p.context->error = IL_ERROR_PAR_NOT_ZERO;
701         return FALSE;
702         }
703     if ((pipe->objectType != IL_PIPE) || (pFile->o.p.objectType != IL_EFS_FILE)) {
704         pFile->o.p.context->error = IL_ERROR_OBJECT_TYPE;
705         return FALSE;
706         }
707
708         /*  Validate that file was opened for write. */
709     if (pFile->info.openMode != IL_EFS_WRITE) {
710         pFile->o.p.context->error = IL_ERROR_EFS_OPEN_MODE;
711         return FALSE;
712         }
713
714         /*  If file type doesnt handle masks, ignore maskImage, else validate it */
715     if (maskImage) {
716         if (!(pFile->pFileType->info.attributes & IL_EFS_MASK_WRITES))
717             maskImage = (ilClientImage)NULL;
718         else {
719             ilImageInfo            *pInfo;
720             register const ilImageDes    *pDes;
721             register const ilImageFormat *pFormat;
722
723             if (!ilQueryClientImage (maskImage, &pInfo, 0))
724                 return FALSE;
725             pDes = pInfo->pDes;
726             pFormat = pInfo->pFormat;
727             if ((pDes->compression != IL_UNCOMPRESSED)
728              || (pDes->nSamplesPerPixel != 1)
729              || (pFormat->rowBitAlign != 32)
730              || (pFormat->nBitsPerSample[0] != 1)) {
731                 pFile->o.p.context->error = IL_ERROR_EFS_OPEN_MODE;
732                 return FALSE;
733                 }
734             }
735         }
736
737         /*  If pipe element added successfully inc file's refCount to mark in use,
738             and add file/maskImage to list of objects to be destroyed when pipe emptied.
739         */
740     pFile->o.p.context->error = IL_OK;
741     if ((*pFile->pFileType->info.WriteImage) (pipe, file, xRes, yRes, maskImage)) {
742         if (maskImage) {
743             ((ilObjectPtr)maskImage)->refCount++;
744             _ilAddPipeDestroyObject (pipe, maskImage);
745             }
746         pFile->o.refCount++;
747         return _ilAddPipeDestroyObject (pipe, (ilObject)pFile);
748         }
749     else return FALSE;
750 }
751