📄 streamdb.c
字号:
/**** streamdb.c ****//*********************************************************************** Copyright (c) 1991, 1992 Iris Computing Laboratories.** This software is provided for demonstration purposes only. As* freely-distributed, modifiable source code, this software carries* absolutely no warranty. Iris Computing Laboratories disclaims* all warranties for this software, including any implied warranties* of merchantability and fitness, and shall not be liable for* damages of any type resulting from its use.* Permission to use, copy, modify, and distribute this source code* for any purpose and without fee is hereby granted, provided that* the above copyright and this permission notice appear in all copies* and supporting documentation, and provided that Iris Computing* Laboratories not be named in advertising or publicity pertaining* to the redistribution of this software without specific, written* prior permission.**********************************************************************//*************************************************************************A `StreamDB' object encapsulates the data and operations required toprovide basic creation, deletion, storage, and manipulation of a byte-oriented stream database. During processing, it is organized as adoubly-linked list where each element in the list points to a stringthat contains the text for one database entry. A database is createdwith streamdb_create(), which takes a filename as one of its arguments.Also, the application can specify the delimiter that serves as aseparator between database entries in the file.streamdb_insert() adds entries to the database and streamdb_delete()removes entries. Deletion must be done by position or with the specialconstant `streamdb_CURRENT'. With insertions, you can specify aposition or one of two special constants `streamdb_CURRENT' and`streamdb_APPEND'. Position references are zero-based, as with arraysin C. Note that you can't insert at/before the current position untilyou establish a current entry with streamdb_set_current().In addition to inserting and deleting database entries, you can loada database from a disk file with streamdb_load() and save a databaseto disk with either streamdb_save() or streamdb_save_alt(). Theformer save operation uses the filename specified in streamdb_create()and updates the file modification status, while the latter takes analternate filename as an argument and does not update the filemodification status.You can create database entries with either NULL text fields or nullstrings for their text. This allows a two-stage operation for(1) creating an entry, and (2) setting its text field to a legitimatevalue. During save operations, however, entries with NULL text fieldsor zero-length text fields will be skipped.There are several convenience functions for getting and settingcurrent entries, and text fields and for retrieving the first andlast entries in the doubly-linked list.Sample usage:void main(argc, argv)int argc;char *argv[];{ StreamDB db; EntryDB current; db = streamdb_create("teststream.db", "####\n"); current = streamdb_insert(db, "third entry\n", streamdb_APPEND); streamdb_set_current(db, current); streamdb_insert(db, "first entry\n", 0); streamdb_insert(db, "second entry\n", streamdb_CURRENT); streamdb_insert(db, "fourth entry\n", 3); streamdb_save(db); printf("Database modified? %s\n", streamdb_modified(db) ? "Yes" : "No"); streamdb_delete(db, 0); streamdb_delete(db, 2); streamdb_save_alt(db, "teststream.alt"); printf("Database modified? %s\n", streamdb_modified(db) ? "Yes" : "No"); streamdb_destroy(db); exit(0);}*************************************************************************/#include "streamdb.h"/*Private globals using by the sorting functions only:*/static EntryDB first_entry, last_entry;static int num_nodes;/*Private support functions:*/static int build_list_from_buffer();static EntryDB get_new_node();static int offset_without_delimiter();static int load_buffer();static int read_from_disk();static int write_to_disk();/*static int file_exists(); *** not currently used */static int file_size();static int scan_for_next_entry();static int build_search_tree();static int insert_into_tree();static int go_left();static void traverse_tree();static void destroy_tree();static void update_linked_list();/*Public functions:*//*streamdb_create() allocates a data structure that holdsinformation about an active stream-oriented database.*/StreamDB streamdb_create(filename, delimiter)char *filename, *delimiter;{ StreamDB sObject; if (!(sObject = (StreamDB) malloc(sizeof(_StreamDB)))) return NULL; sObject->self = sObject; if (!filename) *sObject->filename = EOS; else strncpy(sObject->filename, filename, streamdb_MAX_FILENAME_SPEC); sObject->file_modified = FALSE; sObject->num_entries = 0; sObject->delimiter_len = strlen(delimiter); sObject->first = sObject->last = sObject->current = NULL; sObject->delimiter = delimiter; if (!delimiter || !*delimiter) { free(sObject->self); return NULL; } else return sObject;} /* streamdb_create *//*streamdb_destroy() frees the storage for a `StreamDB' object.*/void streamdb_destroy(sObject)StreamDB sObject;{ if (sObject->first) { EntryDB entry = sObject->first; while (entry) { if (entry->text) free(entry->text); sObject->first = entry; /* borrow `first' for temp. storage */ entry = entry->next; free(sObject->first); } } free(sObject->self);} /* streamdb_destroy *//*streamdb_modified() returns TRUE or FALSE depending on whetheror not the stream has been modified since the most recentload/save operation.*/int streamdb_modified(sObject)StreamDB sObject;{ return sObject->file_modified;} /* streamdb_modified *//*streamdb_get_num_entries() returns the total number of entriesin the database -- the number of nodes in the linked list.*/int streamdb_get_num_entries(sObject)StreamDB sObject;{ return sObject->num_entries;} /* streamdb_get_num_entries *//*streamdb_get_first() returns a pointer tothe list of entries.*/EntryDB streamdb_get_first(sObject)StreamDB sObject;{ return sObject->first;} /* streamdb_get_first *//*streamdb_get_last() returns a pointer tothe tail of the list of entries.*/EntryDB streamdb_get_last(sObject)StreamDB sObject;{ return sObject->last;} /* streamdb_get_last *//*streamdb_get_previous() returns a pointer tothe previous entry.*//*ARGSUSED*/EntryDB streamdb_get_previous(sObject, entry)StreamDB sObject; /* not currently used */EntryDB entry;{ return (entry) ? entry->previous : NULL;} /* streamdb_get_previous *//*streamdb_get_next() returns a pointer tothe next entry.*//*ARGSUSED*/EntryDB streamdb_get_next(sObject, entry)StreamDB sObject; /* not currently used */EntryDB entry;{ return (entry) ? entry->next : NULL;} /* streamdb_get_next *//*streamdb_get_entry_by_position() returns a pointer tothe entry in the specified position--zero-based.*/EntryDB streamdb_get_entry_by_position(sObject, position)StreamDB sObject;int position;{ EntryDB entry = sObject->first; if (position < 0 || position >= sObject->num_entries) return NULL; while (entry && position--) entry = entry->next; return entry;} /* streamdb_get_entry_by_position *//*streamdb_get_current() returns a pointer tothe current entry.*/EntryDB streamdb_get_current(sObject)StreamDB sObject;{ return sObject->current;} /* streamdb_get_current *//*streamdb_set_current() updates the pointer tothe current entry.*/EntryDB streamdb_set_current(sObject, entry)StreamDB sObject;EntryDB entry;{ return sObject->current = entry;} /* streamdb_set_current *//*streamdb_get_current_text() returns a pointer tothe text for the current entry.*/char *streamdb_get_current_text(sObject)StreamDB sObject;{ if (!sObject->current) return NULL; return sObject->current->text;} /* streamdb_get_current_text *//*streamdb_set_current_text() returns a pointer tothe new text for the current entry. `new_text' canbe NULL, and `new_text' can point to a null string.*/char *streamdb_set_current_text(sObject, new_text)StreamDB sObject;char *new_text;{ char *text; if (!sObject->current) return NULL; sObject->file_modified = TRUE; if ((text = sObject->current->text) != NULL) free(text); if (!new_text) return sObject->current->text = NULL; else if ((text = (char *) malloc((unsigned) (strlen(new_text) + 1))) != NULL) strcpy(text, new_text); return sObject->current->text = text;} /* streamdb_set_current_text *//*streamdb_get_entry_text() returns a pointer tothe text for the current entry.*//*ARGSUSED*/char *streamdb_get_entry_text(sObject, entry)StreamDB sObject; /* not currently used */EntryDB entry;{ return (entry) ? entry->text : NULL;} /* streamdb_get_entry_text *//*streamdb_create_solitaire() returns a pointer to astandalone copy of an existing entry. This functionis useful in providing undo services, i.e., you canmake a copy of an entry before deleting it. It isnot part of the database structure.*/EntryDB streamdb_create_solitaire(sObject, entry)StreamDB sObject;EntryDB entry;{ EntryDB new_entry; if (!entry) return NULL; if ((new_entry = get_new_node()) == NULL) return NULL; if (!entry->text) new_entry->text = NULL; else { if ((new_entry->text = (char *) malloc((unsigned) (strlen(entry->text) + 1))) == NULL) { streamdb_free_solitaire(sObject, new_entry); return NULL; } } strcpy(new_entry->text, entry->text); new_entry->previous = entry->previous; new_entry->next = entry->next; return new_entry;} /* streamdb_create_solitaire *//*streamdb_free_solitaire() deletes the storage for astandalone database node.*//*ARGSUSED*/void streamdb_free_solitaire(sObject, entry)StreamDB sObject; /* not currently used */EntryDB entry;{ if (!entry) return; if (entry->text) free(entry->text); free(entry);} /* streamdb_free_solitaire *//*streamdb_load() oversees the loading of the database's textinto a list of dynamically-allocated buffers, one for eachentry. It returns a pointer to the head of the list.*/EntryDB streamdb_load(sObject)StreamDB sObject;{ int size; char *buffer; if ((size = file_size(sObject->filename)) == streamdb_NO_FILE) return NULL; if ((buffer = (char *) malloc((unsigned) (size + 1))) == NULL) return NULL; if (!(size = load_buffer(sObject, buffer))) return NULL; if (build_list_from_buffer(sObject, buffer, size)) { free(buffer); return sObject->first; } else { free(buffer); return NULL; }} /* streamdb_load *//*streamdb_save() oversees the storage of a memory-resident,stream-oriented database as a text file. It returns TRUE ifthe disk write operation succeeded.*/int streamdb_save(sObject)StreamDB sObject;{ if (!sObject->file_modified) return FALSE; if (!write_to_disk(sObject, sObject->filename)) return FALSE; sObject->file_modified = FALSE; return TRUE;} /* streamdb_save *//*streamdb_save_as() oversees the storage of a memory-resident,stream-oriented database as a named text file. It returns TRUEif the disk write operation succeeded.*/int streamdb_save_as(sObject, filename)StreamDB sObject;char *filename;{ if (!write_to_disk(sObject, filename)) return FALSE; sObject->file_modified = FALSE; streamdb_set_filename(sObject, filename); return TRUE;} /* streamdb_save_as *//*streamdb_save_alt() oversees the storage of a memory-resident,stream-oriented database as a text file. It returns TRUE ifthe disk write operation succeeded. This save function takesa filename allowing you to save the database to an alternatefile, and IT DOES NOT UPDATE THE FILE MODIFICATION STATUS.*/int streamdb_save_alt(sObject, filename)StreamDB sObject;char *filename;{ return write_to_disk(sObject, filename);} /* streamdb_save_alt *//*streamdb_set_filename() updates the filename for theexisting stream database.*/char *streamdb_set_filename(sObject, filename)StreamDB sObject;char *filename;{ strncpy(sObject->filename, filename, streamdb_MAX_FILENAME_SPEC); return sObject->filename;} /* streamdb_set_filename *//*streamdb_insert() oversees the insertion of a string intostream-oriented database. It returns a pointer to the newentry. The position of insertion is zero-based, like acharacter array. The insertion is performed like a texteditor insertion, e.g., an insertion at the current entryis placed just before the current entry and the currententry pointer is not modified. `text' can be NULL, and`text' can point to a null string.*/EntryDB streamdb_insert(sObject, text, position)StreamDB sObject;char *text;int position;{ EntryDB new_entry, entry; if (position > sObject->num_entries) return NULL; if (position == streamdb_CURRENT && !sObject->current) { if (!sObject->first) /* first entry into an empty database--allow it */; else return NULL; } if ((new_entry = get_new_node()) == NULL) return NULL; if (!text) new_entry->text = NULL; else { if ((new_entry->text = (char *) malloc((unsigned) (strlen(text) + 1))) == NULL) return NULL; strcpy(new_entry->text, text); /* could be zero-length string */ } sObject->file_modified = TRUE; entry = sObject->first; if (position == 0 || (position == streamdb_APPEND && !entry) || (position == streamdb_CURRENT && sObject->current == sObject->first)) { new_entry->next = entry; /* could be NULL */ if (entry) entry->previous = new_entry; sObject->first = new_entry; if (!entry) sObject->last = new_entry; if (position == streamdb_CURRENT && !sObject->current) sObject->current = new_entry; } else if (position == streamdb_APPEND || position == sObject->num_entries) { entry = sObject->last; entry->next = new_entry; new_entry->previous = entry; sObject->last = new_entry;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -