/*....................................................................... * Delete a hash-table. * * Input: * hash HashTable * The hash table to be deleted. * Output: * return HashTable * The deleted hash table (always NULL). */ HashTable *_del_HashTable(HashTable *hash) { if(hash) { /* * Clear and delete the bucket array. */ if(hash->bucket) { _clear_HashTable(hash); free(hash->bucket); hash->bucket = NULL; }; /* * Delete application data. */ if(hash->del_fn) hash->del_fn(hash->app_data); /* * If the hash table was allocated from an internal free-list, delete * it and the hash table by deleting the free-list. Otherwise just * return the hash-table to the external free-list. */ if(hash->internal_mem) _del_HashMemory(hash->mem, 1); else hash = (HashTable *) _del_FreeListNode(hash->mem->hash_memory, hash); }; return NULL; }
/*....................................................................... * Free a string that was previously returned by _new_StringMemString(). * * Input: * sm StringMem * The free-list from which the string was originally * allocated. * s char * The string to be returned to the free-list, or NULL. * Output: * return char * Always NULL. */ char *_del_StringMemString(StringMem *sm, char *s) { int was_malloc; /* True if the string originally came from malloc() */ /* * Is there anything to be deleted? */ if(s && sm) { /* * Retrieve the true string pointer. This is one less than the one * returned by _new_StringMemString() because the first byte of the * allocated memory is reserved by _new_StringMemString as a flag byte * to say whether the memory was allocated from the free-list or directly * from malloc(). */ s--; /* * Get the origination flag. */ was_malloc = s[0]; if(was_malloc) { free(s); s = NULL; sm->nmalloc--; } else { s = (char *) _del_FreeListNode(sm->fl, s); }; }; return NULL; }
/*....................................................................... * Private function used to delete a hash-table node. * The node must have been removed from its list before calling this * function. * * Input: * hash HashTable * The table for which the node was originally * allocated. * node HashNode * The node to be deleted. * Output: * return HashNode * The deleted node (always NULL). */ static HashNode *_del_HashNode(HashTable *hash, HashNode *node) { if(node) { node->symbol.name = _del_StringMemString(hash->mem->string_memory, node->symbol.name); /* * Call the user-supplied data-destructor if provided. */ if(node->symbol.data && node->symbol.del_fn) node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code, node->symbol.data); /* * Return the node to the free-list. */ node->next = NULL; node = (HashNode *) _del_FreeListNode(hash->mem->node_memory, node); }; return NULL; }
/*....................................................................... * Get a new directory reader object from the cache. * * Input: * ef ExpandFile * The pathname expansion resource object. * pathname const char * The pathname of the directory. * Output: * return DirNode * The cache entry of the new directory reader, * or NULL on error. On error, ef->errmsg will * contain a description of the error. */ static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname) { char *errmsg = NULL; /* An error message from a called function */ DirNode *node; /* The cache node used */ /* * Get the directory reader cache. */ DirCache *cache = &ef->cache; /* * Extend the cache if there are no free cache nodes. */ if(!cache->next) { node = (DirNode *) _new_FreeListNode(cache->mem); if(!node) { snprintf(ef->errmsg, sizeof(ef->errmsg), "Insufficient memory to open a new directory"); return NULL; }; /* * Initialize the cache node. */ node->next = NULL; node->prev = NULL; node->dr = NULL; /* * Allocate a directory reader object. */ node->dr = _new_DirReader(); if(!node->dr) { snprintf(ef->errmsg, sizeof(ef->errmsg), "Insufficient memory to open a new directory"); node = (DirNode *) _del_FreeListNode(cache->mem, node); return NULL; }; /* * Append the node to the cache list. */ node->prev = cache->tail; if(cache->tail) cache->tail->next = node; else cache->head = node; cache->next = cache->tail = node; }; /* * Get the first unused node, but don't remove it from the list yet. */ node = cache->next; /* * Attempt to open the specified directory. */ if(_dr_open_dir(node->dr, pathname, &errmsg)) { strncpy(ef->errmsg, errmsg, ERRLEN); ef->errmsg[ERRLEN] = '\0'; return NULL; }; /* * Now that we have successfully opened the specified directory, * remove the cache node from the list, and relink the list around it. */ cache->next = node->next; if(node->prev) node->prev->next = node->next; else cache->head = node->next; if(node->next) node->next->prev = node->prev; else cache->tail = node->prev; node->next = node->prev = NULL; /* * Return the successfully initialized cache node to the caller. */ return node; }
/*....................................................................... * Write as many characters as possible from the start of a character * queue via a given output callback function, removing those written * from the queue. * * Input: * cq GlCharQueue * The queue to write characters from. * write_fn GL_WRITE_FN * The function to call to output characters, * or 0 to simply discard the contents of the * queue. * data void * Anonymous data to pass to write_fn(). * Output: * return GlFlushState The status of the flush operation: * GLQ_FLUSH_DONE - The flush operation * completed successfully. * GLQ_FLUSH_AGAIN - The flush operation * couldn't be completed * on this call. Call this * function again when the * output channel can accept * further output. * GLQ_FLUSH_ERROR Unrecoverable error. */ GlqFlushState _glq_flush_queue(GlCharQueue *cq, GlWriteFn *write_fn, void *data) { /* * Check the arguments. */ if(!cq) { errno = EINVAL; return GLQ_FLUSH_ERROR; }; /* * If possible keep writing until all of the chained buffers have been * emptied and removed from the list. */ while(cq->buffers.head) { /* * Are we looking at the only node in the list? */ int is_tail = cq->buffers.head == cq->buffers.tail; /* * How many characters more than an exact multiple of the buffer-segment * size have been added to the buffer so far? */ int nmodulo = cq->ntotal % GL_CQ_SIZE; /* * How many characters of the buffer segment at the head of the list * have been used? Note that this includes any characters that have * already been flushed. Also note that if nmodulo==0, this means that * the tail buffer segment is full. The reason for this is that we * don't allocate new tail buffer segments until there is at least one * character to be added to them. */ int nhead = (!is_tail || nmodulo == 0) ? GL_CQ_SIZE : nmodulo; /* * How many characters remain to be flushed from the buffer * at the head of the list? */ int nbuff = nhead - (cq->nflush % GL_CQ_SIZE); /* * Attempt to write this number. */ int nnew = write_fn(data, cq->buffers.head->bytes + cq->nflush % GL_CQ_SIZE, nbuff); /* * Was anything written? */ if(nnew > 0) { /* * Increment the count of the number of characters that have * been flushed from the head of the queue. */ cq->nflush += nnew; /* * If we succeded in writing all of the contents of the current * buffer segment, remove it from the queue. */ if(nnew == nbuff) { /* * If we just emptied the last node left in the list, then the queue is * now empty and should be reset. */ if(is_tail) { _glq_empty_queue(cq); } else { /* * Get the node to be removed from the head of the list. */ CqCharBuff *node = cq->buffers.head; /* * Make the node that follows it the new head of the queue. */ cq->buffers.head = node->next; /* * Return it to the freelist. */ node = (CqCharBuff *) _del_FreeListNode(cq->bufmem, node); }; }; /* * If the write blocked, request that this function be called again * when space to write next becomes available. */ } else if(nnew==0) { return GLQ_FLUSH_AGAIN; /* * I/O error. */ } else { _err_record_msg(cq->err, "Error writing to terminal", END_ERR_MSG); return GLQ_FLUSH_ERROR; }; }; /* * To get here the queue must now be empty. */ return GLQ_FLUSH_DONE; }