/* * fman.c - file manager for dynamic opening/closing files * * Programmmed by Hirotsugu Kakugawa, Hiroshima University * E-Mail: h.kakugawa@computer.org * * Edition History * 6 May 1995 * 15 May 1995 Improved finding free fds. Added VFFM_UnIntern(). * 15 Feb 1996 Added VFFM_Intern2() (for sony font class). * 12 Mar 1996 Added VFFM_Deinit(). */ /* This file is part of VFlib * * Copyright (C) 1995-1998 Hirotsugu KAKUGAWA. All rights reserved. * * This file is part of the VFlib Library. This library is free * software; you can redistribute it and/or modify it under the terms of * the GNU Library General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your * option) any later version. This library is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Library General Public License for more details. * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "config.h" #include "defs.h" #include "_VF.h" #include "VF.h" Import int VF_MaxOpenFontFiles; Private fm_tbl FM_Table[MAX_FONT_FILES+1]; #define SENTINEL_PORT MAX_FONT_FILES Private int InitCache(); Private int CacheCheck(); Private int CacheIt(); Private char *Dum_str = "!@#$%^&We don't use such a file name!@#$%^&"; Private int Dum_open(); Private int Dum_close(); Private int VFFM_InternDummy(); Private int VFFM_Internal_Close(); Private int VFFM_Internal_Open(); /* (INTERNAL USE ONLY) */ int VFFM_Init() { FILE_Port port; if (VF_MaxOpenFontFiles >= MAX_FONT_FILES) return FALSE; for (port = 0; port < MAX_FONT_FILES; port++){ FM_Table[port].opened = FALSE; FM_Table[port].path = NULL; FM_Table[port].uu.stream = (FILE*) NULL; FM_Table[port].open_fun = NULL; FM_Table[port].close_fun = NULL; } FM_Table[SENTINEL_PORT].path = NULL; InitCache(VF_MaxOpenFontFiles, HASH_SIZE); /* Allocate file descriptors in advance */ if (! VFFM_InternDummy()) return FALSE; return TRUE; } /* (INTERNAL USE ONLY) */ int VFFM_Deinit() { FILE_Port port; #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf("VFFM_Deinit()\n"); #endif for (port = 0; port < MAX_FONT_FILES; port++){ if (FM_Table[port].opened == TRUE){ if (FM_Table[port].close_fun != NULL){ (FM_Table[port].close_fun)(port, &FM_Table[port]); } else { #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf("VFFM_Internal_Close (VFFM_Deinit()): %s\n", FM_Table[port].path); #endif fclose(FM_Table[port].uu.stream); } } if (FM_Table[port].path != NULL){ if (Dum_str != FM_Table[port].path) free(FM_Table[port].path); } FM_Table[port].path = NULL; FM_Table[port].opened = FALSE; } return TRUE; } Private int VFFM_InternDummy() { FILE_Port port; for (port = 0; port < VF_MaxOpenFontFiles; port++){ if (port >= MAX_FONT_FILES) break; FM_Table[port].opened = FALSE; FM_Table[port].uu.fd = -1; FM_Table[port].path = Dum_str; FM_Table[port].open_fun = Dum_open; FM_Table[port].close_fun = Dum_close; if (CacheIt(port) < 0) return FALSE; } return TRUE; } Private int Dum_open(port, ftp) FILE_Port port; fm_tbl *ftp; { int fd; #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf("Dum_open: %d\n", (int)port); #endif if ((fd = dup(0)) < 0) return -1; #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf(" fd= %d\n", fd); #endif ftp->uu.fd = fd; return 1; } Private int Dum_close(port, ftp) FILE_Port port; fm_tbl *ftp; { #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf("Dum_close: %d\n", (int)port); #endif close(ftp->uu.fd); ftp->uu.fd = -1; return 1; } static FILE_Port ___VFFM_Intern(); /* (INTERNAL USE ONLY) */ FILE_Port VFFM_Intern(path, open_fun, close_fun) char *path; int (*open_fun)(); int (*close_fun)(); { return ___VFFM_Intern(path, open_fun, close_fun, 0); } /* (INTERNAL USE ONLY) */ FILE_Port VFFM_InternInt(path, open_fun, close_fun) char *path; int (*open_fun)(); int (*close_fun)(); { return ___VFFM_Intern(path, open_fun, close_fun, 1); } static FILE_Port ___VFFM_Intern(path, open_fun, close_fun, type) char *path; int (*open_fun)(); int (*close_fun)(); int type; { FILE_Port port, tport; /* fixed by kei */ char *path_str; #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf("Intern: %s\n", path); #endif /* Check whether the same path is interned or not */ for (port = 0; port < SENTINEL_PORT; port++){ if (FM_Table[port].path == NULL) continue; if (strcmp(path, FM_Table[port].path) == 0 && FM_Table[port].opened == TRUE) return port; } /* Find a free slot */ for (port = 0; port < SENTINEL_PORT; port++){ if (FM_Table[port].path == NULL) break; } if (port == SENTINEL_PORT) return NULL_PORT; if ((path_str = malloc(strlen(path)+1)) == NULL) return NULL_PORT; strcpy(path_str, path); FM_Table[port].opened = FALSE; FM_Table[port].path = path_str; if (type == 0) FM_Table[port].uu.stream = NULL; else FM_Table[port].uu.fd = -1; FM_Table[port].open_fun = open_fun; FM_Table[port].close_fun = close_fun; tport = port; /* fixed by kei */ if (type == 0){ if (VFFM_FStream(port) == (FILE*)NULL) port = NULL_PORT; } else { if (VFFM_FInt(port) < 0) port = NULL_PORT; } if (port == NULL_PORT){ free(FM_Table[tport].path); /* fixed by kei */ FM_Table[tport].path = NULL; /* fixed by kei */ } return port; } /* (INTERNAL USE ONLY) */ FILE* VFFM_FStream(port) int port; { if (! CacheCheck(port)) CacheIt(port); return FM_Table[port].uu.stream; } /* (INTERNAL USE ONLY) */ int VFFM_FInt(port) int port; { if (! CacheCheck(port)) CacheIt(port); return FM_Table[port].uu.fd; } /* (INTERNAL USE ONLY) */ int VFFM_UnIntern(port) FILE_Port port; { #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf("UnIntern: %d\n", (int)port); #endif if (! CacheCheck(port)) return TRUE; /* the file is not opened */ (void) VFFM_Internal_Close(port); if ((FM_Table[port].path != NULL) && (FM_Table[port].path != Dum_str)) free(FM_Table[port].path); /* open dummy */ FM_Table[port].opened = FALSE; FM_Table[port].path = Dum_str; FM_Table[port].uu.fd = -1; FM_Table[port].open_fun = Dum_open; FM_Table[port].close_fun = Dum_close; if (CacheIt(port) < 0) return FALSE; return TRUE; } /* Called by Cache Man as a default open_fun */ Private int VFFM_Internal_Open(port) int port; { int val; FM_Table[port].opened = FALSE; if (FM_Table[port].open_fun != NULL){ val = (FM_Table[port].open_fun)(port, &FM_Table[port]); if (val >= 0) FM_Table[port].opened = TRUE; return val; } #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf("VFFM_Internal_Open: %s\n", FM_Table[port].path); #endif FM_Table[port].uu.stream = fopen(FM_Table[port].path, "rb"); if (FM_Table[port].uu.stream != NULL){ FM_Table[port].opened = TRUE; return 0; } return -1; } /* Called by Cache Man as a default close_fun. */ Private int VFFM_Internal_Close(port) FILE_Port port; { FM_Table[port].opened = FALSE; if (FM_Table[port].close_fun != NULL) return (FM_Table[port].close_fun)(port, &FM_Table[port]); #ifdef DYNAMIC_OPEN_CLOSE_DEBUG printf("VFFM_Internal_Close: %s\n", FM_Table[port].path); #endif fclose(FM_Table[port].uu.stream); FM_Table[port].uu.stream = (FILE*)NULL; return TRUE; } /* ------------------------ */ /** ** Cache **/ struct s_cache { struct s_cache *h_forw; /* forward in hash table */ struct s_cache *h_back; /* backward in hash table */ struct s_cache *l_forw; /* forward in LRU list */ struct s_cache *l_back; /* backward in LRU list */ struct s_cache *free; /* free list */ int port; }; typedef struct s_cache CACHE; typedef int CACHE_ID; Private CACHE *StreamCache; Private int CacheSize; Private CACHE *HashTable; Private int HashSize; Private CACHE CacheLRUList; Private CACHE CacheFreeList; #define CACHE_OBJ(cid) (&StreamCache[cid]) #define FREE_LIST() CacheFreeList.free #define CDR_FREE_LIST(x) (x)->free /* Hash Function */ #define HashFunction(port) ((port)%HashSize) Private CACHE *RequireCache(); Private int LRUMoveTop(); Private int LRUPutTop(); Private int LRUPutTop2(); Private int LRUDeleteTail(); Private void ReturnCache(); Private CACHE *HashIsInterned(); Private int HashInternIt(); Private int HashUninternIt(); /* * InitCache() - Init cache. Must specify cache size and hash table size. * Returns FALSE if out of memory or other fatal error. * Returns TRUE otherwise. */ Private int InitCache(cache_size, hash_size) int cache_size, hash_size; { int i; CACHE *cptr; CacheSize = cache_size; HashSize = hash_size; /* CACHE */ if (cache_size < 1){ fprintf(stderr, "VFlib: InitCache() - Cache size is too small\n"); return FALSE; } StreamCache = (CACHE*)calloc(CacheSize, sizeof(CACHE)); if (StreamCache == (CACHE*)NULL) return FALSE; /* FREE LIST */ FREE_LIST() = cptr = &StreamCache[0]; for (i = 1; i < CacheSize; i++){ CDR_FREE_LIST(cptr) = &StreamCache[i]; cptr = CDR_FREE_LIST(cptr); } CDR_FREE_LIST(&StreamCache[CacheSize-1]) = (CACHE*)NULL; /* HASH */ if (hash_size < 1){ fprintf(stderr, "VFlib: InitCache() - Hash table size is too small\n"); return FALSE; } HashTable = (CACHE*)calloc(HashSize, sizeof(CACHE)); if (HashTable == (CACHE*)NULL){ free(StreamCache); return FALSE; } for (i = 0; i < HashSize; i++){ HashTable[i].h_forw = &HashTable[i]; HashTable[i].h_back = &HashTable[i]; } /* Init LRU list. */ CacheLRUList.l_forw = &CacheLRUList; CacheLRUList.l_back = &CacheLRUList; return TRUE; } /* * CacheCheck() */ Private int CacheCheck(port) FILE_Port port; { CACHE *cptr; if ((cptr = HashIsInterned(port)) == (CACHE*)NULL) return FALSE; (void) LRUMoveTop(cptr); return TRUE; } /* * CacheIt() --- cache it. The cache slot is moved into * the head of the LRU list. */ Private int CacheIt(port) int port; { CACHE *cptr; if ((cptr = RequireCache()) == (CACHE*)NULL){ fprintf(stderr, "VFlib: CacheIt() - error\n"); exit(1); } cptr->port = port; if (LRUPutTop(cptr) < 0){ ReturnCache(cptr); return -1; } HashInternIt(cptr); return 0; } /* * RequireCache() --- Returns a cache block. * 1. if there is free cache, it will be returned. * 2. if there is no free cache, old cache is discarded and reused. */ Private CACHE* RequireCache() { CACHE *cptr; if (FREE_LIST() != (CACHE*)NULL){ cptr = FREE_LIST(); FREE_LIST() = CDR_FREE_LIST(cptr); } else { (void) LRUDeleteTail(); if ((cptr = FREE_LIST()) == (CACHE*)NULL){ fprintf(stderr, "VFlib: Failed to discard old cache, FlushOldCache()\n"); exit(1); } FREE_LIST() = CDR_FREE_LIST(cptr); } return cptr; } Private void ReturnCache(cptr) CACHE *cptr; { CDR_FREE_LIST(cptr) = FREE_LIST(); FREE_LIST() = cptr; } /** ** LRU LIST **/ /* LRUMoveTop() - moves a cache block into the top of LRU list. * THE CACHE *MUST* BE IN LRU LIST. */ Private int LRUMoveTop(cptr) CACHE *cptr; { CACHE *cptr_b, *cptr_f; cptr_b = cptr->l_back; cptr_f = cptr->l_forw; cptr_b->l_forw = cptr_f; cptr_f->l_back = cptr_b; return LRUPutTop2(cptr, FALSE); } /* LRUPutTop() - puts a cache into the top of LRU list. * THE CACHE *MUST NOT* BE IN LRU LIST. */ Private int LRUPutTop(cptr) CACHE *cptr; { return LRUPutTop2(cptr, TRUE); } Private int LRUPutTop2(cptr, f) CACHE *cptr; int f; { CACHE *cptr_f; FILE_Port port; int val; cptr_f = CacheLRUList.l_forw; cptr->l_forw = cptr_f; cptr_f->l_back = cptr; cptr->l_back = &CacheLRUList; CacheLRUList.l_forw = cptr; val = 0; if (f == TRUE){ port = cptr->port; if (VFFM_Internal_Open(port) < 0) val = -1; } return val; } Private int LRUDeleteTail() { CACHE *cptr, *cptr_b; FILE_Port port; if ((cptr = CacheLRUList.l_back) == &CacheLRUList) return FALSE; cptr_b = cptr->l_back; cptr_b->l_forw = &CacheLRUList; CacheLRUList.l_back = cptr_b; (void) HashUninternIt(cptr); CDR_FREE_LIST(cptr) = FREE_LIST(); FREE_LIST() = cptr; port = cptr->port; VFFM_Internal_Close(cptr->port); return TRUE; } Private CACHE* HashIsInterned(port) int port; { CACHE *cptr, *cptr0; cptr0 = &HashTable[HashFunction(port)]; for (cptr = cptr0->h_forw; cptr != cptr0; cptr = cptr->h_forw){ if (cptr->port == port) return cptr; } return (CACHE*)NULL; } Private int HashInternIt(cptr) CACHE *cptr; { int hash; CACHE *hcptr; hash = HashFunction(cptr->port); hcptr = HashTable[hash].h_forw; cptr->h_forw = hcptr; cptr->h_back = &HashTable[hash]; hcptr->h_back = cptr; HashTable[hash].h_forw = cptr; return TRUE; } Private int HashUninternIt(cptr) CACHE *cptr; { CACHE *cptr_b, *cptr_f; cptr_b = cptr->h_back; cptr_f = cptr->h_forw; cptr_b->h_forw = cptr_f; cptr_f->h_back = cptr_b; return TRUE; } /*EOF*/