From 40be9fa39735cd4103c18eb3013eac686a02784c Mon Sep 17 00:00:00 2001 From: fred Date: Thu, 26 Sep 1996 10:34:54 +0000 Subject: [PATCH] Import of flower-1.0.0 --- flower/cursor.cc | 75 +++++++++++++ flower/cursor.inl | 100 ++++++++++++++++++ flower/dataf.cc | 125 ++++++++++++++++++++++ flower/findcurs.hh | 12 +++ flower/findcurs.inl | 37 +++++++ flower/lgetopt.cc | 214 +++++++++++++++++++++++++++++++++++++ flower/lgetopt.hh | 90 ++++++++++++++++ flower/link.hh | 38 +++++++ flower/link.inl | 100 ++++++++++++++++++ flower/list.cc | 88 ++++++++++++++++ flower/list.hh | 96 +++++++++++++++++ flower/list.inl | 144 +++++++++++++++++++++++++ flower/stringutil.hh | 245 +++++++++++++++++++++++++++++++++++++++++++ flower/textdb.cc | 23 ++++ flower/textdb.hh | 56 ++++++++++ flower/textstr.hh | 114 ++++++++++++++++++++ 16 files changed, 1557 insertions(+) create mode 100644 flower/cursor.cc create mode 100644 flower/cursor.inl create mode 100644 flower/dataf.cc create mode 100644 flower/findcurs.hh create mode 100644 flower/findcurs.inl create mode 100644 flower/lgetopt.cc create mode 100644 flower/lgetopt.hh create mode 100644 flower/link.hh create mode 100644 flower/link.inl create mode 100644 flower/list.cc create mode 100644 flower/list.hh create mode 100644 flower/list.inl create mode 100644 flower/stringutil.hh create mode 100644 flower/textdb.cc create mode 100644 flower/textdb.hh create mode 100644 flower/textstr.hh diff --git a/flower/cursor.cc b/flower/cursor.cc new file mode 100644 index 0000000000..10e90de919 --- /dev/null +++ b/flower/cursor.cc @@ -0,0 +1,75 @@ +// cursor.cc +#ifndef CURSOR_CC +#define CURSOR_CC + +#include "cursor.hh" +//#define inline +//#include "cursor.inl" +#include + +template +Cursor +Cursor::operator ++( int ) +{ + Cursor r = *this; + assert( pointer_ ); + pointer_ = pointer_->next(); + return r; +} +template +Cursor +Cursor::operator -=( int j ) +{ + while (j--) + (*this)--; + return *this; +} +template +Cursor +Cursor::operator +=( int j ) +{ + while (j++) + (*this)++; + return *this; +} + +template +Cursor +Cursor::operator --( int ) +{ + Cursor r = *this; + assert( pointer_ ); + pointer_ = pointer_->previous(); + return r; +} + +template +Cursor +Cursor::operator +( int i ) const +{ + Cursor r = *this; + + if (i<0) + return r -(-i); + + while (i--) + r++; + + return r; +} + +template +Cursor +Cursor::operator -( int i ) const +{ + Cursor r = *this; + if (i<0) + return r +(-i); + + while (i--) + r--; + + return r; +} + +#endif diff --git a/flower/cursor.inl b/flower/cursor.inl new file mode 100644 index 0000000000..0243d14a2b --- /dev/null +++ b/flower/cursor.inl @@ -0,0 +1,100 @@ + // cursor.inl +#ifndef CURSOR_INL +#define CURSOR_INL +#include +//#include "list.hh" + +template +inline +Cursor::Cursor( List& list, Link* pointer ) : + list_( list ) +{ + if ( list.size() ) + pointer_ = pointer ? pointer : list.top().pointer_; + else + pointer_ = pointer; +} + +template +inline +Cursor::Cursor( const Cursor& cursor ) : + list_( cursor.list_ ) +{ + pointer_ = cursor.pointer_; +} + +template +inline T& +Cursor::operator *() +{ + assert( pointer_ ); + return pointer_->thing(); +} + +template +Cursor +Cursor::operator =( const Cursor& c ) +{ + assert( &list_ == &c.list_ ); + pointer_ = c.pointer_; + return *this; +} + +template +inline void +Cursor::add( const T& thing ) +{ + list_.add( thing, *this ); +} + +template +inline void +Cursor::insert( const T& thing ) +{ + list_.insert( thing, *this ); +} + +template +inline void +Cursor::remove() +{ + assert( pointer_ ); + list_.remove( *this ); +} + +template +inline const List& +Cursor::list() const +{ + return list_; +} + +template +inline Link* +Cursor::pointer() +{ + return pointer_; +} + +template +inline bool +Cursor::backward() +{ + return ( pointer_ != 0 ); +} + +template +inline bool +Cursor::forward() +{ + return ( pointer_ != 0 ); +} + +template +inline bool +Cursor::ok() +{ + return ( pointer_ != 0 ); +} + +#endif \ No newline at end of file diff --git a/flower/dataf.cc b/flower/dataf.cc new file mode 100644 index 0000000000..e53c716d8b --- /dev/null +++ b/flower/dataf.cc @@ -0,0 +1,125 @@ +#include +#include + +#include "textstr.hh" +Text_stream::Text_stream(String fn) +{ + if (fn == "") + { + name = ""; + f = stdin; + } + + else + { + name = fn; + f = fopen(fn, "r"); + } + + if (!f) { + cerr <<__FUNCTION__<< ": can't open `" << fn << "'\n"; + exit(1); + } + + line_no = 1; + } + +void +Text_stream::message(String s) +{ + cerr << "\n"< +class FindCursor : public Cursor +{ +public: + FindCursor( List& list, Link* pointer = 0 ); + FindCursor( const Cursor& cursor ); + + Cursor operator =( const Cursor& c ); + FindCursor find( const T& thing ); +}; + +#include "findcurs.inl" diff --git a/flower/findcurs.inl b/flower/findcurs.inl new file mode 100644 index 0000000000..593d4aae10 --- /dev/null +++ b/flower/findcurs.inl @@ -0,0 +1,37 @@ +template +inline +FindCursor::FindCursor( List& list, Link* pointer ) : + Cursor( list, pointer ) +{ +} + +template +inline +FindCursor::FindCursor( const Cursor& cursor ) : + Cursor( cursor ) +{ +} + +template +Cursor +FindCursor::operator =( const Cursor& c ) +{ + ( (Cursor&)*this ) = c; + return *this; +} + +template +inline FindCursor +FindCursor::find( const T& thing ) +{ + Cursor c( *this ); + while( c.forward() ) + { + T& look = *c; + if ( U::equal( look, thing ) ) + return c; + c++; + } + + return FindCursor( *this ); // cursor is not .ok() +} diff --git a/flower/lgetopt.cc b/flower/lgetopt.cc new file mode 100644 index 0000000000..ac9a12d035 --- /dev/null +++ b/flower/lgetopt.cc @@ -0,0 +1,214 @@ +/* + process command line, GNU style. + + + this is (Copyleft) 1996, Han-Wen Nienhuys, + */ +#include +#include +#include +#include "lgetopt.hh" + +long +Getopt_long::intarg() { + long l; + if (sscanf(optarg, "%ld", &l) != 1) + report(E_ILLEGALARG); + + return l; +} +long_option_init * +Getopt_long::parselong() { + const char *optnm = argv[optind] + 2 ; + assert(*optnm); + + char *endopt = strchr(optnm, '='); + int searchlen = (endopt) ? endopt - optnm : strlen(optnm); + + beet=0; + for (int i=0; i< table_len; i++) { + const char *ln = the_opts[i].longname; + + if (ln && !strncmp(ln, optnm, searchlen)) { + beet = the_opts+i; + break; + } + } + + if (!beet) { + report(E_UNKNOWNOPTION); + return 0; + } + optind++; + optindind = 0; + + + if (beet->take_arg) { + if (endopt) + optarg = endopt +1; // a '=' + else { + optarg = argv[optind]; + optind++; + } + if (!optarg) + report(E_ARGEXPECT); + + } else { + optarg = 0; + if (endopt) + report(E_NOARGEXPECT); + } + + return beet; +} + + +ostream & +long_option_init::printon(ostream &errorout) +{ + if (shortname) + errorout <<"-" << shortname; + if (shortname && longname) + errorout << ", "; + if (longname) + errorout << "`--" << longname << "'"; + return errorout; +} + +// report an error, GNU style. +void +Getopt_long::report(Errorcod c) +{ + error = c; + if (!errorout) + return; + + *errorout << argv[0] << ": "; + switch (c) { + case E_ARGEXPECT: + *errorout<< "option "; + beet->printon(*errorout); + *errorout << "requires an argument"<longname << "' does not allow an argument"<printon(*errorout); + *errorout << '\n'; + default: + assert(false); + } + exit(2); +} + +long_option_init * +Getopt_long::parseshort() { + char c=argv[optind][optindind]; + beet=0; + assert(c); + + for (int i=0; i < table_len; i++) + if (the_opts[i].shortname == c) { + beet = the_opts+i; + break; + } + + if (!beet){ + report(E_UNKNOWNOPTION); + return 0; + } + + optindind++; + if (!beet->take_arg){ + optarg = 0; + return beet; + } + optarg = argv[optind] + optindind; + + optind ++; + optindind = 0; + + if (!optarg[0]) { + optarg = argv[optind]; + optind ++; + } + if (!optarg) { + report(E_ARGEXPECT); + } + + return beet; +} + +long_option_init * +Getopt_long::operator()() { + if (!next()) + return 0; + + if (optindind) + return parseshort(); + + if (argv[optind][0] != '-') + return 0; + + if (argv[optind][1] == '-') {// what to do with "command -- bla" + return parselong(); + } else { + optindind = 1; + return parseshort(); + } +} + +Getopt_long::Getopt_long(int c, char **v, long_option_init *lo) { + the_opts = lo; + errorout = &cerr; + argv = v; + argc = c; + optind = 1; + optindind = 0; + + // reached end of option table? + int i; + for (i = 0; the_opts[i].longname ||the_opts[i].shortname; i++) + ; + table_len = i; +} + +bool Getopt_long::next() { + error = E_NOERROR; + while (optind < argc && !argv[optind][optindind]) { + optind++; + optindind = 0; + } + return (optind < argc); +} + +char * +Getopt_long::current_arg() +{ + if (optind >= argc) + return 0; + char * a = argv[optind]; + return a + optindind; +} + +char * +Getopt_long::get_next_arg() +{ + char * a = current_arg(); + if ( a) { + optind ++; + optindind = 0; + } + return a; +} diff --git a/flower/lgetopt.hh b/flower/lgetopt.hh new file mode 100644 index 0000000000..0ff3e08419 --- /dev/null +++ b/flower/lgetopt.hh @@ -0,0 +1,90 @@ +#ifndef LGETOPT_HH +#define LGETOPT_HH + +#include + + +class ostream; + +struct long_option_init { + bool take_arg; + const char* longname; + char shortname; + + ostream &printon(ostream &errorout); +}; + + +/// +class Getopt_long { +public: + /// + enum Errorcod { E_NOERROR = 0, E_ARGEXPECT, E_NOARGEXPECT, E_UNKNOWNOPTION, + E_ILLEGALARG } ; + + /** errorcodes: no error, argument expected, no argument expected, + unknown option, illegal argument (eg. int expected). */ + +private: + + /// the option info. + long_option_init *the_opts; + int table_len; + + /// if doing short option, argv[optind][optindind] is processed next. + int optindind; + + /// the option found + long_option_init *beet; + + /// get ready for processing next error. + bool next(); + long_option_init *parselong(); + long_option_init *parseshort(); + + ostream *errorout; + + /// report an error and abort + void report(Errorcod c); +public: + /// what to do with errors + void seterror(ostream *os); + /** + report messages on #*os#, and abort. + if #os# is null, then do not report nor abort, just set #error# + */ + + /// argument. Set to 0 if not present + char* optarg; + + + /// return an integer (with err. detect) + long intarg(); + /// argv[optind] will be processed next. + int optind; + + /// the arguments + char **argv; + + /// the arg. count + int argc; + + /// construct: pass arguments and option info. + Getopt_long(int c, char **v, long_option_init *lo); + + /// get the next option + long_option_init *operator()(); + /** + RETURN: pointer to next option found. + 0 if error occurred, or next argument is no option. + */ + + char *current_arg(); + char * get_next_arg(); + Errorcod error; +}; +/** + C++ for version of long_getopt. For processing GNU style command line arguments. + No pointer (return values, arguments) contents are copied. + */ +#endif diff --git a/flower/link.hh b/flower/link.hh new file mode 100644 index 0000000000..6f9c75a411 --- /dev/null +++ b/flower/link.hh @@ -0,0 +1,38 @@ +// link.hh + +#ifndef __LINK_HH +#define __LINK_HH +template +class List; + + +/// class for List +template +class Link +{ +// friend class Cursor; +public: + Link( const T& thing ); + + Link* previous(); + Link* next(); + + /// put new Link item after me in list + void add( const T& thing ); + /// put new Link item before me in list + void insert( const T& thing ); + void remove(List &l); + + T& thing(); + void OK() const; +private: + Link( Link* previous, Link* next, const T& thing ); + + T thing_; + Link* previous_; + Link* next_; +}; + +#include "link.inl" + +#endif // __LINK_HH // diff --git a/flower/link.inl b/flower/link.inl new file mode 100644 index 0000000000..e76caab4f1 --- /dev/null +++ b/flower/link.inl @@ -0,0 +1,100 @@ +// link.inl -*-c++-*- +#ifndef LINK_INL +#define LINK_INL +#include +template +inline +void +Link::OK() const +{ + if (previous_) { + assert(previous_->next_ == this); + } + if (next_) { + assert(next_->previous_ == this); + } +} + +template +inline +Link::Link( const T& thing ) : + thing_( thing ) +{ + previous_ = next_ = 0; +} + +template +inline +Link::Link( Link* previous, Link* next, const T& thing ) : + thing_( thing ) +{ + previous_ = previous; + next_ = next; +} + +template +inline +Link* +Link::next() +{ + return next_; +} + +template +inline +Link* +Link::previous() +{ + return previous_; +} + +template +inline +void +Link::add( const T& thing ) +{ + + Link* l = new Link( this, next_, thing ); + if ( next_ ) + next_->previous_ = l; + next_ = l; +} + +template +inline void +Link::insert( const T& thing ) +{ + // Link* l = new Link( next_, this, thing ); + // bugfix hwn 16/9/96 + Link* l = new Link( previous_, this, thing ); + if ( previous_ ) + previous_->next_ = l; + previous_ = l; +} + +/* + don't forget to adjust #l#'s top_ and bottom_. + */ +template +inline void +Link::remove(List &l) +{ + if ( previous_ ) + previous_->next_ = next_; + else + l.top_ = next_; + + if ( next_ ) + next_->previous_ = previous_; + else + l.bottom_ = previous_; +} + +template +inline +T& +Link::thing() +{ + return thing_; +} +#endif diff --git a/flower/list.cc b/flower/list.cc new file mode 100644 index 0000000000..f93352f507 --- /dev/null +++ b/flower/list.cc @@ -0,0 +1,88 @@ +#ifndef LIST_CC +#define LIST_CC + +#include "list.hh" + +template +void +List::OK() const +{ + int i = size_; + Link *lp = top_; + while (i--) { + assert(lp); + lp->OK(); + lp = lp->next(); + } + assert(!lp); + i = size_; + lp = bottom_; + while (i--) { + assert(lp); + lp->OK(); + lp = lp->previous(); + } + assert(!lp); +} + +template +Cursor +List::top() +{ + + // ?? waarvoor is deze if ? + if ( top_ ) // equivalent: if ( size_ ) + { + Link* t = top_->previous(); + assert( t != top_ ); // silly link + while ( t ) + { + top_ = t; + t = top_->previous(); + } + } + // list empty: Cursor not ok() + return Cursor( *this, top_ ); +} + + +template +Cursor +List::bottom() +{ + /* wat is dit voor zooi? kan dit niet weg? + + (invarianten!) + */ + if ( bottom_ ) // equivalent: if ( size_ ) + { + Link* b = bottom_->next(); + assert( b != bottom_ ); // silly link + while ( b ) + { + bottom_ = b; + b = bottom_->next(); + } + } + // list empty: Cursor not ok() + return Cursor( *this, bottom_ ); +} + + +// not inlined since it assumes knowledge of destructor. +template +inline void +PointerList::remove( Cursor me ) +{ + if ( me.ok() ) + { + + delete *me; + List::remove( me ); + } +} + + + + +#endif diff --git a/flower/list.hh b/flower/list.hh new file mode 100644 index 0000000000..97e672f2e0 --- /dev/null +++ b/flower/list.hh @@ -0,0 +1,96 @@ +// list.hh + +#ifndef __LIST_HH +#define __LIST_HH + +class ostream; +template class Cursor; +template class Link; + +/// all purpose list +template +class List +{ + public: + /// construct empty list + List(); + + /// construct list from first item. + List( const T& thing ); + + virtual ~List(); + + Cursor bottom(); + + int size() const; + Cursor top(); + void OK() const; + protected: + friend class Cursor; + friend class Link; + + /// add after after_me + void add( const T& thing, Cursor after_me ); + + /// put thing before #before_me# + void insert( const T& thing, Cursor before_me ); + virtual void remove( Cursor me ); + + int size_; + Link* top_; + Link* bottom_; +}; +/** + a doubly linked list; + List can be seen as all items written down on paper, + from top to bottom + + class Cursor is used to extend List + + items are always stored as copies in List, but: + #List# : copies of #String# stored + #List# : copies of #String*# stored! + (do not use, use \Ref{PointerList}## instead.) + + {\bf note:} + retrieving "invalid" cursors, i.e. + #top()/bottom()# from empty list, #find()# without success, + results in a nonvalid Cursor ( #!ok()# ) + + + INVARIANTEN! +*/ + + +/// Use for list of pointers, e.g. PointerList. +template +class PointerList : public List +{ + public: + PointerList(); + PointerList( const T& thing ); + + /// + virtual ~PointerList(); + /** + This function deletes deletes the allocated pointers of all links. + #\Ref{~List}# is used to delete the links themselves. + */ + + protected: + virtual void remove( Cursor me ); +}; + +#include "list.inl" +#include "cursor.hh" + +// instantiate a template: explicit instantiation. +#define L_instantiate(a) template class List; template class Cursor; \ + template class Link +#define PL_instantiate(a) L_instantiate(a *); template class PointerList + +#endif // __LIST_HH // + + + + diff --git a/flower/list.inl b/flower/list.inl new file mode 100644 index 0000000000..54df457481 --- /dev/null +++ b/flower/list.inl @@ -0,0 +1,144 @@ +// -*-c++-*- +#ifndef LIST_INL +#define LIST_INL +template +inline +List::List() +{ + top_ = bottom_ = 0; + size_ = 0; +} + +template +inline +List::List( const T& thing ) +{ + top_ = bottom_ = 0; + size_ = 0; + add( thing, Cursor( *this, bottom_ ) ); +} + +template +inline +List::~List() +{ + for ( Cursor c( *this ); c.forward(); c++ ) + remove( c ); +} + +template +inline void +List::add( const T& thing, Cursor after_me ) +{ +#if 0 + if ( after_me.ok() ) + after_me.pointer()->add( thing ); + else if ( size_ ) + bottom().pointer()->add( thing ); + else + bottom_ = top_ = new Link( thing ); +#endif + + if (!size_) { // not much choice if list is empty + bottom_ = top_ = new Link( thing ); + } else { // add at aprioprate place + Link *p = ( after_me.ok() ) ? + after_me.pointer() : bottom().pointer(); + p->add(thing); + if (p == bottom_) // adjust bottom_ if necessary. + bottom_ = p->next(); + } + + size_++; +} +/** + + Procedure: + \begin{itemize} + \item if #after_me# is #ok()#, add after #after_me#, else + \item if list !empty simply add to bottom, else + \item list is empty: create first \Ref{Link} and initialize + #bottom_# and #top_#. + \end{itemize} +*/ + +template +inline void +List::insert( const T& thing, Cursor before_me ) +{ + if (!size_) { + bottom_ = top_ = new Link( thing ); + } else { + Link *p = + (before_me.ok())? + before_me.pointer() : top().pointer(); + + p->insert(thing); + if (p == top_) + top_ = p->previous(); + } + + size_++; +#if 0 // rewrite hwn 16/9 + if ( before_me.ok() ) + before_me.pointer()->insert( thing ); + else if ( size_ ) + top().pointer()->insert( thing ); + else + bottom_ = top_ = new Link( thing ); + size_++; +#endif +} + +template +inline void +List::remove( Cursor me ) +{ + if ( me.ok() ) + { + me.pointer()->remove(*this); + delete me.pointer(); + size_--; + } +} + +template +inline int +List::size() const +{ + return size_; +} + +template +inline +PointerList::PointerList() : + List() +{ +} + +template +inline +PointerList::PointerList( const T& thing ) : + List( thing ) +{ +} + +template +inline +PointerList::~PointerList() +{ + for ( Cursor c( *this ); c.forward(); c++ ) + remove( c ); +} + +template +inline void +PointerList_print( PointerList const & l ) +{ + List& promises_to_be_const = (List&) l; + for ( Cursor c( promises_to_be_const ); c.forward(); c++ ) + (*c)->print(); +} + + +#endif diff --git a/flower/stringutil.hh b/flower/stringutil.hh new file mode 100644 index 0000000000..b7503b7d2a --- /dev/null +++ b/flower/stringutil.hh @@ -0,0 +1,245 @@ +#ifndef STRINGUTIL_HH +#define STRINGUTIL_HH +#include + +#ifndef EVELYN +#define NDEBUG BLONDE + // switching into "blonde" mode + // i trust stringclass nowadays. +#endif + +class String_handle; +/// Internal String struct +class StringData { + // GNU malloc: storage overhead is 8 bytes anyway. + const int INITIALMAX = 8; + +friend class String_handle; + int maxlen; // maxlen is arraysize-1 + + int length; + char* string; + int references; + + /// init to "" + StringData() { + references=0; + maxlen = INITIALMAX; + string = new char[maxlen + 1]; + string[0] = 0; + length = 0; + } + + /// init from src. Conservative allocation. + StringData(StringData const &src) { + references=0; + maxlen = length = src.length; + string = new char[maxlen+1]; // should calc GNU 8byte overhead. + strcpy(string, src.string); + } + + ~StringData() { + assert(references == 0); + delete[] string; + } + + + void setmax(int j) { + OKW(); + if (j > maxlen) { + delete string; + maxlen = j; + string = new char[maxlen + 1]; + + string[0] = 0; + length = 0; + } + } + /** POST: maxlen >= j. + IN: j, maximum stringlength. + contents thrown away. + */ + /// + void remax(int j) { + OKW(); + if (j > maxlen) { + maxlen = j; + char *p = new char[maxlen + 1]; + strcpy(p,string); + delete[] string; + string = p; + // length = strlen(string); + } + } + /** POST: maxlen >= j. + IN: j, maximum stringlength. + contents are kept if it grows. + */ + /// check if writeable. + void OKW() { + + assert (references == 1); + + } + + /// check state. + void OK() { + assert(strlen(string) == size_t(length)); + assert(maxlen >= length); + assert(bool(string)); + assert(references >= 1); + } + + // needed? + void update() { + length = strlen (string); + } + + /// reduce memory usage. + void tighten() { // should be dec'd const + maxlen = length; + char *p = new char[maxlen + 1]; + strcpy(p,string); + delete[] string; + string = p; + } + + // assignment. + void set(const char *s) { + OKW(); + + assert(s); + + length = strlen (s); + remax(length); + strcpy(string,s); + } + + /// concatenation. + void operator += (const char *s) { + OK(); + OKW(); + int old = length; + + length += strlen(s); + remax (length); + strcpy(string + old, s); + } + + /// the array itself + operator const char *() const { return string; } + + // idem, non const + char *array_for_modify() { + OKW(); + return string; + } + void trunc(int j) { + OKW(); + assert(j >= 0 && j <= length); + string[j] = 0; + length = j; + } + + /** not really safe. Can alter length without StringData knowing it. + */ + char &operator [](int j) { + assert(j >= 0 && j <= length); + return string[j] ; + } + + char operator [](int j) const { + assert(j >= 0 && j <= length); + return string[j]; + } +}; + +/** + the data itself. Handles simple tasks (resizing, resetting) + */ +/****************************************************************/ +/// ref. counting for strings +class String_handle { + StringData* data; + + /// decrease ref count. Named kind of like a Tanenbaum semafore + void down() { if (!(--data->references)) delete data; data = 0; } + + /// increase ref count + void up(StringData *d) { data=d; data->references ++; } + + /** make sure data has only one reference. + POST: data->references == 1 + */ + void copy() { + if (data->references !=1){ + StringData *newdata = new StringData(*data); + down(); + up(newdata); + } + } + +public: + + String_handle() { + up(new StringData); + } + ~String_handle() { + down(); + } + String_handle(String_handle const & src) { + up(src.data); + } + + /// retrieve the actual array. + operator const char *() const { return *data; } + char *array_for_modify() { + copy(); + return data->array_for_modify(); + } + + void operator =(String_handle const &src) { + if (this == &src) + return; + down(); + up(src.data); + } + + void operator += (const char *s) { + copy(); + *data += s; + } + + + char operator[](int j) const { return (*data)[j]; } + + // !NOT SAFE! + // don't use this for loops. Use array_for_modify() + char &operator[](int j) { + copy(); // hmm. Not efficient + return data->array_for_modify()[j]; + } + + void operator = (char const *p) { + copy(); + data->set(p); + } + + void trunc(int j) { copy(); data->trunc(j); } + int len() const { return data->length; } +}; +/** + handles ref. counting, and provides a very thin + interface using char * + */ + + +#ifdef NDEBUG +#if (NDEBUG == BLONDE) +#undef NDEBUG +#endif +#endif + + + +#endif // STRINGUTIL_HH diff --git a/flower/textdb.cc b/flower/textdb.cc new file mode 100644 index 0000000000..4dfdc876b3 --- /dev/null +++ b/flower/textdb.cc @@ -0,0 +1,23 @@ +#include "textdb.hh" + +Text_record +Text_db::get_record() +{ + String s; + svec fields; + gobble_leading_white(); + + while ((s = get_word()) != "") + { + fields.add(s); + gobble_white(); + } + + + if (get_line() != "") + assert(false); + + return Text_record(fields, get_name(), line()); +} + + diff --git a/flower/textdb.hh b/flower/textdb.hh new file mode 100644 index 0000000000..8f906b8626 --- /dev/null +++ b/flower/textdb.hh @@ -0,0 +1,56 @@ +#ifndef TEXTDB_HH +#define TEXTDB_HH + +#include "textstr.hh" + +/**: do "#" comments, read quote enclosed fields */ + +/// a "const" svec. Contents can't be changed. +class Text_record : svec +{ + int line_no; + String filename; + +public: + Text_record() { } // needed because of other ctor + + /// report an error in this line. + message(String s) { + cerr << '\n'<< filename << ": "<< line_no << s << "\n"; + } + String operator[](int j) { + return svec::operator[](j); + } + + Text_record(svec s, String fn, int j) : svec(s) { + filename = fn; line_no = j; + } + svec::sz; +}; + +/// abstraction for a datafile +class Text_db : private Data_file +{ + public: + /// get a line with records + Text_record get_record(); + + Text_db(String fn):Data_file(fn) { } + Data_file::error; + Data_file::eof; + + /// get next line. + Text_record operator++(int) { + return get_record(); + } + /// are we done yet? + operator bool() { + return !eof(); + } +}; + + +/** + add a subrec/fieldsep/record separator + */ +#endif diff --git a/flower/textstr.hh b/flower/textstr.hh new file mode 100644 index 0000000000..41a979f27f --- /dev/null +++ b/flower/textstr.hh @@ -0,0 +1,114 @@ +#include +#include + +//#include "globals.hh" +#include "string.hh" +#include "vray.hh" + +/// line counting input stream. +class Text_stream +{ + int line_no; + + // could just have used streams. + FILE *f; + sstack pushback; + String name; + + public: + Text_stream(String fn); + String get_name() { return name; } + bool eof() { + return feof(f); + } + bool eol() { + return (peek() == '\n'); + } + char peek() { + char c = get(); + unget(c); + return c; + } + int line(){ + return line_no; + } + + char get() { + char c; + + if (pushback.empty()) + c = getc(f); + else + c = pushback.pop(); + + if (c =='\n') + line_no++; + return c; + } + void unget(char c) { + if (c =='\n') + line_no--; + pushback.push(c); + } + ~Text_stream (){ + if (!eof()) + cerr <<__FUNCTION__<< ": closing unended file"; + + fclose(f); + } + + /// GNU format message. + void message(String s); +}; +/** + a stream for textfiles. linecounting. Thin interface getchar and + ungetchar. (ungetc is unlimited) + + should protect get and unget against improper use +*/ + + +/// read a data file +class Data_file : private Text_stream +{ + + public: + bool rawmode; + + Text_stream::line; + Text_stream::eof; + Text_stream::get_name; + + char data_get(); + void data_unget(char c) { + unget(c); + } + + /// read line, eat #\n# + String get_line(); + + /// read a word till next space, leave space. Also does quotes + String get_word(); + + /// gobble horizontal white stuff. + void gobble_white(); + + /// gobble empty stuff before first field. + void gobble_leading_white(); + Data_file(String s) : Text_stream(s) { + //*mlog << "(" << s << flush; + rawmode= false; + } + + ~Data_file() { + // *mlog << ")"<