--- /dev/null
+// cursor.cc
+#ifndef CURSOR_CC
+#define CURSOR_CC
+
+#include "cursor.hh"
+//#define inline
+//#include "cursor.inl"
+#include <assert.h>
+
+template<class T>
+Cursor<T>
+Cursor<T>::operator ++( int )
+{
+ Cursor<T> r = *this;
+ assert( pointer_ );
+ pointer_ = pointer_->next();
+ return r;
+}
+template<class T>
+Cursor<T>
+Cursor<T>::operator -=( int j )
+{
+ while (j--)
+ (*this)--;
+ return *this;
+}
+template<class T>
+Cursor<T>
+Cursor<T>::operator +=( int j )
+{
+ while (j++)
+ (*this)++;
+ return *this;
+}
+
+template<class T>
+Cursor<T>
+Cursor<T>::operator --( int )
+{
+ Cursor<T> r = *this;
+ assert( pointer_ );
+ pointer_ = pointer_->previous();
+ return r;
+}
+
+template<class T>
+Cursor<T>
+Cursor<T>::operator +( int i ) const
+{
+ Cursor<T> r = *this;
+
+ if (i<0)
+ return r -(-i);
+
+ while (i--)
+ r++;
+
+ return r;
+}
+
+template<class T>
+Cursor<T>
+Cursor<T>::operator -( int i ) const
+{
+ Cursor<T> r = *this;
+ if (i<0)
+ return r +(-i);
+
+ while (i--)
+ r--;
+
+ return r;
+}
+
+#endif
--- /dev/null
+ // cursor.inl
+#ifndef CURSOR_INL
+#define CURSOR_INL
+#include <assert.h>
+//#include "list.hh"
+
+template<class T>
+inline
+Cursor<T>::Cursor( List<T>& list, Link<T>* pointer ) :
+ list_( list )
+{
+ if ( list.size() )
+ pointer_ = pointer ? pointer : list.top().pointer_;
+ else
+ pointer_ = pointer;
+}
+
+template<class T>
+inline
+Cursor<T>::Cursor( const Cursor<T>& cursor ) :
+ list_( cursor.list_ )
+{
+ pointer_ = cursor.pointer_;
+}
+
+template<class T>
+inline T&
+Cursor<T>::operator *()
+{
+ assert( pointer_ );
+ return pointer_->thing();
+}
+
+template<class T>
+Cursor<T>
+Cursor<T>::operator =( const Cursor<T>& c )
+{
+ assert( &list_ == &c.list_ );
+ pointer_ = c.pointer_;
+ return *this;
+}
+
+template<class T>
+inline void
+Cursor<T>::add( const T& thing )
+{
+ list_.add( thing, *this );
+}
+
+template<class T>
+inline void
+Cursor<T>::insert( const T& thing )
+{
+ list_.insert( thing, *this );
+}
+
+template<class T>
+inline void
+Cursor<T>::remove()
+{
+ assert( pointer_ );
+ list_.remove( *this );
+}
+
+template<class T>
+inline const List<T>&
+Cursor<T>::list() const
+{
+ return list_;
+}
+
+template<class T>
+inline Link<T>*
+Cursor<T>::pointer()
+{
+ return pointer_;
+}
+
+template<class T>
+inline bool
+Cursor<T>::backward()
+{
+ return ( pointer_ != 0 );
+}
+
+template<class T>
+inline bool
+Cursor<T>::forward()
+{
+ return ( pointer_ != 0 );
+}
+
+template<class T>
+inline bool
+Cursor<T>::ok()
+{
+ return ( pointer_ != 0 );
+}
+
+#endif
\ No newline at end of file
--- /dev/null
+#include <fstream.h>
+#include <ctype.h>
+
+#include "textstr.hh"
+Text_stream::Text_stream(String fn)
+{
+ if (fn == "")
+ {
+ name = "<STDIN>";
+ 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"<<get_name() << ": " << line()<<": "<<s<<endl;
+}
+
+void
+Data_file::gobble_white()
+{
+ char c;
+
+ while ((c=data_get()) == ' ' ||c == '\t')
+ if (eof())
+ break;
+
+ data_unget(c);
+}
+
+String
+Data_file::get_word()
+{// should handle escape seq's
+ String s;
+
+ while (1)
+ {
+ char c = data_get();
+
+ if (isspace(c) || eof())
+ {
+ data_unget(c);
+ break;
+ }
+
+
+ if (c == '\"')
+ {
+ rawmode= true;
+
+ while ((c = data_get()) != '\"')
+ if (eof())
+ error("EOF in a string");
+ else
+ s += c;
+
+
+ rawmode= false;
+ }
+ else
+ s += c;
+ }
+
+ return s;
+}
+
+/// get a char.
+char
+Data_file::data_get() {
+ char c = get();
+ if (!rawmode && c == '#') // gobble comment
+ {
+ while ((c = get()) != '\n' && !eof())
+ ;
+ return '\n';
+ }
+
+ return c;
+}
+/**
+ Only class member who uses text_file::get
+ */
+
+/// read line, gobble '\n'
+String Data_file::get_line()
+{
+ char c;
+ String s;
+
+ while ((c = data_get()) != '\n' && !eof())
+ s += c;
+ return s;
+}
+
+/// gobble stuff before first entry on a line.
+void
+Data_file::gobble_leading_white()
+{
+ char c;
+
+ // eat blank lines.
+ while (!eof())
+ {
+ c = data_get();
+ if (!isspace(c))
+ break;
+ }
+ data_unget(c);
+}
+
+
--- /dev/null
+template<class T, class U>
+class FindCursor : public Cursor<T>
+{
+public:
+ FindCursor( List<T>& list, Link<T>* pointer = 0 );
+ FindCursor( const Cursor<T>& cursor );
+
+ Cursor<T> operator =( const Cursor<T>& c );
+ FindCursor<T, U> find( const T& thing );
+};
+
+#include "findcurs.inl"
--- /dev/null
+template<class T, class U>
+inline
+FindCursor<T, U>::FindCursor( List<T>& list, Link<T>* pointer ) :
+ Cursor<T>( list, pointer )
+{
+}
+
+template<class T, class U>
+inline
+FindCursor<T, U>::FindCursor( const Cursor<T>& cursor ) :
+ Cursor<T>( cursor )
+{
+}
+
+template<class T, class U>
+Cursor<T>
+FindCursor<T, U>::operator =( const Cursor<T>& c )
+{
+ ( (Cursor<T>&)*this ) = c;
+ return *this;
+}
+
+template<class T, class U>
+inline FindCursor<T, U>
+FindCursor<T, U>::find( const T& thing )
+{
+ Cursor<T> c( *this );
+ while( c.forward() )
+ {
+ T& look = *c;
+ if ( U::equal( look, thing ) )
+ return c;
+ c++;
+ }
+
+ return FindCursor( *this ); // cursor is not .ok()
+}
--- /dev/null
+/*
+ process command line, GNU style.
+
+
+ this is (Copyleft) 1996, Han-Wen Nienhuys, <hanwen@stack.urc.tue.nl>
+ */
+#include <stdio.h>
+#include <iostream.h>
+#include <assert.h>
+#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"<<endl;
+ break;
+ case E_NOARGEXPECT:
+ *errorout << "option `--" << beet->longname << "' does not allow an argument"<<endl;
+ break;
+
+ case E_UNKNOWNOPTION:
+ *errorout << "unrecognized option ";
+ if (optindind)
+ *errorout << "-" << argv[optind][optindind] << endl;
+ else
+ *errorout << argv[optind] << endl;
+
+ break;
+ case E_ILLEGALARG:
+ *errorout << "illegal argument `" << optarg << "\'to option ";
+ beet->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;
+}
--- /dev/null
+#ifndef LGETOPT_HH
+#define LGETOPT_HH
+
+#include <string.h>
+
+
+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
--- /dev/null
+// link.hh
+
+#ifndef __LINK_HH
+#define __LINK_HH
+template<class T>
+class List;
+
+
+/// class for List
+template<class T>
+class Link
+{
+// friend class Cursor<T>;
+public:
+ Link( const T& thing );
+
+ Link<T>* previous();
+ Link<T>* 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<T> &l);
+
+ T& thing();
+ void OK() const;
+private:
+ Link( Link<T>* previous, Link<T>* next, const T& thing );
+
+ T thing_;
+ Link<T>* previous_;
+ Link<T>* next_;
+};
+
+#include "link.inl"
+
+#endif // __LINK_HH //
--- /dev/null
+// link.inl -*-c++-*-
+#ifndef LINK_INL
+#define LINK_INL
+#include <assert.h>
+template<class T>
+inline
+void
+Link<T>::OK() const
+{
+ if (previous_) {
+ assert(previous_->next_ == this);
+ }
+ if (next_) {
+ assert(next_->previous_ == this);
+ }
+}
+
+template<class T>
+inline
+Link<T>::Link( const T& thing ) :
+ thing_( thing )
+{
+ previous_ = next_ = 0;
+}
+
+template<class T>
+inline
+Link<T>::Link( Link<T>* previous, Link<T>* next, const T& thing ) :
+ thing_( thing )
+{
+ previous_ = previous;
+ next_ = next;
+}
+
+template<class T>
+inline
+Link<T>*
+Link<T>::next()
+{
+ return next_;
+}
+
+template<class T>
+inline
+Link<T>*
+Link<T>::previous()
+{
+ return previous_;
+}
+
+template<class T>
+inline
+void
+Link<T>::add( const T& thing )
+{
+
+ Link<T>* l = new Link<T>( this, next_, thing );
+ if ( next_ )
+ next_->previous_ = l;
+ next_ = l;
+}
+
+template<class T>
+inline void
+Link<T>::insert( const T& thing )
+{
+ // Link<T>* l = new Link<T>( next_, this, thing );
+ // bugfix hwn 16/9/96
+ Link<T>* l = new Link<T>( previous_, this, thing );
+ if ( previous_ )
+ previous_->next_ = l;
+ previous_ = l;
+}
+
+/*
+ don't forget to adjust #l#'s top_ and bottom_.
+ */
+template<class T>
+inline void
+Link<T>::remove(List<T> &l)
+{
+ if ( previous_ )
+ previous_->next_ = next_;
+ else
+ l.top_ = next_;
+
+ if ( next_ )
+ next_->previous_ = previous_;
+ else
+ l.bottom_ = previous_;
+}
+
+template<class T>
+inline
+T&
+Link<T>::thing()
+{
+ return thing_;
+}
+#endif
--- /dev/null
+#ifndef LIST_CC
+#define LIST_CC
+
+#include "list.hh"
+
+template<class T>
+void
+List<T>::OK() const
+{
+ int i = size_;
+ Link<T> *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<class T>
+Cursor<T>
+List<T>::top()
+{
+
+ // ?? waarvoor is deze if ?
+ if ( top_ ) // equivalent: if ( size_ )
+ {
+ Link<T>* t = top_->previous();
+ assert( t != top_ ); // silly link
+ while ( t )
+ {
+ top_ = t;
+ t = top_->previous();
+ }
+ }
+ // list empty: Cursor not ok()
+ return Cursor<T>( *this, top_ );
+}
+
+
+template<class T>
+Cursor<T>
+List<T>::bottom()
+{
+ /* wat is dit voor zooi? kan dit niet weg?
+
+ (invarianten!)
+ */
+ if ( bottom_ ) // equivalent: if ( size_ )
+ {
+ Link<T>* b = bottom_->next();
+ assert( b != bottom_ ); // silly link
+ while ( b )
+ {
+ bottom_ = b;
+ b = bottom_->next();
+ }
+ }
+ // list empty: Cursor not ok()
+ return Cursor<T>( *this, bottom_ );
+}
+
+
+// not inlined since it assumes knowledge of destructor.
+template<class T>
+inline void
+PointerList<T>::remove( Cursor<T> me )
+{
+ if ( me.ok() )
+ {
+
+ delete *me;
+ List<T>::remove( me );
+ }
+}
+
+
+
+
+#endif
--- /dev/null
+// list.hh
+
+#ifndef __LIST_HH
+#define __LIST_HH
+
+class ostream;
+template<class T> class Cursor;
+template<class T> class Link;
+
+/// all purpose list
+template<class T>
+class List
+{
+ public:
+ /// construct empty list
+ List();
+
+ /// construct list from first item.
+ List( const T& thing );
+
+ virtual ~List();
+
+ Cursor<T> bottom();
+
+ int size() const;
+ Cursor<T> top();
+ void OK() const;
+ protected:
+ friend class Cursor<T>;
+ friend class Link<T>;
+
+ /// add after after_me
+ void add( const T& thing, Cursor<T> after_me );
+
+ /// put thing before #before_me#
+ void insert( const T& thing, Cursor<T> before_me );
+ virtual void remove( Cursor<T> me );
+
+ int size_;
+ Link<T>* top_;
+ Link<T>* 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<String># : copies of #String# stored
+ #List<String*># : copies of #String*# stored!
+ (do not use, use \Ref{PointerList}#<String*># 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<AbstractType*>.
+template<class T>
+class PointerList : public List<T>
+{
+ 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<T> me );
+};
+
+#include "list.inl"
+#include "cursor.hh"
+
+// instantiate a template: explicit instantiation.
+#define L_instantiate(a) template class List<a>; template class Cursor<a>; \
+ template class Link<a>
+#define PL_instantiate(a) L_instantiate(a *); template class PointerList<a*>
+
+#endif // __LIST_HH //
+
+
+
+
--- /dev/null
+// -*-c++-*-
+#ifndef LIST_INL
+#define LIST_INL
+template<class T>
+inline
+List<T>::List()
+{
+ top_ = bottom_ = 0;
+ size_ = 0;
+}
+
+template<class T>
+inline
+List<T>::List( const T& thing )
+{
+ top_ = bottom_ = 0;
+ size_ = 0;
+ add( thing, Cursor<T>( *this, bottom_ ) );
+}
+
+template<class T>
+inline
+List<T>::~List()
+{
+ for ( Cursor<T> c( *this ); c.forward(); c++ )
+ remove( c );
+}
+
+template<class T>
+inline void
+List<T>::add( const T& thing, Cursor<T> 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<T>( thing );
+#endif
+
+ if (!size_) { // not much choice if list is empty
+ bottom_ = top_ = new Link<T>( thing );
+ } else { // add at aprioprate place
+ Link<T> *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<class T>
+inline void
+List<T>::insert( const T& thing, Cursor<T> before_me )
+{
+ if (!size_) {
+ bottom_ = top_ = new Link<T>( thing );
+ } else {
+ Link<T> *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<T>( thing );
+ size_++;
+#endif
+}
+
+template<class T>
+inline void
+List<T>::remove( Cursor<T> me )
+{
+ if ( me.ok() )
+ {
+ me.pointer()->remove(*this);
+ delete me.pointer();
+ size_--;
+ }
+}
+
+template<class T>
+inline int
+List<T>::size() const
+{
+ return size_;
+}
+
+template<class T>
+inline
+PointerList<T>::PointerList() :
+ List<T>()
+{
+}
+
+template<class T>
+inline
+PointerList<T>::PointerList( const T& thing ) :
+ List<T>( thing )
+{
+}
+
+template<class T>
+inline
+PointerList<T>::~PointerList()
+{
+ for ( Cursor<T> c( *this ); c.forward(); c++ )
+ remove( c );
+}
+
+template<class T>
+inline void
+PointerList_print( PointerList<T> const & l )
+{
+ List<T>& promises_to_be_const = (List<T>&) l;
+ for ( Cursor<T> c( promises_to_be_const ); c.forward(); c++ )
+ (*c)->print();
+}
+
+
+#endif
--- /dev/null
+#ifndef STRINGUTIL_HH
+#define STRINGUTIL_HH
+#include <assert.h>
+
+#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
--- /dev/null
+#include "textdb.hh"
+
+Text_record
+Text_db::get_record()
+{
+ String s;
+ svec<String> 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());
+}
+
+
--- /dev/null
+#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<String>
+{
+ 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<String>::operator[](j);
+ }
+
+ Text_record(svec<String> s, String fn, int j) : svec<String>(s) {
+ filename = fn; line_no = j;
+ }
+ svec<String>::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
--- /dev/null
+#include <stdio.h>
+#include <ctype.h>
+
+//#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<char> 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 << ")"<<flush;
+ }
+
+ warning(String s) {
+ message("warning: " + s);
+ }
+ error(String s){
+ message(s);
+ exit(1);
+ }
+};