1. smbase

Start data section to licences/sm_licence.txt[1 /1 ]
     1: All of the modules in the sm system are
     2: hereby placed in the public domain.
     3: 
End data section to licences/sm_licence.txt[1]
Start cpp section to elk/sm_malloc_stub.cpp[1 /1 ]
     1: #line 9 "./lpsrc/sm.pak"
     2: // malloc_stub.h            see license.txt for copyright and terms of use
     3: // no-op implementation of ckheap.h
     4: 
     5: #include "sm_ckheap.h"     // interface to implement
     6: 
     7: void checkHeap() {}
     8: 
     9: void checkHeapNode(void *node) {}
    10: 
    11: void malloc_stats() {}
    12: 
    13: unsigned numMallocCalls() { return 0; }
    14: unsigned numFreeCalls() { return 0; }
    15: 
    16: void walkMallocHeap(HeapWalkFn func) {}
    17: 
    18: // EOF
End cpp section to elk/sm_malloc_stub.cpp[1]
Start C section to elk/sm_array.h[1 /1 ]
     1: #line 28 "./lpsrc/sm.pak"
     2: // array.h            see license.txt for copyright and terms of use
     3: // some array classes
     4: 
     5: #ifndef ARRAY_H
     6: #define ARRAY_H
     7: 
     8: #include "sm_xassert.h"
     9: 
    10: 
    11: // -------------------- Array ----------------------
    12: // This is the same as C++'s built-in array, but automatically deallocates.
    13: // If you want bounds checking too, use GrowArray, below.
    14: template <class T>
    15: class Array {
    16: private:     // data
    17:   T *arr;
    18: 
    19: private:     // not allowed
    20:   Array(Array&);
    21:   void operator=(Array&);
    22: 
    23: public:
    24:   Array(int len) : arr(new T[len]) {}
    25:   ~Array() { delete[] arr; }
    26: 
    27:   T const &operator[] (int i) const { return arr[i]; }
    28:   T &operator[] (int i) { return arr[i]; }
    29: 
    30:   operator T const* () const { return arr; }
    31:   operator T const* () { return arr; }
    32:   operator T * () { return arr; }
    33: 
    34:   T const* operator+ (int i) const { return arr+i; }
    35:   T * operator+ (int i) { return arr+i; }
    36: 
    37:   // convenience
    38:   void setAll(T val, int len) {
    39:     for (int i=0; i<len; i++) {
    40:       arr[i] = val;
    41:     }
    42:   }
    43: };
    44: 
    45: 
    46: // ------------------ GrowArray --------------------
    47: // This class implements an array of T's; it automatically expands
    48: // when 'ensureAtLeast' or 'ensureIndexDoubler' is used; it does not
    49: // automatically contract.  All accesses are bounds-checked.
    50: //
    51: // class T must have:
    52: //   T::T();           // default ctor for making arrays
    53: //   operator=(T&);    // assignment for copying to new storage
    54: //   T::~T();          // dtor for when old array is cleared
    55: template <class T>
    56: class GrowArray {
    57: private:     // data
    58:   T *arr;                 // underlying array; NULL if sz==0
    59:   int sz;                 // # allocated entries in 'arr'
    60: 
    61: private:     // funcs
    62:   void bc(int i) const    // bounds-check an index
    63:     { xassert((unsigned)i < (unsigned)sz); }
    64:   void eidLoop(int index);
    65: 
    66: public:      // funcs
    67:   GrowArray(int initSz);
    68:   ~GrowArray();
    69: 
    70:   // allocated space
    71:   int size() const { return sz; }
    72: 
    73:   // element access
    74:   T const& operator[] (int i) const   { bc(i); return arr[i]; }
    75:   T      & operator[] (int i)         { bc(i); return arr[i]; }
    76: 
    77:   // set size, reallocating if old size is different; if the
    78:   // array gets bigger, existing elements are preserved; if the
    79:   // array gets smaller, elements are truncated
    80:   void setSize(int newSz);
    81: 
    82:   // make sure there are at least 'minSz' elements in the array;
    83:   void ensureAtLeast(int minSz)
    84:     { if (minSz > sz) { setSize(minSz); } }
    85: 
    86:   // grab a read-only pointer to the raw array
    87:   T const *getArray() const { return arr; }
    88: 
    89:   // grab a writable pointer; use with care
    90:   T *getDangerousWritableArray() { return arr; }
    91:   T *getArrayNC() { return arr; }     // ok, not all that dangerous..
    92: 
    93:   // make sure the given index is valid; if this requires growing,
    94:   // do so by doubling the size of the array (repeatedly, if
    95:   // necessary)
    96:   void ensureIndexDoubler(int index)
    97:     { if (sz-1 < index) { eidLoop(index); } }
    98: 
    99:   // set an element, using the doubler if necessary
   100:   void setIndexDoubler(int index, T const &value)
   101:     { ensureIndexDoubler(index); arr[index] = value; }
   102: 
   103:   // swap my data with the data in another GrowArray object
   104:   void swapWith(GrowArray<T> &obj) {
   105:     T *tmp1 = obj.arr; obj.arr = this->arr; this->arr = tmp1;
   106:     int tmp2 = obj.sz; obj.sz = this->sz; this->sz = tmp2;
   107:   }
   108: 
   109:   // convenience
   110:   void setAll(T val) {
   111:     for (int i=0; i<sz; i++) {
   112:       arr[i] = val;
   113:     }
   114:   }
   115: };
   116: 
   117: 
   118: template <class T>
   119: GrowArray<T>::GrowArray(int initSz)
   120: {
   121:   sz = initSz;
   122:   if (sz > 0) {
   123:     arr = new T[sz];
   124:   }
   125:   else {
   126:     arr = NULL;
   127:   }
   128: }
   129: 
   130: 
   131: template <class T>
   132: GrowArray<T>::~GrowArray()
   133: {
   134:   if (arr) {
   135:     delete[] arr;
   136:   }
   137: }
   138: 
   139: 
   140: template <class T>
   141: void GrowArray<T>::setSize(int newSz)
   142: {
   143:   if (newSz != sz) {
   144:     // keep track of old
   145:     int oldSz = sz;
   146:     T *oldArr = arr;
   147: 
   148:     // make new
   149:     sz = newSz;
   150:     if (sz > 0) {
   151:       arr = new T[sz];
   152:     }
   153:     else {
   154:       arr = NULL;
   155:     }
   156: 
   157:     // copy elements in common
   158:     for (int i=0; i<sz && i<oldSz; i++) {
   159:       arr[i] = oldArr[i];
   160:     }
   161: 
   162:     // get rid of old
   163:     if (oldArr) {
   164:       delete[] oldArr;
   165:     }
   166:   }
   167: }
   168: 
   169: 
   170: // this used to be ensureIndexDoubler's implementation, but
   171: // I wanted the very first check to be inlined
   172: template <class T>
   173: void GrowArray<T>::eidLoop(int index)
   174: {
   175:   if (sz-1 >= index) {
   176:     return;
   177:   }
   178: 
   179:   int newSz = sz;
   180:   while (newSz-1 < index) {
   181:     #ifndef NDEBUG_NO_ASSERTIONS    // silence warning..
   182:       int prevSz = newSz;
   183:     #endif
   184:     if (newSz == 0) {
   185:       newSz = 1;
   186:     }
   187:     newSz = newSz*2;
   188:     xassert(newSz > prevSz);        // otherwise overflow -> infinite loop
   189:   }
   190: 
   191:   setSize(newSz);
   192: }
   193: 
   194: 
   195: // ---------------------- ArrayStack ---------------------
   196: // This is an array where some of the array is unused.  Specifically,
   197: // it maintains a 'length', and elements 0 up to length-1 are
   198: // considered used, whereas length up to size-1 are unused.  The
   199: // expected use is as a stack, where "push" adds a new (used) element.
   200: template <class T>
   201: class ArrayStack : public GrowArray<T> {
   202: private:
   203:   int len;               // # of elts in the stack
   204: 
   205: public:
   206:   ArrayStack(int initArraySize = 10)
   207:     : GrowArray<T>(initArraySize),
   208:       len(0)
   209:     {}
   210:   ~ArrayStack();
   211: 
   212:   // element access; these declarations are necessary because
   213:   // the uses of 'operator[]' below are not "dependent", hence
   214:   // they can't use declarations inherited from GrowArray<T>
   215:   T const& operator[] (int i) const { return GrowArray<T>::operator[](i); }
   216:   T      & operator[] (int i)       { return GrowArray<T>::operator[](i); }
   217: 
   218:   void push(T const &val)
   219:     { setIndexDoubler(len++, val); }
   220:   T pop()
   221:     { return operator[](--len); }
   222:   T const &top() const
   223:     { return operator[](len-1); }
   224:   T &top()
   225:     { return operator[](len-1); }
   226: 
   227:   // alternate interface, where init/deinit is done explicitly
   228:   // on returned references
   229:   T &pushAlt()    // returns newly accessible item
   230:     { GrowArray<T>::ensureIndexDoubler(len++); return top(); }
   231:   T &popAlt()     // returns item popped
   232:     { return operator[](--len); }
   233: 
   234:   // items stored
   235:   int length() const
   236:     { return len; }
   237: 
   238:   bool isEmpty() const
   239:     { return len==0; }
   240:   bool isNotEmpty() const
   241:     { return !isEmpty(); }
   242: 
   243:   void popMany(int ct)
   244:     { len -= ct; xassert(len >= 0); }
   245:   void empty()
   246:     { len = 0; }
   247: 
   248:   // useful when someone has used 'getDangerousWritableArray' to
   249:   // fill the array's internal storage
   250:   void setLength(int L) { len = L; }
   251: 
   252:   // consolidate allocated space to match length
   253:   void consolidate() { setSize(length()); }
   254: 
   255:   // swap
   256:   void swapWith(ArrayStack<T> &obj) {
   257:     GrowArray<T>::swapWith(obj);
   258:     int tmp = obj.len; obj.len = this->len; this->len = tmp;
   259:   }
   260: };
   261: 
   262: template <class T>
   263: ArrayStack<T>::~ArrayStack()
   264: {}
   265: 
   266: 
   267: // iterator over contents of an ArrayStack, to make it easier to
   268: // switch between it and SObjList as a representation
   269: template <class T>
   270: class ArrayStackIterNC {
   271:   NO_OBJECT_COPIES(ArrayStackIterNC);   // for now
   272: 
   273: private:     // data
   274:   ArrayStack<T> /*const*/ &arr;   // array being accessed
   275:   int index;                      // current element
   276: 
   277: public:      // funcs
   278:   ArrayStackIterNC(ArrayStack<T> /*const*/ &a) : arr(a), index(0) {}
   279: 
   280:   // iterator actions
   281:   bool isDone() const             { return index >= arr.length(); }
   282:   void adv()                      { xassert(!isDone()); index++; }
   283:   T /*const*/ *data() const       { return &(arr[index]); }
   284: };
   285: 
   286: 
   287: // I want const polymorphism!
   288: 
   289: 
   290: // pop (and discard) a value off a stack at end of scope
   291: template <class T>
   292: class ArrayStackPopper {
   293: private:
   294:   ArrayStack<T> &stk;
   295: 
   296: public:
   297:   ArrayStackPopper(ArrayStack<T> &s) : stk(s) {}
   298:   ArrayStackPopper(ArrayStack<T> &s, T const &pushVal)
   299:     : stk(s) { stk.push(pushVal); }
   300:   ~ArrayStackPopper()
   301:     { stk.pop(); }
   302: };
   303: 
   304: 
   305: // ------------------- ObjArrayStack -----------------
   306: // an ArrayStack of owner pointers
   307: template <class T>
   308: class ObjArrayStack {
   309: private:    // data
   310:   ArrayStack<T*> arr;
   311: 
   312: public:     // funcs
   313:   ObjArrayStack(int initArraySize = 10)
   314:     : arr(initArraySize)
   315:     {}
   316:   ~ObjArrayStack() { deleteAll(); }
   317: 
   318:   void push(T *ptr)          { arr.push(ptr); }
   319:   T *pop()                   { return arr.pop(); }
   320: 
   321:   T const *topC() const      { return arr.top(); }
   322:   T       *top()             { return arr.top(); }
   323: 
   324:   T const * operator[](int index) const  { return arr[index]; }
   325:   T *       operator[](int index)        { return arr[index]; }
   326: 
   327:   int length() const         { return arr.length(); }
   328:   bool isEmpty() const       { return arr.isEmpty(); }
   329:   bool isNotEmpty() const    { return !isEmpty(); }
   330: 
   331:   void deleteTopSeveral(int ct);
   332:   void deleteAll()           { deleteTopSeveral(length()); }
   333: 
   334:   // will not delete any items
   335:   void consolidate()         { arr.consolidate(); }
   336: 
   337:   void swapWith(ObjArrayStack<T> &obj)   { arr.swapWith(obj.arr); }
   338: };
   339: 
   340: 
   341: template <class T>
   342: void ObjArrayStack<T>::deleteTopSeveral(int ct)
   343: {
   344:   while (ct--) {
   345:     delete pop();
   346:   }
   347: }
   348: 
   349: 
   350: // ------------------------- ArrayStackEmbed --------------------------
   351: // This is like ArrayStack, but the first 'n' elements are stored
   352: // embedded in this object, instead of allocated on the heap; in some
   353: // circumstances, this lets us avoid allocating memory in common cases.
   354: //
   355: // For example, suppose you have an algorithm that is usually given a
   356: // small number of elements, say 1 or 2, but occasionally needs to
   357: // work with more.  If you put the array of elements in the heap, then
   358: // even in the common case a heap allocation is required, which is
   359: // bad.  But by using ArrayStackEmbed<T,2>, you can be sure that if
   360: // the number of elements is <= 2 there will be no heap allocation,
   361: // even though you still get a uniform (array-like) interface to all
   362: // the elements.
   363: template <class T, int n>
   364: class ArrayStackEmbed {
   365: private:      // data
   366:   // embedded storage
   367:   T embed[n];
   368: 
   369:   // heap-allocated storage
   370:   GrowArray<T> heap;
   371: 
   372:   // total number of elements in the stack; if this
   373:   // exceeds 'n', then heap.arr is non-NULL
   374:   int len;
   375: 
   376: private:      // funcs
   377:   void bc(int i) const    // bounds-check an index
   378:     { xassert((unsigned)i < (unsigned)len); }
   379: 
   380: public:       // funcs
   381:   ArrayStackEmbed()
   382:     : /*embed is default-init'd*/
   383:       heap(0),    // initially a NULL ptr
   384:       len(0)
   385:   {}
   386:   ~ArrayStackEmbed()
   387:   {}              // heap auto-deallocs its internal data
   388: 
   389:   void push(T const &val)
   390:   {
   391:     if (len < n) {
   392:       embed[len++] = val;
   393:     }
   394:     else {
   395:       heap.setIndexDoubler(len++ - n, val);
   396:     }
   397:   }
   398: 
   399:   T pop()
   400:   {
   401:     xassert(len > 0);
   402:     if (len <= n) {
   403:       return embed[--len];
   404:     }
   405:     else {
   406:       return heap[--len - n];
   407:     }
   408:   }
   409: 
   410:   int length() const
   411:     { return len; }
   412:   bool isEmpty() const
   413:     { return len==0; }
   414:   bool isNotEmpty() const
   415:     { return !isEmpty(); }
   416: 
   417:   // direct element access
   418:   T const &getElt(int i) const
   419:   {
   420:     bc(i);
   421:     if (i < n) {
   422:       return embed[i];
   423:     }
   424:     else {
   425:       return heap[i - n];
   426:     }
   427:   }
   428: 
   429:   T const& operator[] (int i) const
   430:     { return getElt(i); }
   431:   T & operator[] (int i)
   432:     { return const_cast<T&>(getElt(i)); }
   433: 
   434:   T const &top() const
   435:     { return getElt(len-1); }
   436: };
   437: 
   438: 
   439: #endif // ARRAY_H
End C section to elk/sm_array.h[1]
Start C section to elk/sm_arraymap.h[1 /1 ]
     1: #line 468 "./lpsrc/sm.pak"
     2: // arraymap.h            see license.txt for copyright and terms of use
     3: // template class to maintain an array-based map from
     4: // integers to object pointers; the map owns all of
     5: // the objects referred-to
     6: 
     7: // as far as I know, nothing currently uses this file, but
     8: // I believe it *has* been tested (whatever once used it now
     9: // uses something else)
    10: 
    11: #ifndef ARRAYMAP_H
    12: #define ARRAYMAP_H
    13: 
    14: #include "sm_xassert.h"
    15: 
    16: // map: int -> T
    17: template <class T>
    18: class ArrayMap {
    19: private:     // data
    20:   T **map;               // array[0,nextId-1] of owner ptr
    21:   int nextId;            // next id to assign
    22:   int mapSize;           // allocated size of 'map'
    23: 
    24: private:     // funcs
    25:   void make();
    26:   void del();
    27:   void validate(int index) const;
    28: 
    29: public:      // data
    30:   ArrayMap() { make(); }
    31:   ~ArrayMap() { del(); }
    32: 
    33:   // # of elements defined
    34:   int count() const { return nextId; }
    35: 
    36:   // insert a new element and yield its assigned id
    37:   int insert(T * /*owner*/ t);
    38: 
    39:   // retrieve by id
    40:   T const *lookupC(int id) const;
    41:   T *lookup(int id) { return const_cast<T*>(lookupC(id)); }
    42:   T *&lookupRef(int id) { validate(id); return map[id]; }
    43: 
    44:   // throw everything away
    45:   void empty() { del(); make(); }
    46: };
    47: 
    48: template <class T>
    49: void ArrayMap<T>::make()
    50: {
    51:   mapSize = 100;
    52:   nextId = 0;
    53:   map = new T* [mapSize];
    54: }
    55: 
    56: template <class T>
    57: void ArrayMap<T>::del()
    58: {
    59:   for (int i=0; i<nextId; i++) {
    60:     delete map[i];
    61:   }
    62:   delete[] map;
    63: }
    64: 
    65: template <class T>
    66: int ArrayMap<T>::insert(T *t)
    67: {
    68:   if (nextId == mapSize) {
    69:     // make it bigger
    70:     int newMapSize = mapSize * 2;
    71:     T **newMap = new T* [newMapSize];
    72: 
    73:     // copy the old contents to the new map
    74:     for (int i=0; i<mapSize; i++) {
    75:       newMap[i] = map[i];
    76:     }
    77:     mapSize = newMapSize;
    78: 
    79:     // blow away the old map
    80:     delete[] map;
    81: 
    82:     // grab the new map
    83:     map = newMap;
    84:   }
    85: 
    86:   int ret = nextId++;
    87:   map[ret] = t;
    88:   return ret;
    89: }
    90: 
    91: template <class T>
    92: void ArrayMap<T>::validate(int id) const
    93: {
    94:   xassert(0 <= id && id < nextId);
    95: }
    96: 
    97: template <class T>
    98: T const *ArrayMap<T>::lookupC(int id) const
    99: {
   100:   validate(id);
   101:   return map[id];
   102: }
   103: 
   104: 
   105: #define FOREACH_ARRAYMAP(type, array, var)       \
   106:   type const *var = NULL;                        \
   107:   for (int var##id=0;                            \
   108:        var##id<(array).count() &&                \
   109:          (var=(array).lookupC(var##id), true);   \
   110:        var##id++)
   111: 
   112: 
   113: #define FOREACH_ARRAYMAP_INDEX(array, var)   \
   114:   for (int var=0;                            \
   115:        var<(array).count();                  \
   116:        var++)
   117: 
   118: 
   119: #define MUTATE_EACH_ARRAYMAP(type, array, var)   \
   120:   type *var = NULL;                              \
   121:   for (int var##id=0;                            \
   122:        var##id<(array).count() &&                \
   123:          (var=(array).lookup(var##id), true);    \
   124:        var##id++)
   125: 
   126: 
   127: #endif // ARRAYMAP_H
End C section to elk/sm_arraymap.h[1]
Start C section to elk/sm_arrayqueue.h[1 /1 ]
     1: #line 596 "./lpsrc/sm.pak"
     2: // arrayqueue.h
     3: // queue, implemented with an array
     4: 
     5: #ifndef ARRAYQUEUE_H
     6: #define ARRAYQUEUE_H
     7: 
     8: #include "sm_xassert.h"
     9: 
    10: // needed operations on T:
    11: //   T()                       // default ctor
    12: //   operator=(T&)             // assignment
    13: //   bool operator==(T&)       // comparison
    14: 
    15: template <class T>
    16: class ArrayQueue {
    17: private:     // data
    18:   T *arr;                      // working storage
    19:   int arrSize;                 // allocated length of 'arr'
    20:   int head;                    // index of first element to dequeue
    21:   int tail;                    // index+1 of last element to dequeue
    22: 
    23:   // NOTE: If head == tail then the queue is empty.  If head > tail,
    24:   // then the queue elements circularly wrap around the end of 'arr'.
    25:   // At all times, 0 <= head,tail < arrSize.
    26: 
    27: public:      // funcs
    28:   ArrayQueue(int initSize = 10);
    29:   ~ArrayQueue();
    30: 
    31:   // test # of elements in queue
    32:   int length() const
    33:     { return head<=tail? tail-head : arrSize-(head-tail); }
    34:   bool isEmpty() const                  { return head==tail; }
    35:   bool isNotEmpty() const               { return !isEmpty(); }
    36: 
    37:   // add/remove elements in FIFO order
    38:   void enqueue(T const &t);
    39:   T dequeue();
    40: 
    41:   // remove all elements
    42:   void empty()                          { head = tail = 0; }
    43: 
    44:   // access elements of the queue in dequeue order; that is,
    45:   // element 0 is the next element to be dequeued, and element
    46:   // length()-1 is the element most recently enqueued
    47:   //
    48:   // as this interface is O(1), it is the intended method
    49:   // of iterating over the elements in the queue
    50:   T const &eltC(int index) const;
    51:   T &elt(int index)                     { return const_cast<T&>(eltC(index)); }
    52:   T &operator[] (int index)             { return elt(index); }
    53:   T const &operator[] (int index) const { return eltC(index); }
    54: 
    55:   // reverse the sequence of stored elements
    56:   void reverse();
    57: 
    58:   // true if a specific element is among the queue elements
    59:   bool contains(T const &t) const;
    60: };
    61: 
    62: 
    63: template <class T>
    64: ArrayQueue<T>::ArrayQueue(int initSize)
    65: {
    66:   // initial size must be positive, since array growth is
    67:   // simply by doubling the size
    68:   xassert(initSize > 0);
    69: 
    70:   arr = new T[initSize];
    71:   arrSize = initSize;
    72:   head = tail = 0;
    73: }
    74: 
    75: 
    76: template <class T>
    77: ArrayQueue<T>::~ArrayQueue()
    78: {
    79:   delete[] arr;
    80: }
    81: 
    82: 
    83: template <class T>
    84: void ArrayQueue<T>::enqueue(T const &t)
    85: {
    86:   if (length() == arrSize-1) {
    87:     // must expand the queue
    88: 
    89:     // make new array
    90:     int newArrSize = arrSize * 2;
    91:     T *newArr = new T[newArrSize];
    92: 
    93:     // copy elements sequentially
    94:     int oldLength = length();
    95:     for (int i=0; i<oldLength; i++) {
    96:       newArr[i] = eltC(i);
    97:     }
    98: 
    99:     // discard old array
   100:     delete[] arr;
   101: 
   102:     // put new one in its place
   103:     arr = newArr;
   104:     arrSize = newArrSize;
   105:     head = 0;
   106:     tail = oldLength;
   107:   }
   108: 
   109:   // store the new element where 'tail' points
   110:   arr[tail] = t;
   111: 
   112:   // advance 'tail'
   113:   if (++tail == arrSize) {
   114:     tail = 0;
   115:   }
   116: }
   117: 
   118: 
   119: template <class T>
   120: T ArrayQueue<T>::dequeue()
   121: {
   122:   if (isEmpty()) {
   123:     xfailure("attempt to dequeue an empty queue");
   124:   }
   125: 
   126:   // advance 'head' while yielding the element it currently points at;
   127:   // avoid making an intermediate copy (for performance)
   128:   if (head == arrSize-1) {
   129:     head = 0;
   130:     return arr[arrSize-1];
   131:   }
   132:   else {
   133:     return arr[head++];
   134:   }
   135: }
   136: 
   137: 
   138: template <class T>
   139: T const &ArrayQueue<T>::eltC(int index) const
   140: {
   141:   xassert(0 <= index && index < length());
   142: 
   143:   if (head+index < arrSize) {
   144:     return arr[head+index];
   145:   }
   146:   else {
   147:     return arr[head+index - arrSize];
   148:   }
   149: }
   150: 
   151: 
   152: template <class T>
   153: void ArrayQueue<T>::reverse()
   154: {
   155:   int i = 0, j = length()-1;
   156:   while (i < j) {
   157:     // swap i,j elements
   158:     T tmp = elt(i);
   159:     elt(i) = elt(j);
   160:     elt(j) = tmp;
   161: 
   162:     i++;
   163:     j--;
   164:   }
   165: }
   166: 
   167: 
   168: template <class T>
   169: bool ArrayQueue<T>::contains(T const &t) const
   170: {
   171:   int len=length();
   172:   for (int i=0; i<len; i++) {
   173:     if (t == eltC(i)) {
   174:       return true;
   175:     }
   176:   }
   177:   return false;
   178: }
   179: 
   180: 
   181: #endif // ARRAYQUEUE_H
End C section to elk/sm_arrayqueue.h[1]
Start C section to elk/sm_astlist.h[1 /1 ]
     1: #line 778 "./lpsrc/sm.pak"
     2: // astlist.h            see license.txt for copyright and terms of use
     3: // owner list wrapper around VoidTailList
     4: // name 'AST' is because the first application is in ASTs
     5: 
     6: #ifndef ASTLIST_H
     7: #define ASTLIST_H
     8: 
     9: #include "sm_vdtllist.h"
    10: 
    11: template <class T> class ASTListIter;
    12: template <class T> class ASTListIterNC;
    13: 
    14: // a list which owns the items in it (will deallocate them), and
    15: // has constant-time access to the last element
    16: template <class T>
    17: class ASTList {
    18: private:
    19:   friend class ASTListIter<T>;
    20:   friend class ASTListIterNC<T>;
    21: 
    22: protected:
    23:   VoidTailList list;                    // list itself
    24: 
    25: private:
    26:   ASTList(ASTList const &obj);          // not allowed
    27: 
    28: public:
    29:   ASTList()                             : list() {}
    30:   ~ASTList()                            { deleteAll(); }
    31: 
    32:   // ctor to make singleton list; often quite useful
    33:   ASTList(T *elt)                       : list() { prepend(elt); }
    34: 
    35:   // stealing ctor; among other things, since &src->list is assumed to
    36:   // point at 'src', this class can't have virtual functions;
    37:   // these ctors delete 'src'
    38:   ASTList(ASTList<T> *src)              : list(&src->list) {}
    39:   void steal(ASTList<T> *src)           { deleteAll(); list.steal(&src->list); }
    40: 
    41:   // selectors
    42:   int count() const                     { return list.count(); }
    43:   bool isEmpty() const                  { return list.isEmpty(); }
    44:   bool isNotEmpty() const               { return list.isNotEmpty(); }
    45:   T *nth(int which)                     { return (T*)list.nth(which); }
    46:   T const *nthC(int which) const        { return (T const*)list.nth(which); }
    47:   T *first()                            { return (T*)list.first(); }
    48:   T const *firstC() const               { return (T const*)list.first(); }
    49:   T *last()                             { return (T*)list.last(); }
    50:   T const *lastC() const                { return (T const*)list.last(); }
    51: 
    52:   // insertion
    53:   void prepend(T *newitem)              { list.prepend(newitem); }
    54:   void append(T *newitem)               { list.append(newitem); }
    55:   void insertAt(T *newitem, int index)  { list.insertAt(newitem, index); }
    56:   void concat(ASTList<T> &tail)         { list.concat(tail.list); }
    57: 
    58:   // removal
    59:   T *removeFirst()                      { return (T*)list.removeFirst(); }
    60:   T *removeLast()                       { return (T*)list.removeLast(); }
    61:   T *removeAt(int index)                { return (T*)list.removeAt(index); }
    62:   void removeItem(T *item)              { list.removeItem((void*)item); }
    63: 
    64:   // this one is awkwardly named to remind the user that it's
    65:   // contrary to the usual intent of this class
    66:   void removeAll_dontDelete()           { return list.removeAll(); }
    67: 
    68:   // deletion
    69:   void deleteFirst()                    { delete (T*)list.removeFirst(); }
    70:   void deleteAll();
    71: 
    72:   // list-as-set: selectors
    73:   int indexOf(T const *item) const      { return list.indexOf((void*)item); }
    74:   int indexOfF(T const *item) const     { return list.indexOfF((void*)item); }
    75:   bool contains(T const *item) const    { return list.contains((void*)item); }
    76: 
    77:   // list-as-set: mutators
    78:   bool prependUnique(T *newitem)        { return list.prependUnique(newitem); }
    79:   bool appendUnique(T *newitem)         { return list.appendUnique(newitem); }
    80: 
    81:   // debugging: two additional invariants
    82:   void selfCheck() const                { list.selfCheck(); }
    83: };
    84: 
    85: 
    86: template <class T>
    87: void ASTList<T>::deleteAll()
    88: {
    89:   while (!list.isEmpty()) {
    90:     deleteFirst();
    91:   }
    92: }
    93: 
    94: 
    95: template <class T>
    96: class ASTListIter {
    97: protected:
    98:   VoidTailListIter iter;      // underlying iterator
    99: 
   100: public:
   101:   ASTListIter(ASTList<T> const &list) : iter(list.list) {}
   102:   ~ASTListIter()                       {}
   103: 
   104:   void reset(ASTList<T> const &list)   { iter.reset(list.list); }
   105: 
   106:   // iterator copying; generally safe
   107:   ASTListIter(ASTListIter const &obj)             : iter(obj.iter) {}
   108:   ASTListIter& operator=(ASTListIter const &obj)  { iter = obj.iter;  return *this; }
   109: 
   110:   // iterator actions
   111:   bool isDone() const                   { return iter.isDone(); }
   112:   void adv()                            { iter.adv(); }
   113:   T const *data() const                 { return (T const*)iter.data(); }
   114: };
   115: 
   116: #define FOREACH_ASTLIST(T, list, iter) \
   117:   for(ASTListIter<T> iter(list); !iter.isDone(); iter.adv())
   118: 
   119: 
   120: // version of the above, but for non-const-element traversal
   121: template <class T>
   122: class ASTListIterNC {
   123: protected:
   124:   VoidTailListIter iter;      // underlying iterator
   125: 
   126: public:
   127:   ASTListIterNC(ASTList<T> &list)      : iter(list.list) {}
   128:   ~ASTListIterNC()                     {}
   129: 
   130:   void reset(ASTList<T> &list)         { iter.reset(list.list); }
   131: 
   132:   // iterator copying; generally safe
   133:   ASTListIterNC(ASTListIterNC const &obj)             : iter(obj.iter) {}
   134:   ASTListIterNC& operator=(ASTListIterNC const &obj)  { iter = obj.iter;  return *this; }
   135: 
   136:   // iterator actions
   137:   bool isDone() const                   { return iter.isDone(); }
   138:   void adv()                            { iter.adv(); }
   139:   T *data() const                       { return (T*)iter.data(); }
   140: 
   141:   // iterator mutation; use with caution
   142:   void setDataLink(T *newData)          { iter.setDataLink((void*)newData); }
   143: };
   144: 
   145: #define FOREACH_ASTLIST_NC(T, list, iter) \
   146:   for(ASTListIterNC<T> iter(list); !iter.isDone(); iter.adv())
   147: 
   148: 
   149: // this function is somewhat at odds with the nominal purpose
   150: // of ASTLists, but I need it in a weird situation so ...
   151: template <class T>
   152: ASTList<T> *shallowCopy(ASTList<T> *src)
   153: {
   154:   ASTList<T> *ret = new ASTList<T>;
   155:   FOREACH_ASTLIST_NC(T, *src, iter) {
   156:     ret->append(iter.data());
   157:   }
   158:   return ret;
   159: }
   160: 
   161: 
   162: #endif // ASTLIST_H
End C section to elk/sm_astlist.h[1]
Start C section to elk/sm_autofile.h[1 /1 ]
     1: #line 941 "./lpsrc/sm.pak"
     2: // autofile.h            see license.txt for copyright and terms of use
     3: // little wrapper around FILE*
     4: 
     5: #ifndef SMUTIL_H
     6: #define SMUTIL_H
     7: 
     8: #include <stdio.h>      // FILE
     9: 
    10: 
    11: // fopen, but throw an XOpen exception (see exc.h) on failure instead
    12: // of returning NULL
    13: FILE *xfopen(char const *fname, char const *mode);
    14: 
    15: 
    16: // automatically close a file in the destructor
    17: class AutoFclose {
    18: private:       // data
    19:   FILE *fp;
    20: 
    21: private:       // disallowed
    22:   AutoFclose(AutoFclose&);
    23:   void operator=(AutoFclose&);
    24: 
    25: public:
    26:   AutoFclose(FILE *f) : fp(f) {}
    27:   ~AutoFclose() { fclose(fp); }
    28: 
    29:   // may as well allow access to my storage
    30:   FILE *getFP() { return fp; }
    31: };
    32: 
    33: 
    34: // simple wrapper on FILE*
    35: class AutoFILE : private AutoFclose {
    36: public:
    37:   // open, throwing an XOpen exception on failure
    38:   AutoFILE(char const *fname, char const *mode);
    39: 
    40:   // close the file
    41:   ~AutoFILE();
    42: 
    43:   // behave like FILE* in between
    44:   operator FILE* () { return getFP(); }
    45: };
    46: 
    47: 
    48: #endif // SMUTIL_H
End C section to elk/sm_autofile.h[1]
Start C section to elk/sm_bflatten.h[1 /1 ]
     1: #line 990 "./lpsrc/sm.pak"
     2: // bflatten.h            see license.txt for copyright and terms of use
     3: // binary file flatten implementation
     4: 
     5: #ifndef BFLATTEN_H
     6: #define BFLATTEN_H
     7: 
     8: #include "sm_flatten.h"
     9: #include "sm_ohashtbl.h"
    10: #include <stdio.h>        // FILE
    11: 
    12: class BFlatten : public Flatten {
    13: private:     // data
    14:   FILE *fp;               // file being read/written
    15:   bool readMode;          // true=read, false=write
    16: 
    17:   struct OwnerMapping {
    18:     void *ownerPtr;       // a pointer
    19:     int intName;          // a unique integer name
    20:   };
    21:   OwnerHashTable<OwnerMapping> ownerTable;      // owner <-> int mapping
    22:   int nextUniqueName;     // counter for making int names
    23: 
    24: private:     // funcs
    25:   static void const* getOwnerPtrKeyFn(OwnerMapping *data);
    26:   static void const* getIntNameKeyFn(OwnerMapping *data);
    27: 
    28: public:      // funcs
    29:   BFlatten(char const *fname, bool reading);
    30:   virtual ~BFlatten();
    31: 
    32:   // Flatten funcs
    33:   virtual bool reading() const { return readMode; }
    34:   virtual void xferSimple(void *var, unsigned len);
    35:   virtual void noteOwner(void *ownerPtr);
    36:   virtual void xferSerf(void *&serfPtr, bool nullable=false);
    37: };
    38: 
    39: 
    40: // for debugging, write and then read something
    41: template <class T>
    42: T *writeThenRead(T &obj)
    43: {
    44:   char const *fname = "flattest.tmp";
    45: 
    46:   // write
    47:   {
    48:     BFlatten out(fname, false /*reading*/);
    49:     obj.xfer(out);
    50:   }
    51: 
    52:   // read
    53:   BFlatten in(fname, true /*reading*/);
    54:   T *ret = new T(in);
    55:   ret->xfer(in);
    56: 
    57:   remove(fname);
    58: 
    59:   return ret;
    60: }
    61: 
    62: #endif // BFLATTEN_H
End C section to elk/sm_bflatten.h[1]
Start C section to elk/sm_bit2d.h[1 /1 ]
     1: #line 1053 "./lpsrc/sm.pak"
     2: // bit2d.h            see license.txt for copyright and terms of use
     3: // 2-d array of bits
     4: 
     5: #ifndef __BIT2D_H
     6: #define __BIT2D_H
     7: 
     8: #include "sm_typ.h"
     9: #include "sm_point.h"
    10: 
    11: class Flatten;
    12: 
    13: class Bit2d {
    14: private:     // data
    15:   byte *data;       // bits; [0..stride-1] is first row, etc.
    16:   bool owning;      // when false, 'data' is not owned by this object
    17:   point size;       // size.x is # of cols, size.y is # of rows
    18:   int stride;       // bytes between starts of adjacent rows;
    19:                     // computable from size.x but stored for quick access
    20: 
    21: private:     // funcs
    22:   byte *byteptr(point const &p)               { return data + p.y * stride + (p.x>>3); }
    23:   byte const *byteptrc(point const &p) const  { return data + p.y * stride + (p.x>>3); }
    24: 
    25:   // this is the number of bytes allocated in 'data'
    26:   int datasize() const                        { return size.y * stride; }
    27: 
    28: public:      // funcs
    29:   // NOTE: does *not* clear the bitmap!  use 'setall' to do that
    30:   Bit2d(point const &aSize);
    31:   Bit2d(Bit2d const &obj);
    32: 
    33:   Bit2d& operator= (Bit2d const &obj);     // sizes must be equal already
    34:   ~Bit2d();
    35: 
    36:   Bit2d(Flatten&);
    37:   void xfer(Flatten &flat);
    38: 
    39:   bool okpt(point const &p) const    { return p.gtez() && p < size; }
    40:   point const &Size() const          { return size; }
    41: 
    42:   bool operator== (Bit2d const &obj) const;     // compare sizes and data
    43: 
    44:   // bit access (these were inline earlier, but they expand to a huge amount
    45:   // of code (more than 100 bytes), so I've un-inlined them)
    46:   int get(point const &p) const;
    47:   void set(point const &p);     // to 1
    48:   void reset(point const &p);   // to 0
    49:   void setto(point const &p, int val);
    50:   void toggle(point const &p);
    51: 
    52:   // set the bit, but return what it was previously
    53:   int testAndSet(point const &p);
    54: 
    55:   // set everything
    56:   void setall(int val);
    57: 
    58:   // debugging
    59:   void print() const;
    60: 
    61:   // bit of a hack: I want to be able to save the data as code which,
    62:   // when compiled, will build a bit2d from static data.. for this
    63:   // I need access to some private fields and a special ctor
    64:   Bit2d(byte * /*serf*/ data, point const &size, int stride);
    65:   byte *private_data() { return data; }
    66:   int private_datasize() const { return datasize(); }
    67:   int private_stride() const { return stride; }
    68: };
    69: 
    70: #endif // __BIT2D_H
    71: 
End C section to elk/sm_bit2d.h[1]
Start C section to elk/sm_bitarray.h[1 /1 ]
     1: #line 1125 "./lpsrc/sm.pak"
     2: // bitarray.h            see license.txt for copyright and terms of use
     3: // one-dimensional array of bits
     4: 
     5: #ifndef BITARRAY_H
     6: #define BITARRAY_H
     7: 
     8: #include "sm_xassert.h"
     9: 
    10: class Flatten;            // flatten.h
    11: 
    12: class BitArray {
    13: private:    // data
    14:   unsigned char *bits;
    15:   int numBits;              // # of bits in the array
    16: 
    17: private:    // disallowed for now
    18:   BitArray(BitArray&);
    19:   void operator=(BitArray&);
    20: 
    21: private:    // funcs
    22:   void bc(int i) const { xassert((unsigned)i < (unsigned)numBits); }
    23:   int allocdBytes() const { return (numBits+7) / 8; }
    24: 
    25: public:     // funcs
    26:   BitArray(int n);          // create with given # of bits, initially zeroed
    27:   ~BitArray();
    28: 
    29:   BitArray(Flatten&);
    30:   void xfer(Flatten &flat);
    31: 
    32:   // test a bit, return 0 or 1
    33:   int test(int i) const
    34:     { bc(i); return ((bits[i >> 3]) >> (i & 7)) & 1; }
    35: 
    36:   // set a bit to a specific value
    37:   void set(int i)
    38:     { bc(i); bits[i >> 3] |= (1 << (i & 7)); }
    39:   void reset(int i)
    40:     { bc(i); bits[i >> 3] &= ~(1 << (i & 7)); }
    41: 
    42:   // set a bit to an arbitrary value
    43:   void setTo(int i, int val) {
    44:     if (val) {
    45:       set(i);
    46:     }
    47:     else {
    48:       reset(i);
    49:     }
    50:   }
    51: 
    52:   // clear all bits
    53:   void clearAll();
    54: };
    55: 
    56: 
    57: #endif // BITARRAY_H
End C section to elk/sm_bitarray.h[1]
Start C section to elk/sm_boxprint.h[1 /1 ]
     1: #line 1183 "./lpsrc/sm.pak"
     2: // boxprint.h
     3: // another pretty-printing module, this one based on the box model
     4: // described at http://caml.inria.fr/FAQ/format-eng.html
     5: 
     6: #ifndef BOXPRINT_H
     7: #define BOXPRINT_H
     8: 
     9: #include "sm_str.h"
    10: #include "sm_astlist.h"
    11: #include "sm_array.h"
    12: 
    13: 
    14: // fwd
    15: class BoxPrint;
    16: 
    17: 
    18: // manages the process of rendering a boxprint tree to a sm_string
    19: class BPRender {
    20: public:
    21:   // output sm_string
    22:   sm_stringBuilder sb;
    23: 
    24:   // right margin column; defaults to 72
    25:   int margin;
    26: 
    27:   // column for next output; equal to the number of characters
    28:   // after the last newline in 'sb'
    29:   int curCol;
    30: 
    31:   // text to begin every line with; not counted towards column
    32:   // counts; defaults to ""
    33:   sm_string lineStartText;
    34: 
    35: public:
    36:   BPRender();
    37:   ~BPRender();
    38: 
    39:   // chars in the current line
    40:   int getCurCol() const { return curCol; }
    41: 
    42:   // chars remaining on current line before the margin; might
    43:   // be negative if the input didn't have enough breaks
    44:   int remainder() const { return margin - getCurCol(); }
    45: 
    46:   // add some text (that doesn't include newlines) to the output
    47:   void add(char const *text);
    48: 
    49:   // add a newline, plus indentation to get to column 'ind'
    50:   void breakLine(int ind);
    51: 
    52:   // take the sm_string out of the rendering engine, replacing it
    53:   // with the empty sm_string
    54:   sm_string takeString() {
    55:     sm_string ret(sb);
    56:     reset();
    57:     return ret;
    58:   }
    59: 
    60:   // just clear the buffer to its original state; must do this
    61:   // manually after changing 'lineStartText'
    62:   void reset();
    63: 
    64:   // take the tree out of a boxprint builder, convert it to a sm_string,
    65:   // and delete the tree
    66:   sm_string takeAndRender(BoxPrint &bld);
    67: };
    68: 
    69: 
    70: // interface for elements in a boxprint tree
    71: class BPElement {
    72: public:
    73:   // if no breaks are taken, compute the # of columns
    74:   virtual int oneLineWidth()=0;
    75: 
    76:   // render this element as a sm_string with newlines, etc.
    77:   virtual void render(BPRender &mgr)=0;
    78: 
    79:   // true if this element is a BPBreak and is enabled; returns false
    80:   // by default
    81:   virtual bool isBreak() const;
    82: 
    83:   // print the boxprint tree; for debugging code that produces them;
    84:   // these methods do not emit leading or trailing whitespace
    85:   virtual void debugPrint(std::ostream &os, int ind) const =0;
    86: 
    87:   // deallocate the element
    88:   virtual ~BPElement();
    89: };
    90: 
    91: 
    92: // leaf in the tree: text to print
    93: class BPText : public BPElement {
    94: public:
    95:   sm_string text;
    96: 
    97: public:
    98:   BPText(char const *t);
    99:   ~BPText();
   100: 
   101:   // BPElement funcs
   102:   virtual int oneLineWidth();
   103:   virtual void render(BPRender &mgr);
   104:   virtual void debugPrint(std::ostream &os, int ind) const;
   105: };
   106: 
   107: 
   108: // leaf in the tree: a "break", which might end up being a
   109: // space or a newline+indentation
   110: class BPBreak : public BPElement {
   111: public:
   112:   // When true, this is a conditional break, and whether it is taken
   113:   // or not depends on the prevailing break strategy of the box in
   114:   // which it is located.  When false, the break is never taken, so
   115:   // this is effectively just a space.
   116:   bool enabled;
   117: 
   118:   // Nominally, when a break is taken, the indentation used is such
   119:   // that the next char in the box is directly below the first char
   120:   // in the box.  When this break is passed, however, it can add to
   121:   // that nominal indent of 0; these adjustments accumulate as the
   122:   // box is rendered.
   123:   int indent;
   124: 
   125: public:
   126:   BPBreak(bool e, int i);
   127:   ~BPBreak();
   128: 
   129:   // BPElement funcs
   130:   virtual int oneLineWidth();
   131:   virtual void render(BPRender &mgr);
   132:   virtual bool isBreak() const;
   133:   virtual void debugPrint(std::ostream &os, int ind) const;
   134: };
   135: 
   136: 
   137: // kinds of boxes
   138: enum BPKind {
   139:   // enabled breaks are always taken
   140:   BP_vertical,
   141: 
   142:   // enabled breaks are individually taken or not taken depending
   143:   // on how much room is available; "hov"
   144:   BP_sequence,
   145: 
   146:   // either all enabled breaks are taken, or none are taken; "h/v"
   147:   BP_correlated,
   148: 
   149:   // # of kinds, also used to signal the end of a box in some cases
   150:   NUM_BPKINDS
   151: };
   152: 
   153: // internal node in the tree: a list of subtrees, some of which
   154: // may be breaks
   155: class BPBox : public BPElement {
   156: public:
   157:   // subtrees
   158:   ASTList<BPElement> elts;
   159: 
   160:   // break strategy for this box
   161:   BPKind kind;
   162: 
   163: public:
   164:   BPBox(BPKind k);
   165:   ~BPBox();
   166: 
   167:   // BPElement funcs
   168:   virtual int oneLineWidth();
   169:   virtual void render(BPRender &mgr);
   170:   virtual void debugPrint(std::ostream &os, int ind) const;
   171: };
   172: 
   173: 
   174: // assists in the process of building a box tree by providing
   175: // a number of syntactic shortcuts
   176: class BoxPrint {
   177: public:      // types
   178:   // additional command besides BPKind
   179:   enum Cmd {
   180:     sp,          // insert disabled break
   181:     br,          // insert enabled break
   182:     ind,         // ibr(levelIndent)
   183:     und,         // ibr(-levelIndent) ("unindent")
   184:   };
   185: 
   186:   // insert enabled break with indentation
   187:   struct IBreak {
   188:     int indent;
   189:     IBreak(int i) : indent(i) {}
   190:     // use default copy ctor
   191:   };
   192: 
   193:   // operator sequence
   194:   struct Op {
   195:     char const *text;
   196:     Op(char const *t) : text(t) {}
   197:     // default copy ctor
   198:   };
   199: 
   200: private:     // data
   201:   // stack of open boxes; always one open vert box at the top
   202:   ObjArrayStack<BPBox> boxStack;
   203: 
   204: public:      // data
   205:   // convenient names for the box kinds
   206:   static BPKind const vert;       // = BP_vertical
   207:   static BPKind const seq;        // = BP_sequence
   208:   static BPKind const hv;         // = BP_correlated ("h/v")
   209:   static BPKind const end;        // = NUM_BPKINDS
   210: 
   211:   // indentation amount for the ind/und commands; defaults to 2
   212:   int levelIndent;
   213: 
   214: private:     // funcs
   215:   // innermost box being built
   216:   BPBox *box() { return boxStack.top(); }
   217: 
   218: public:      // funcs
   219:   BoxPrint();
   220:   ~BoxPrint();
   221: 
   222:   // append another element to the current innermost box
   223:   void append(BPElement *elt);
   224: 
   225:   // add BPText nodes to current box
   226:   BoxPrint& operator<< (int i);
   227:   BoxPrint& operator<< (char const *s);
   228: 
   229:   // open/close boxes
   230:   BoxPrint& operator<< (BPKind k);
   231: 
   232:   // insert breaks
   233:   BoxPrint& operator<< (Cmd c);
   234: 
   235:   // insert break with indentation
   236:   static IBreak ibr(int i) { return IBreak(i); }
   237:   BoxPrint& operator<< (IBreak b);
   238: 
   239:   // op(text) is equivalent to sp << text << br
   240:   static Op op(char const *text) { return Op(text); }
   241:   BoxPrint &operator << (Op o);
   242: 
   243:   // take the accumulated box tree out; all opened boxes must have
   244:   // been closed; the builder is left in a state where it can be used
   245:   // to build a new tree if desired, or it can be simply destroyed
   246:   BPBox* /*owner*/ takeTree();
   247: 
   248:   // print the current stack of trees
   249:   void debugPrint(std::ostream &os) const;
   250:   void debugPrintCout() const;      // for gdb
   251: };
   252: 
   253: 
   254: #endif // BOXPRINT_H
End C section to elk/sm_boxprint.h[1]
Start C section to elk/sm_breaker.h[1 /1 ]
     1: #line 1438 "./lpsrc/sm.pak"
     2: // breaker.h            see license.txt for copyright and terms of use
     3: // function stub through which critical event code flow is directed
     4: //   for easy breakpoints
     5: // Scott McPeak, 1997,1998  This file is public domain.
     6: 
     7: #ifndef __BREAKER_H
     8: #define __BREAKER_H
     9: 
    10: void breaker();
    11: 
    12: // bassert = breaker assert; failure simply calls breaker, which is
    13: // a breakpoint in the debugger and is ignored when not in debugger;
    14: // useful mainly for places I want to ensure something is true during
    15: // initial testing, but after that it's ok if it's false
    16: template <class T>          // allow possibly null pointers, etc
    17: inline void bassert(T cond)
    18: {
    19:   if (!cond) {
    20:     breaker();
    21:   }
    22: }
    23: 
    24: 
    25: // this will call breaker on the first pass, but not any subsequent (unless
    26: // it's called MAXINT*2 times...)
    27: #define BREAK_FIRST_PASS     \
    28:   {                          \
    29:     static int passCount=0;  \
    30:     bassert(passCount++);    \
    31:   } /*no semicolon*/
    32: 
    33: 
    34: // this is obsolete...
    35: void _breaker_assert(char * __cond, char * __file, int __line);
    36:   // this will be called on failed assertions instead of _assert
    37:   // only if BREAKER_ASSERT is defined (due to a modification to
    38:   // assert.h directly)
    39: 
    40: #endif // __BREAKER_H
    41: 
End C section to elk/sm_breaker.h[1]
Start C section to elk/sm_ckheap.h[1 /1 ]
     1: #line 1480 "./lpsrc/sm.pak"
     2: // ckheap.h            see license.txt for copyright and terms of use
     3: // interface to check heap integrity, etc.
     4: 
     5: #ifndef CKHEAP_H
     6: #define CKHEAP_H
     7: 
     8: #ifdef __cplusplus
     9: extern "C" {
    10: #endif
    11: 
    12: 
    13: // check heap integrity, and fail an assertion if it's bad
    14: void checkHeap();
    15: 
    16: // check that a given pointer is a valid allocated object;
    17: // fail assertion if not
    18: void checkHeapNode(void *node);
    19: 
    20: // prints allocation statistics to stderr
    21: void malloc_stats();
    22: 
    23: // count # of malloc/free calls in program
    24: unsigned numMallocCalls();
    25: unsigned numFreeCalls();
    26: 
    27: 
    28: // actions the heap walk iterator might request
    29: enum HeapWalkOpts {
    30:   HW_GO   = 0,         // keep going
    31:   HW_STOP = 1,         // stop iterating
    32:   HW_FREE = 2,         // free the block I just examined
    33: };
    34: 
    35: // function for walking the heap
    36: //   block:   pointer to the malloc'd block of memory
    37: //   size:    # of bytes in the block; possibly larger than
    38: //            what was requested
    39: //   returns: bitwise OR of HeapWalkOpts options
    40: // NOTE: you cannot call malloc or free inside this function
    41: // (you can cause 'block' to be freed by returning HW_FREE)
    42: typedef enum HeapWalkOpts (*HeapWalkFn)(void *block, int size);
    43: 
    44: // heap walk entry
    45: void walkMallocHeap(HeapWalkFn func);
    46: 
    47: 
    48: #ifdef __cplusplus
    49: } // extern "C"
    50: #endif
    51: 
    52: #endif // CKHEAP_H
End C section to elk/sm_ckheap.h[1]
Start C section to elk/sm_crc.h[1 /1 ]
     1: #line 1533 "./lpsrc/sm.pak"
     2: // crc.h            see license.txt for copyright and terms of use
     3: // simple crc function
     4: 
     5: #ifndef __CRC_H
     6: #define __CRC_H
     7: 
     8: unsigned long crc32(unsigned char const *data, int length);
     9: 
    10: #endif // __CRC_H
    11: 
End C section to elk/sm_crc.h[1]
Start C section to elk/sm_datablok.h[1 /1 ]
     1: #line 1545 "./lpsrc/sm.pak"
     2: // datablok.h            see license.txt for copyright and terms of use
     3: // arbitrary block of data
     4: // Scott McPeak, 1998-2000  This file is public domain.
     5: 
     6: #ifndef __DATABLOK_H
     7: #define __DATABLOK_H
     8: 
     9: #include "sm_typ.h"
    10: 
    11: class DataBlock {
    12: private:      // data
    13:   byte *data;                  // data itself (may be NULL)
    14:   int dataLen;                 // length of data, starting at data[0]
    15:   int allocated;               // amount of memory allocated at 'data'
    16: 
    17:   // invariants: 0 <= dataLen <= allocated
    18:   //             (data==NULL) == (allocated==0)
    19: 
    20:   // endpost: 'data' will be kept allocated with one extra byte at the
    21:   // end, where an endpost byte is written.  thus, we have another
    22:   // invariant:
    23:   //             (data!=NULL) implies data[allocated] == endpost
    24:   static byte const endpost;
    25: 
    26: private:      // funcs
    27:   void init(int allocatedSize);
    28:     // base ctor
    29: 
    30:   static byte *allocate(int size);
    31:     // allocate a block of memory, writing endpost
    32: 
    33:   void copyCtorShared(DataBlock const &obj);
    34:     // shared by both copy constructors (actually, only one is the true
    35:     // copy ctor...)
    36: 
    37:   void ctor(byte const *srcData, int dataLen);
    38:   void ctor(byte const *srcData, int dataLen, int allocatedSize);
    39:     // shared ctor calls as a workaround for char casting problems
    40: 
    41:   void selfCheck() const;
    42:     // confirm that invariants are true
    43: 
    44: public:       // funcs
    45:   // constructors
    46:   DataBlock(int allocatedSize = 0);
    47:     // make an empty datablock holder; when allocatedSize is 0, 'data'
    48:     // is initially set to NULL
    49: 
    50:   EXPLICIT DataBlock(char const *srcString);
    51:     // make a copy of 'srcString' data, which is null-terminated
    52: 
    53:   DataBlock(byte const *srcData, int dataLen) { ctor(srcData, dataLen); }
    54:   DataBlock(char const *srcData, int dataLen) { ctor((byte const*)srcData, dataLen); }
    55:     // make a copy of 'srcData', which is 'dataLen' bytes long
    56: 
    57:   DataBlock(byte const *srcData, int dataLen, int allocatedSize)
    58:     { ctor(srcData, dataLen, allocatedSize); }
    59:   DataBlock(char const *srcData, int dataLen, int allocatedSize)
    60:     { ctor((byte const*)srcData, dataLen, allocatedSize); }
    61:     // make a copy of 'srcData', which is 'dataLen' bytes long, in a buffer
    62:     // that is 'allocatedSize' bytes long
    63: 
    64:   DataBlock(DataBlock const &obj);
    65:     // copy data, allocate same amount as 'obj'
    66: 
    67:   DataBlock(DataBlock const &obj, int minToAllocate);
    68:     // copy obj's contents; allocate either obj.getAllocated() or
    69:     // minToAllocate, whichever is larger (this turns out to be a
    70:     // common idiom)
    71: 
    72:   ~DataBlock();
    73: 
    74:   // selectors
    75:   byte const *getDataC() const { return data; }
    76:   int getDataLen() const { return dataLen; }
    77:   int getAllocated() const { return allocated; }
    78: 
    79:   bool dataEqual(DataBlock const &obj) const;
    80:     // compares data length and data-length bytes of data
    81: 
    82:   bool allEqual(DataBlock const &obj) const;
    83:     // compares data, length, and allocation length
    84: 
    85:   bool operator== (DataBlock const &obj) const
    86:     { return dataEqual(obj); }
    87:   bool operator!= (DataBlock const &obj) const
    88:     { return !operator==(obj); }
    89:     // SM, 1/24/99: with the coding of blokutil, I've finally clarified that
    90:     // allocation length is a concern of efficiency, not correctness, and so
    91:     // have changed the meaning of == to just look at data.  The old behavior
    92:     // is available as 'allEqual()'.
    93: 
    94:   // mutators
    95:   byte *getData() { return data; }
    96:   void setDataLen(int newLen);
    97:     // asserts that 0 <= newLen <= allocated
    98:   void setAllocated(int newAllocated);     // i.e. realloc
    99:   void addNull();
   100:     // add a null ('\0') to the end; there must be sufficient allocated space
   101: 
   102:   void changeDataLen(int changeAmount)
   103:     { setDataLen(getDataLen() + changeAmount); }
   104: 
   105:   void ensureAtLeast(int minAllocated);
   106:     // if 'allocated' is currently less than minAllocated, then
   107:     // set 'allocated' to minAllocated (preserving existing contents)
   108: 
   109:   void growDataLen(int changeAmount);
   110:     // grows allocated data if necessary, whereas changeDataLen will throw
   111:     // an exception if there isn't already enough allocated space
   112: 
   113:   void setFromString(char const *srcString);
   114:   void setFromBlock(byte const *srcData, int dataLen);
   115:   void setFromBlock(char const *srcData, int dataLen)
   116:     { setFromBlock((byte const*)srcData, dataLen); }
   117: 
   118:   DataBlock& operator= (DataBlock const &obj);
   119:     // causes data AND allocation length equality
   120: 
   121:   // convenient file read/write
   122:   void writeToFile(char const *fname) const;
   123:   void readFromFile(char const *fname);
   124: 
   125:   // for debugging
   126:   enum { DEFAULT_PRINT_BYTES = 16 };
   127:   void print(char const *label = NULL,
   128:              int bytesPerLine = DEFAULT_PRINT_BYTES) const;
   129:     // write a simple representation to stdout
   130:     // if label is not NULL, the data is surrounded by '---'-style delimiters
   131: 
   132:   void dontPrint(char const *label = NULL,
   133:                  int bytesPerLine = DEFAULT_PRINT_BYTES) const;
   134:     // does nothing; useful for two reasons:
   135:     //   1. lets me define macros that expand to 'print' during debug
   136:     //      and dontPrint during non-debug
   137:     //   2. plays a vital role in a g++ bug workaround (g++ sucks!!)
   138: 
   139:   // utility, defined here for no good reason
   140:   static void printHexLine(byte const *data, int length, int lineLength);
   141:     // print 'length' bytes of 'data' in hex
   142:     // blank-pad the output as if 'linelen' bytes were present
   143: 
   144:   static void printPrintableLine(byte const *data, int length,
   145:                                  char unprintable = '.');
   146:     // print 'length' bytes of 'data', substituting 'unprintable' for bytes for
   147:     // which 'isprint' is false
   148: };
   149: 
   150: #endif // __DATABLOK_H
   151: 
End C section to elk/sm_datablok.h[1]
Start C section to elk/sm_exc.h[1 /1 ]
     1: #line 1697 "./lpsrc/sm.pak"
     2: // exc.h            see license.txt for copyright and terms of use
     3: // exception classes for SafeTP project
     4: // Scott McPeak, 1996-1998  This file is public domain.
     5: 
     6: #ifndef __EXC_H
     7: #define __EXC_H
     8: 
     9: #include "sm_breaker.h"
    10: #include "sm_typ.h"
    11: #include "sm_xassert.h"
    12: #include "sm_str.h"
    13: #include <iostream>    // std::ostream
    14: 
    15: // forward declarations
    16: class ELK_EXTERN sm_stringBuilder;
    17: 
    18: 
    19: // by using this macro, the debugger gets a shot before the stack is unwound
    20: #ifdef THROW
    21: #undef THROW
    22: #endif
    23: #define THROW(obj) \
    24:   { breaker(); throw (obj); }
    25: 
    26: 
    27: // this function returns true if we're in the process of unwinding the
    28: // stack, and therefore destructors may want to avoid throwing new exceptions;
    29: // for now, may return false positives, but won't return false negatives
    30: bool unwinding();
    31: 
    32: // inside a catch expression, the unwinding() function needs a tweak; pass
    33: // the caught expression, and this returns whether there any *additional*
    34: // exceptions currently in flight
    35: class xBase;
    36: bool unwinding_other(xBase const &x);
    37: 
    38: // using unwinding() in destructors to avoid abort()
    39: #define CAUTIOUS_RELAY           \
    40:   catch (xBase &x) {             \
    41:     if (!unwinding_other(x)) {   \
    42:       throw;   /* re-throw */    \
    43:     }                            \
    44:   }
    45: 
    46: 
    47: // -------------------- xBase ------------------
    48: // intent is to derive all exception objects from this
    49: class xBase {
    50:   sm_string msg;
    51:     // the human-readable description of the exception
    52: 
    53: public:
    54:   static bool logExceptions;
    55:     // initially true; when true, we write a record of the thrown exception
    56:     // to clog
    57: 
    58:   static int creationCount;
    59:     // current # of xBases running about; used to support unrolling()
    60: 
    61: public:
    62:   xBase(char const *m);    // create exception object with message 'm'
    63:   xBase(xBase const &m);   // copy ctor
    64:   virtual ~xBase();
    65: 
    66:   char const *why() const
    67:     { return (char const*)msg; }
    68: 
    69:   void insert(std::ostream &os) const;
    70:   friend std::ostream& operator << (std::ostream &os, xBase const &obj)
    71:     { obj.insert(os); return os; }
    72:     // print why
    73: };
    74: 
    75: // equivalent to THROW(xBase(msg))
    76: void xbase(char const *msg) NORETURN;
    77: 
    78: 
    79: // -------------------- x_assert -----------------------
    80: // thrown by _xassert_fail, declared in xassert.h
    81: // throwing this corresponds to detecting a bug in the program
    82: class x_assert : public xBase {
    83:   sm_string condition;          // text of the failed condition
    84:   sm_string filename;           // name of the source file
    85:   int lineno;                // line number
    86: 
    87: public:
    88:   x_assert(char const *cond, char const *fname, int line);
    89:   x_assert(x_assert const &obj);
    90:   ~x_assert();
    91: 
    92:   char const *cond() const { return (char const *)condition; }
    93:   char const *fname() const { return (char const *)filename; }
    94:   int line() const { return lineno; }
    95: };
    96: 
    97: 
    98: // ---------------------- xFormat -------------------
    99: // throwing this means a formatting error has been detected
   100: // in some input data; the program cannot process it, but it
   101: // is not a bug in the program
   102: class xFormat : public xBase {
   103:   sm_string condition;          // what is wrong with the input
   104: 
   105: public:
   106:   xFormat(char const *cond);
   107:   xFormat(xFormat const &obj);
   108:   ~xFormat();
   109: 
   110:   char const *cond() const { return (char const*)condition; }
   111: };
   112: 
   113: // compact way to throw an xFormat
   114: void xformat(char const *condition) NORETURN;
   115: 
   116: // convenient combination of condition and human-readable message
   117: #define checkFormat(cond, message) \
   118:   ((cond)? (void)0 : xformat(message))
   119: 
   120: // assert-like interface to xFormat
   121: void formatAssert_fail(char const *cond, char const *file, int line) NORETURN;
   122: 
   123: #define formatAssert(cond) \
   124:   ((cond)? (void)0 : formatAssert_fail(#cond, __FILE__, __LINE__))
   125: 
   126: 
   127: // -------------------- XOpen ---------------------
   128: // thrown when we fail to open a file
   129: class XOpen : public xBase {
   130: public:
   131:   sm_string filename;
   132: 
   133: public:
   134:   XOpen(char const *fname);
   135:   XOpen(XOpen const &obj);
   136:   ~XOpen();
   137: };
   138: 
   139: void throw_XOpen(char const *fname) NORETURN;
   140: 
   141: 
   142: #endif // __EXC_H
   143: 
End C section to elk/sm_exc.h[1]
Start C section to elk/sm_flatten.h[1 /1 ]
     1: #line 1841 "./lpsrc/sm.pak"
     2: // flatten.h            see license.txt for copyright and terms of use
     3: // interface to automate process of flattening structures made of objects with
     4: //   arbitrary types, and possibly circular references
     5: // this is a trimmed-down version of the one in 'proot'
     6: 
     7: #ifndef FLATTEN_H
     8: #define FLATTEN_H
     9: 
    10: #include "sm_trdelete.h"
    11: 
    12: class Flatten {
    13: public:
    14:   Flatten();
    15:   virtual ~Flatten();
    16: 
    17:   TRASHINGDELETE;
    18: 
    19:   // query the read/write state
    20:   virtual bool reading() const = 0;
    21:   bool writing() const { return !reading(); }
    22: 
    23:   // transferring xfer a simple data type of fixed length
    24:   // 'len', in bytes
    25:   virtual void xferSimple(void *var, unsigned len)=0;
    26: 
    27:   // syntactic sugar
    28:   //#define xferVar(varname) xferSimple(&varname, sizeof(varname))
    29:   //#define XFERV(varname) flat.xferVar(varname)
    30: 
    31:   // xfer various C built-in data types (will add them as I need them)
    32:   virtual void xferChar(char &c);
    33:   virtual void xferInt(int &i);
    34:   virtual void xferLong(long &l);
    35:   virtual void xferBool(bool &b);
    36: 
    37:   // read or write a null-terminated character buffer, allocated with new;
    38:   // this works if 'str' is NULL
    39:   virtual void xferCharString(char *&str);
    40: 
    41:   // xfer a buffer allocated with 'new', of a given length
    42:   virtual void xferHeapBuffer(void *&buf, int len);
    43: 
    44:   // read: write the code; write: read & compare to code, fail if != ;
    45:   // the code is arbitrary, but should be unique across the source tree
    46:   // (I usually make the code with my Emacs' Ctl-Alt-R, which inserts a random number)
    47:   virtual void checkpoint(int code);
    48: 
    49:   // ------------- utilities ---------
    50:   // for when we already know whether we're reading or writing; internally,
    51:   // these assert which state we're in
    52:   void writeInt(int i);
    53:   int readInt();
    54: 
    55:   // ------------- owner/serf ------------
    56:   // take note of an owner pointer where we expect to xfer serfs to it
    57:   virtual void noteOwner(void *ownerPtr) = 0;
    58: 
    59:   // xfer a serf pointer that we've previously taken note of
    60:   virtual void xferSerf(void *&serfPtr, bool nullable=false) = 0;
    61:   void writeSerf(void *serfPtr);
    62:   void *readSerf();
    63: };
    64: 
    65: #endif // FLATTEN_H
End C section to elk/sm_flatten.h[1]
Start C section to elk/sm_gprintf.h[1 /1 ]
     1: #line 1907 "./lpsrc/sm.pak"
     2: /* gprintf.h
     3:  * generalized printf interface
     4:  * http://www.efgh.com/software/gprintf.htm
     5:  * this file is in the public domain */
     6: 
     7: #ifndef GPRINTF_H
     8: #define GPRINTF_H
     9: 
    10: #include <stdarg.h>      /* va_list */
    11: 
    12: #ifdef __cplusplus
    13: extern "C" {
    14: #endif
    15: 
    16: /* This is called once for each output character.  It returns >=0 for
    17:  * success or <0 for failure, in which case that code will end up as
    18:  * the return value from general_printf.  'extra' is user-defined
    19:  * context, and is passed the same value as the 'extra' arg to
    20:  * general_printf.  'ch' is of course the character to output. */
    21: typedef int (*Gprintf_output_function)(void *extra, int ch);
    22: 
    23: /* Interpret 'format' and 'args' as printf does, but calling
    24:  * 'output' for each rendered character.  Returns the # of characters
    25:  * output (not including final NUL), or <0 for failure (same code
    26:  * that 'output' returns if it fails). */
    27: int general_vprintf(Gprintf_output_function output,
    28:                     void *extra, const char *format, va_list args);
    29: 
    30: /* same thing but accepting variable # of args directly */
    31: int general_printf(Gprintf_output_function output,
    32:                    void *extra, const char *format, ...);
    33: 
    34: #ifdef __cplusplus
    35: }
    36: #endif
    37: 
    38: #endif /* GPRINTF_H */
End C section to elk/sm_gprintf.h[1]
Start C section to elk/sm_growbuf.h[1 /1 ]
     1: #line 1946 "./lpsrc/sm.pak"
     2: // growbuf.h            see license.txt for copyright and terms of use
     3: // buffer that grows as needed by doubling in size
     4: 
     5: #ifndef __GROWBUF_H
     6: #define __GROWBUF_H
     7: 
     8: #include "sm_datablok.h"
     9: 
    10: class GrowBuffer : public DataBlock {
    11: public:
    12:   GrowBuffer(int allocSize=0)
    13:     : DataBlock(allocSize) {}
    14:   ~GrowBuffer() {}
    15: 
    16:   // append to the end, at least doubling allocated
    17:   // size if growth is needed
    18:   void append(byte const *str, int len);
    19:   void append(char const *str, int len)
    20:     { append((byte const*)str, len); }
    21: };
    22: 
    23: #endif // __GROWBUF_H
End C section to elk/sm_growbuf.h[1]
Start C section to elk/sm_hashline.h[1 /1 ]
     1: #line 1970 "./lpsrc/sm.pak"
     2: // hashline.h
     3: // module for maintaining and using #line info in source files
     4: 
     5: // terminology:
     6: //   pp source: preprocessed source, i.e. whatever had the #line
     7: //              info sprinkled throughout it
     8: //   orig source: original source, the files to which the #line
     9: //                directives refer
    10: 
    11: #ifndef HASHLINE_H
    12: #define HASHLINE_H
    13: 
    14: #include "sm_strobjdict.h"
    15: #include "sm_array.h"
    16: 
    17: // map from lines in some given pp source file to lines in
    18: // orig source files; there should be one HashLineMap object
    19: // for each pp source file of interest
    20: class HashLineMap {
    21: private:    // types
    22:   // records a single #line directive
    23:   class HashLine {
    24:   public:
    25:     int ppLine;              // pp source line where it appears
    26:     int origLine;            // orig line it names
    27:     char const *origFname;   // orig fname it names
    28: 
    29:   public:
    30:     HashLine()
    31:       : ppLine(0), origLine(0), origFname(NULL) {}
    32:     HashLine(int pl, int ol, char const *of)
    33:       : ppLine(pl), origLine(ol), origFname(of) {}
    34:     HashLine(HashLine const &obj)
    35:       : DMEMB(ppLine), DMEMB(origLine), DMEMB(origFname) {}
    36:   };
    37: 
    38: private:    // data
    39:   // name of the pp file; this is needed for queries to lines
    40:   // before any #line is encountered
    41:   sm_string ppFname;
    42: 
    43:   // map for canonical storage of orig filenames; I don't rely on
    44:   // an external sm_string table because I don't want the extra
    45:   // dependency
    46:   StringObjDict<sm_string> filenames;
    47: 
    48:   // growable array of HashLine objects
    49:   ArrayStack<HashLine> directives;
    50: 
    51:   // previously-added ppLine; used to verify the entries are
    52:   // being added in sorted order
    53:   int prev_ppLine;
    54: 
    55: public:     // funcs
    56:   HashLineMap(char const *ppFname);
    57:   ~HashLineMap();
    58: 
    59:   // call this time each time a #line directive is encountered;
    60:   // successive calls must have strictly increasing values of 'ppLine'
    61:   void addHashLine(int ppLine, int origLine, char const *origFname);
    62: 
    63:   // call this when all the #line directives have been added; this
    64:   // consolidates the 'directives' array to reclaim any space created
    65:   // during the growing process but that is now not needed
    66:   void doneAdding();
    67: 
    68:   // map from pp line to orig line/file; note that queries exactly on
    69:   // #line lines have undefined results
    70:   void map(int ppLine, int &origLine, char const *&origFname) const;
    71:   int mapLine(int ppLine) const;           // returns 'origLine'
    72:   char const *mapFile(int ppLine) const;   // returns 'origFname'
    73: 
    74:   // for curiosity, find out how many unique filenames are recorded in
    75:   // the 'filenames' dictionary
    76:   int numUniqueFilenames() { return filenames.size(); }
    77: };
    78: 
    79: #endif // HASHLINE_H
End C section to elk/sm_hashline.h[1]
Start C section to elk/sm_hashtbl.h[1 /1 ]
     1: #line 2050 "./lpsrc/sm.pak"
     2: // hashtbl.h            see license.txt for copyright and terms of use
     3: // hash table mapping arbitrary keys to void*, where
     4: // the stored pointers can be used to derive the key,
     5: // and cannot be NULL
     6: 
     7: #ifndef HASHTBL_H
     8: #define HASHTBL_H
     9: 
    10: #include "sm_typ.h"
    11: 
    12: class HashTable {
    13: private:    // types
    14:   friend class HashTableIter;
    15: 
    16: public:     // types
    17:   // given a stored data pointer, retrieve the associated key
    18:   typedef void const* (*GetKeyFn)(void *data);
    19: 
    20:   // given a key, retrieve the associated hash value;
    21:   // this should be a 32-bit integer ready to be mod'd by the table size
    22:   typedef unsigned (*HashFn)(void const *key);
    23: 
    24:   // compare two keys; this is needed so we can handle collisions
    25:   // in the hash function; return true if they are equal
    26:   typedef bool (*EqualKeyFn)(void const *key1, void const *key2);
    27: 
    28:   // constants
    29:   enum {
    30:     defaultSize = 33
    31:   };
    32: 
    33: private:    // data
    34:   // maps
    35:   GetKeyFn getKey;
    36:   HashFn coreHashFn;
    37:   EqualKeyFn equalKeys;
    38: 
    39:   // array of pointers to data, indexed by the hash value,
    40:   // with collisions resolved by moving to adjacent entries;
    41:   // some entries are NULL, meaning that hash value has no mapping
    42:   void **hashTable;
    43: 
    44:   // Why use linear hashing instead of double hashing?  To support
    45:   // deletion.  Since every probe sequence that goes through index k
    46:   // will have a tail of k+1,k+2,... (mod tableSize) I can easily find
    47:   // and re-insert all the elements whose position might have depended
    48:   // on the presence of a now-deleted element.  Excessive clustering
    49:   // is (hopefully) avoided through load factor control.
    50: 
    51:   // number of slots in the hash table
    52:   int tableSize;
    53: 
    54:   // number of mapped (non-NULL) entries
    55:   int numEntries;
    56: 
    57:   // when false, we never make the table smaller (default: true)
    58:   bool enableShrink;
    59: 
    60: private:    // funcs
    61:   // disallowed
    62:   HashTable(HashTable&);
    63:   void operator=(HashTable&);
    64:   void operator==(HashTable&);
    65: 
    66:   // hash fn for the current table size; always in [0,tableSize-1]
    67:   unsigned hashFunction(void const *key) const;
    68: 
    69:   // given a collision at 'index', return the next index to try
    70:   int nextIndex(int index) const { return (index+1) % tableSize; }
    71: 
    72:   // resize the table, transferring all the entries to their
    73:   // new positions
    74:   void resizeTable(int newSize);
    75: 
    76:   // return the index of the entry corresponding to 'data' if it
    77:   // is mapped, or a pointer to the entry that should be filled
    78:   // with its mapping, if unmapped
    79:   int getEntry(void const *key) const;
    80: 
    81:   // make a new table with the given size
    82:   void makeTable(int size);
    83: 
    84:   // check a single entry for integrity
    85:   void checkEntry(int entry) const;
    86: 
    87: public:     // funcs
    88:   HashTable(GetKeyFn gk, HashFn hf, EqualKeyFn ek,
    89:             int initSize = HashTable::defaultSize);
    90:   ~HashTable();
    91: 
    92:   // return # of mapped entries
    93:   int getNumEntries() const { return numEntries; }
    94: 
    95:   // if this hash value has a mapping, return it; otherwise,
    96:   // return NULL
    97:   void *get(void const *key) const;
    98: 
    99:   // add a mapping from 'key' to 'value'; there must not already
   100:   // be a mapping for this key
   101:   void add(void const *key, void *value);
   102: 
   103:   // remove the mapping for 'key' -- it must exist
   104:   // returns the removed item
   105:   void *remove(void const *key);
   106: 
   107:   // remove all mappings
   108:   void empty(int initSize = HashTable::defaultSize);
   109: 
   110:   // set whether shrinkage is allowed; it's useful to be able to
   111:   // disable this to avoid any allocation in certain situations
   112:   void setEnableShrink(bool en) { enableShrink = en; }
   113: 
   114:   // allow external access to an accessor function
   115:   void const *callGetKeyFn(void *data) { return getKey(data); }
   116: 
   117:   // check the data structure's invariants, and throw an exception
   118:   // if there is a problem
   119:   void selfCheck() const;
   120: 
   121:   // ------ useful default functions -------
   122:   // returns its argument
   123:   static void const* identityKeyFn(void *data);
   124: 
   125:   // puts the argument through two cycles of a linear
   126:   // congruential pseudo-random number generator
   127:   static unsigned lcprngHashFn(void const *key);
   128: 
   129:   // does pointer equality comparison
   130:   static bool pointerEqualKeyFn(void const *key1, void const *key2);
   131: };
   132: 
   133: unsigned lcprngTwoSteps(unsigned v);
   134: 
   135: 
   136: // iterate over all stored values in a HashTable
   137: // NOTE: you can't change the table while an iter exists
   138: class HashTableIter {
   139: private:      // data
   140:   HashTable &table;      // table we're iterating over
   141:   int index;             // current slot to return in adv(); -1 when done
   142: 
   143: private:      // funcs
   144:   void moveToSth();
   145: 
   146: public:       // funcs
   147:   HashTableIter(HashTable &table);
   148: 
   149:   bool isDone() const { return index == -1; }
   150:   void adv();
   151:   void *data() const;          // returns a value stored in the table
   152: };
   153: 
   154: 
   155: #endif // HASHTBL_H
End C section to elk/sm_hashtbl.h[1]
Start C section to elk/sm_macros.h[1 /1 ]
     1: #line 2206 "./lpsrc/sm.pak"
     2: // macros.h            see license.txt for copyright and terms of use
     3: // grab-bag of useful macros, stashed here to avoid mucking up
     4: //   other modules with more focus; there's no clear rhyme or
     5: //   reason for why some stuff is here and some in typ.h
     6: // (no configuration stuff here!)
     7: 
     8: #ifndef __MACROS_H
     9: #define __MACROS_H
    10: 
    11: #include "sm_typ.h"
    12: 
    13: // complement of ==
    14: #define NOTEQUAL_OPERATOR(T)             \
    15:   bool operator != (T const &obj) const  \
    16:     { return !operator==(obj); }
    17: 
    18: // toss this into a class that already has == and < defined, to
    19: // round out the set of relational operators (assumes a total
    20: // order, i.e.  a < b  <=>  b < a)
    21: #define RELATIONAL_OPERATORS(T)                    \
    22:   NOTEQUAL_OPERATOR(T)                             \
    23:   bool operator <= (T const &obj) const            \
    24:     { return !obj.operator<(*this); }              \
    25:   bool operator > (T const &obj) const             \
    26:     { return obj.operator<(*this); }               \
    27:   bool operator >= (T const &obj) const            \
    28:     { return !operator<(obj); }
    29: 
    30: 
    31: // member copy in constructor initializer list
    32: #define DMEMB(var) var(obj.var)
    33: 
    34: // member copy in operator =
    35: #define CMEMB(var) var = obj.var
    36: 
    37: // member comparison in operator ==
    38: #define EMEMB(var) var == obj.var
    39: 
    40: 
    41: // standard insert operator
    42: // (note that you can put 'virtual' in front of the macro call if desired)
    43: #define INSERT_OSTREAM(T)                                \
    44:   void insertOstream(std::ostream &os) const;                 \
    45:   friend std::ostream& operator<< (std::ostream &os, T const &obj) \
    46:     { obj.insertOstream(os); return os; }
    47: 
    48: 
    49: // usual declarations for a data object (as opposed to control object)
    50: #define DATA_OBJ_DECL(T)                \
    51:   T();                                  \
    52:   T(T const &obj);                      \
    53:   ~T();                                 \
    54:   T& operator= (T const &obj);          \
    55:   bool operator== (T const &obj) const; \
    56:   NOTEQUAL_OPERATOR(T)                  \
    57:   INSERTOSTREAM(T)
    58: 
    59: 
    60: // copy this to the .cc file for implementation of DATA_OBJ_DECL
    61: #if 0
    62: T::T()
    63: {}
    64: 
    65: T::T(T const &obj)
    66:   : DMEMB(),
    67:     DMEMB(),
    68:     DMEMB()
    69: {}
    70: 
    71: T::~T()
    72: {}
    73: 
    74: T& T::operator= (T const &obj)
    75: {
    76:   if (this != &obj) {
    77:     CMEMB();
    78:   }
    79:   return *this;
    80: }
    81: 
    82: bool T::operator== (T const &obj) const
    83: {
    84:   return
    85:     EMEMB() &&
    86:     EMEMB();
    87: }
    88: 
    89: void T::insertOstream(std::ostream &os) const
    90: {}
    91: #endif // 0
    92: 
    93: 
    94: // assert something at compile time (must use this inside a function);
    95: // works because compilers won't let us declare negative-length arrays
    96: // (the expression below works with egcs-1.1.2, gcc-2.x, gcc-3.x)
    97: #define STATIC_ASSERT(cond) \
    98:   { (void)((int (*)(char failed_static_assertion[(cond)?1:-1]))0); }
    99: 
   100: // assert that a table is an expected size; the idea is to make sure
   101: // that static data in some table gets updated when a corresponding
   102: // symbolic constant is changed
   103: #define ASSERT_TABLESIZE(table, size) \
   104:   STATIC_ASSERT(TABLESIZE(table) == (size))
   105: 
   106: 
   107: // for silencing variable-not-used warnings
   108: template <class T>
   109: inline void pretendUsedFn(T const &) {}
   110: #define PRETEND_USED(arg) pretendUsedFn(arg) /* user ; */
   111: 
   112: 
   113: // appended to function declarations to indicate they do not
   114: // return control to their caller; e.g.:
   115: //   void exit(int code) NORETURN;
   116: #ifdef __GNUC__
   117:   #define NORETURN __attribute__((noreturn))
   118: #else
   119:   // just let the warnings roll if we can't suppress them
   120:   #define NORETURN
   121: #endif
   122: 
   123: 
   124: // these two are a common idiom in my code for typesafe casts;
   125: // they are essentially a roll-your-own RTTI
   126: #define CAST_MEMBER_FN(destType)                                                \
   127:   destType const &as##destType##C() const;                                      \
   128:   destType &as##destType() { return const_cast<destType&>(as##destType##C()); }
   129: 
   130: #define CAST_MEMBER_IMPL(inClass, destType)         \
   131:   destType const &inClass::as##destType##C() const  \
   132:   {                                                 \
   133:     xassert(is##destType());                        \
   134:     return (destType const&)(*this);                \
   135:   }
   136: 
   137: 
   138: // same as the above, but returning pointers; I think returning
   139: // references was a mistake
   140: #define DOWNCAST_FN(destType)                                                   \
   141:   destType const *as##destType##C() const;                                      \
   142:   destType *as##destType() { return const_cast<destType*>(as##destType##C()); }
   143: 
   144: #define DOWNCAST_IMPL(inClass, destType)            \
   145:   destType const *inClass::as##destType##C() const  \
   146:   {                                                 \
   147:     xassert(is##destType());                        \
   148:     return static_cast<destType const*>(this);      \
   149:   }
   150: 
   151: 
   152: // keep track of a count and a high water mark
   153: #define INC_HIGH_WATER(count, highWater)  \
   154:   count++;                                \
   155:   if (count > highWater) {                \
   156:     highWater = count;                    \
   157:   }
   158: 
   159: 
   160: // egcs has the annoying "feature" that it warns
   161: // about switches on enums where not all cases are
   162: // covered .... what is this, f-ing ML??
   163: #define INCL_SWITCH \
   164:   default: break; /*silence warning*/
   165: 
   166: 
   167: // for a class that maintains allocated-node stats
   168: #define ALLOC_STATS_DECLARE                     \
   169:   static int numAllocd;                         \
   170:   static int maxAllocd;                         \
   171:   static void printAllocStats(bool anyway);
   172: 
   173: // these would go in a .cc file, whereas above goes in .h file
   174: #define ALLOC_STATS_DEFINE(classname)                      \
   175:   int classname::numAllocd = 0;                            \
   176:   int classname::maxAllocd = 0;                            \
   177:   STATICDEF void classname::printAllocStats(bool anyway)   \
   178:   {                                                        \
   179:     if (anyway || numAllocd != 0) {                        \
   180:       std::cout << #classname << " nodes: " << numAllocd        \
   181:            << ", max  nodes: " << maxAllocd                \
   182:            << std::endl;                                        \
   183:     }                                                      \
   184:   }
   185: 
   186: #define ALLOC_STATS_IN_CTOR                     \
   187:   INC_HIGH_WATER(numAllocd, maxAllocd);
   188: 
   189: #define ALLOC_STATS_IN_DTOR                     \
   190:   numAllocd--;
   191: 
   192: 
   193: // ----------- automatic data value restorer -------------
   194: // used when a value is to be set to one thing now, but restored
   195: // to its original value on return (even when the return is by
   196: // an exception being thrown)
   197: template <class T>
   198: class Restorer {
   199:   T &variable;
   200:   T prevValue;
   201: 
   202: public:
   203:   Restorer(T &var, T newValue)
   204:     : variable(var),
   205:       prevValue(var)
   206:   {
   207:     variable = newValue;
   208:   }
   209: 
   210:   // this one does not set it to a new value, just remembers the current
   211:   Restorer(T &var)
   212:     : variable(var),
   213:       prevValue(var)
   214:   {}
   215: 
   216:   ~Restorer()
   217:   {
   218:     variable = prevValue;
   219:   }
   220: };
   221: 
   222: 
   223: // declare a bunch of a set-like operators for enum types
   224: #define ENUM_BITWISE_AND(Type)                  \
   225:   inline Type operator& (Type f1, Type f2)      \
   226:     { return (Type)((int)f1 & (int)f2); }       \
   227:   inline Type& operator&= (Type &f1, Type f2)   \
   228:     { return f1 = f1 & f2; }
   229: 
   230: #define ENUM_BITWISE_OR(Type)                   \
   231:   inline Type operator| (Type f1, Type f2)      \
   232:     { return (Type)((int)f1 | (int)f2); }       \
   233:   inline Type& operator|= (Type &f1, Type f2)   \
   234:     { return f1 = f1 | f2; }
   235: 
   236: #define ENUM_BITWISE_XOR(Type)                  \
   237:   inline Type operator^ (Type f1, Type f2)      \
   238:     { return (Type)((int)f1 ^ (int)f2); }       \
   239:   inline Type& operator^= (Type &f1, Type f2)   \
   240:     { return f1 = f1 ^ f2; }
   241: 
   242: #define ENUM_BITWISE_NOT(Type, ALL)             \
   243:   inline Type operator~ (Type f)                \
   244:     { return (Type)((~(int)f) & ALL); }
   245: 
   246: #define ENUM_BITWISE_OPS(Type, ALL)             \
   247:   ENUM_BITWISE_AND(Type)                        \
   248:   ENUM_BITWISE_OR(Type)                         \
   249:   ENUM_BITWISE_XOR(Type)                        \
   250:   ENUM_BITWISE_NOT(Type, ALL)
   251: 
   252: 
   253: // macro to conditionalize something on NDEBUG; I typically use this
   254: // to hide the declaration of a variable whose value is only used by
   255: // debugging trace statements (and thus provokes warnings about unused
   256: // variables if NDEBUG is set)
   257: #ifdef NDEBUG
   258:   #define IFDEBUG(stuff)
   259: #else
   260:   #define IFDEBUG(stuff) stuff
   261: #endif
   262: 
   263: 
   264: // put at the top of a class for which the default copy ctor
   265: // and operator= are not desired; then don't define these functions
   266: #define NO_OBJECT_COPIES(name)   \
   267:   private:                       \
   268:     name(name&);                 \
   269:     void operator=(name&) /*user ;*/
   270: 
   271: 
   272: #endif // __MACROS_H
End C section to elk/sm_macros.h[1]
Start C section to elk/sm_missing.h[1 /1 ]
     1: #line 2479 "./lpsrc/sm.pak"
     2: // missing.h            see license.txt for copyright and terms of use
     3: // routines that I implemented because they aren't available on all platforms
     4: // Scott McPeak, 1998  This file is public domain.
     5: 
     6: #ifndef __MISSING_H
     7: #define __MISSING_H
     8: 
     9: // stricmp
    10: int missing_stricmp(char const *s1, char const *s2);
    11: 
    12: #endif // __MISSING_H
    13: 
End C section to elk/sm_missing.h[1]
Start C section to elk/sm_mysig.h[1 /1 ]
     1: #line 2493 "./lpsrc/sm.pak"
     2: // mysig.h            see license.txt for copyright and terms of use
     3: // some simple Unix signal-handling stuff
     4: 
     5: #ifndef MYSIG_H
     6: #define MYSIG_H
     7: 
     8: #include <signal.h>     // signal stuff
     9: #include <setjmp.h>     // jmp_buf
    10: 
    11: #ifdef __cplusplus
    12: extern "C" {
    13: #endif // __cplusplus
    14: 
    15: // type of a signal handler function; generally, there are
    16: // three options for a signal handler:
    17: //   - return, in which case the default action for the
    18: //     signal is taken
    19: //   - longjmp to a state where computation can resume
    20: //   - abort(2) or exit(2)
    21: // it's somewhat dangerous to do other system calls, but people
    22: // do it anyway
    23: typedef void (*SignalHandler)(int signum);
    24: 
    25: 
    26: // install the given handler on the given signal
    27: void setHandler(int signum, SignalHandler handler);
    28: 
    29: 
    30: // simple handler that just prints and re-raises
    31: void printHandler(int signum);
    32: 
    33: 
    34: // to use jmpHandler, call setjmp(sane_state) before
    35: // installing the handler
    36: extern jmp_buf sane_state;
    37: 
    38: // handler to do a longjmp to sane_state
    39: void jmpHandler(int signum);
    40: 
    41: 
    42: // install a segfault handler that will print the address that
    43: // caused the fault; this is very useful for debugging
    44: void printSegfaultAddrs();
    45: 
    46: 
    47: #ifdef __cplusplus
    48: }
    49: #endif // __cplusplus
    50: 
    51: #endif // MYSIG_H
End C section to elk/sm_mysig.h[1]
Start C section to elk/sm_nonport.h[1 /1 ]
     1: #line 2545 "./lpsrc/sm.pak"
     2: // nonport.h            see license.txt for copyright and terms of use
     3: // collection of nonportable routines (the interfaces
     4: //   are portable, but the implementations are not)
     5: // Scott McPeak, Dan Bonachea 1998-1999  This file is public domain.
     6: 
     7: #ifndef __NONPORT_H
     8: #define __NONPORT_H
     9: 
    10: #include "sm_typ.h"
    11: #include <stdarg.h>  // va_list
    12: 
    13: 
    14: // I'm attempting to improve error handling in this module; this fn will be
    15: // called when a syscall fails, *in addition* to whatever error behavior
    16: // is documented here (e.g., a fn might call this, and then return false).
    17: // The default behavior is to do nothing.  In sftpc and sftpd, I plan to
    18: // point this at xSysError::xsyserror (see syserr.h).
    19: typedef void (*NonportFailFunc)(char const *syscallName, char const *context);
    20:   // syscallName  - name of failing system call
    21:   // context      - current activity (maybe just calling fn's name) or NULL
    22: extern NonportFailFunc nonportFail;
    23: 
    24: // this is default handler
    25: void defaultNonportFail(char const *syscallName, char const *context);
    26: 
    27: 
    28: 
    29: // put terminal into 'raw' or 'cooked' mode
    30: void setRawMode(bool raw);
    31: 
    32: // get the next character typed without buffering or echoing; needs the
    33: // console to be in 'raw' mode
    34: char getConsoleChar();
    35: 
    36: 
    37: // get a millisecond count, where 0 is an unspecified event
    38: long getMilliseconds();
    39: 
    40: 
    41: // remove all priviledges to a file, except for read/write
    42: // access by the file's owner; returns false on error
    43: bool limitFileAccess(char const *fname);
    44: 
    45: 
    46: // get process id; meaning is somewhat system-dependent, but the goal
    47: // is to return something that can be used to correlate log output
    48: // from (say) sftpd with log output from some other source (syslog,
    49: // or NT event viewer, etc.)
    50: #if 0
    51: int getProcessId();
    52: #endif
    53: 
    54: // create a new directory; returns false on error;
    55: // precise naming semantics, such as use
    56: // of 'current working directory', etc., are specified by the
    57: // underlying OS's mkdir (or equivalent) command (it is hoped
    58: // this underspecification will not be a problem in practice)
    59: bool createDirectory(char const *dirname);
    60: 
    61: // change to a directory; returns false on failure
    62: // again, current-directory semantics are unspecified
    63: bool changeDirectory(char const *dirname);
    64: 
    65: // retrieve the name of the current working directory
    66: // (more best effort crap, I guess)
    67: bool getCurrentDirectory(char *dirname, int dirnameLen);
    68: 
    69: 
    70: // get and process the names of files *and directories* in the current directory
    71: typedef bool (*PerFileFunc)(char const *name, void *extra);
    72:   // name   - file/dir being processed (contains no slashes)
    73:   // extra  - 2nd parameter to applyToCwdContents
    74:   // return - true to continue, false to stop iterating
    75: void applyToCwdContents(PerFileFunc func, void *extra=NULL);
    76: 
    77: // same as above, but in an explicitly named directory
    78: void applyToDirContents(char const *dirName,
    79:                         PerFileFunc func, void *extra=NULL);
    80: 
    81: 
    82: // return true if the given sm_string names a directory
    83: bool isDirectory(char const *path);
    84: 
    85: 
    86: // delete a file; returns false on failure
    87: bool removeFile(char const *fname);
    88: 
    89: 
    90: // retrieve the current date
    91: void getCurrentDate(int &month, int &day, int &year);
    92:   // month:    1 = January ... 12 = December
    93:   // day:      1 = first day of month, ...
    94:   // year:     1999 is when this being coded
    95:   // e.g., February 8, 1999  is  month=2, day=8, year=1999
    96: 
    97: 
    98: // sleep for a bit (low resolution)
    99: void portableSleep(unsigned seconds);
   100: 
   101: 
   102: /*
   103: // determine usable name of current user, and write it into 'buffer'
   104: void getCurrentUsername(char *buffer, int buflen);
   105: */
   106: 
   107: // read a sm_string from the console, with no echo
   108: void readNonechoString(char *buffer, int buflen, char const *prompt);
   109: 
   110: 
   111: // return true if a file or directory exists
   112: bool fileOrDirectoryExists(char const *name);
   113: 
   114: 
   115: // ensure that the pathname part of a file name exists;
   116: // it creates missing directories as necessary, with only
   117: // user rwx permission; if 'isDirectory' is true, the whole
   118: // name is also verified as a directory; returns false on
   119: // error
   120: bool ensurePath(char const *filename, bool isDirectory);
   121: 
   122: 
   123: // returns true if the system has a cryptographically-
   124: // secure random number generator
   125: bool hasSystemCryptoRandom();
   126: 
   127: // if the above fn returns true, this will retrieve a
   128: // random 32-bit integer; may block until the bits
   129: // become available
   130: unsigned getSystemCryptoRandom();
   131: 
   132: 
   133: // determine how many characters, *not* including the final NUL, would
   134: // be written by vsprintf; this is allowed to overestimate
   135: int vnprintf(char const *format, va_list args);
   136: 
   137: // this is implemented in terms of vnprintf, so not technically
   138: // a function with "nonportable implementation", but it belongs
   139: // here anyway
   140: int nprintf(char const *format, ...);
   141: 
   142: 
   143: #endif // __NONPORT_H
   144: 
End C section to elk/sm_nonport.h[1]
Start C section to elk/sm_objlist.h[1 /1 ]
     1: #line 2690 "./lpsrc/sm.pak"
     2: // objlist.h
     3: // owner list of arbitrary dynamically-allocated objects
     4: // NOTE: automatically generated from xobjlist.h -- do not edit directly
     5: 
     6: // Author: Scott McPeak, 2000
     7: 
     8: #ifndef OBJLIST_H
     9: #define OBJLIST_H
    10: 
    11: #include "sm_voidlist.h"
    12: 
    13: 
    14: // forward declarations of template classes, so we can befriend them in ObjList
    15: // (not required by Borland C++ 4.5, but GNU wants it...)
    16: template <class T> class ObjListIter;
    17: template <class T> class ObjListMutator;
    18: template <class T> class ObjListIterNC;
    19: 
    20: 
    21: // the list is considered to own all of the items; it is an error to insert
    22: // an item into more than one such list, or to insert an item more than once
    23: // into any such list
    24: template <class T>
    25: class ObjList {
    26: private:
    27:   friend class ObjListIter<T>;
    28:   friend class ObjListMutator<T>;
    29:   friend class ObjListIterNC<T>;
    30: 
    31: protected:
    32:   VoidList list;                        // list itself
    33: 
    34: private:
    35:   // this is an owner list; these are not allowed
    36:   ObjList(ObjList const &obj);
    37:   ObjList& operator= (ObjList const &src);
    38: 
    39: public:
    40:   ObjList()                            : list() {}
    41:   ~ObjList()                           { deleteAll(); }
    42: 
    43:   // The difference function should return <0 if left should come before
    44:   // right, 0 if they are equivalent, and >0 if right should come before
    45:   // left.  For example, if we are sorting numbers into ascending order,
    46:   // then 'diff' would simply be subtraction.
    47:   typedef int (*Diff)(T const *left, T const *right, void *extra);
    48: 
    49:   // selectors
    50:   int count() const                     { return list.count(); }
    51:   bool isEmpty() const                  { return list.isEmpty(); }
    52:   bool isNotEmpty() const               { return list.isNotEmpty(); }
    53:   T *nth(int which)                     { return (T*)list.nth(which); }
    54:   T const *nthC(int which) const        { return (T const*)list.nth(which); }
    55:   T *first()                            { return (T*)list.first(); }
    56:   T const *firstC() const               { return (T const*)list.first(); }
    57:   T *last()                             { return (T*)list.last(); }
    58:   T const *lastC() const                { return (T const*)list.last(); }
    59: 
    60:   // insertion
    61:   void prepend(T *newitem)              { list.prepend((void*)newitem); }
    62:   void append(T *newitem)               { list.append((void*)newitem); }
    63:   void insertAt(T *newitem, int index)  { list.insertAt((void*)newitem, index); }
    64:   void insertSorted(T *newitem, Diff diff, void *extra=NULL)
    65:     { list.insertSorted((void*)newitem, (VoidDiff)diff, extra); }
    66: 
    67:   // removal
    68:   T *removeAt(int index)                { return (T*)list.removeAt(index); }
    69:   T *removeFirst()                      { return (T*)list.removeFirst(); }
    70:   void deleteAt(int index)              { delete (T*)list.removeAt(index); }
    71:   void deleteAll();
    72: 
    73:   // list-as-set: selectors
    74:   int indexOf(T const *item) const      { return list.indexOf((void*)item); }
    75:   int indexOfF(void *item) const        { return list.indexOfF((void*)item); }
    76:   bool contains(T const *item) const    { return list.contains((void*)item); }
    77: 
    78:   // list-as-set: mutators
    79:   bool prependUnique(T *newitem)        { return list.prependUnique((void*)newitem); }
    80:   bool appendUnique(T *newitem)         { return list.appendUnique((void*)newitem); }
    81:   void removeItem(T const *item)        { list.removeItem((void*)item); }    // whether the arg should be const is debatable..
    82:   bool removeIfPresent(T const *item)   { return list.removeIfPresent((void*)item); }
    83: 
    84:   // complex modifiers
    85:   void reverse()                                    { list.reverse(); }
    86:   void insertionSort(Diff diff, void *extra=NULL)   { list.insertionSort((VoidDiff)diff, extra); }
    87:   void mergeSort(Diff diff, void *extra=NULL)       { list.mergeSort((VoidDiff)diff, extra); }
    88: 
    89:   // and a related test
    90:   bool isSorted(Diff diff, void *extra=NULL) const  { return list.isSorted((VoidDiff)diff, extra); }
    91: 
    92:   // multiple lists
    93:   void concat(ObjList &tail)                       { list.concat(tail.list); }
    94:   // (we do *not* have appendAll, since these are supposed to be owner lists)
    95: 
    96:   // steal
    97:   void stealTailAt(int index, ObjList &tail)       { list.stealTailAt(index, tail.list); }
    98: 
    99:   // equal items in equal positions
   100:   bool equalAsLists(ObjList const &otherList, Diff diff, void *extra=NULL) const
   101:     { return list.equalAsLists(otherList.list, (VoidDiff)diff, extra); }
   102:   int compareAsLists(ObjList const &otherList, Diff diff, void *extra=NULL) const
   103:     { return list.compareAsLists(otherList.list, (VoidDiff)diff, extra); }
   104: 
   105:   // last-as-set: comparisons (NOT efficient)
   106:   bool equalAsSets(ObjList const &otherList, Diff diff, void *extra=NULL) const
   107:     { return list.equalAsSets(otherList.list, (VoidDiff)diff, extra); }
   108:   bool isSubsetOf(ObjList const &otherList, Diff diff, void *extra=NULL) const
   109:     { return list.isSubsetOf(otherList.list, (VoidDiff)diff, extra); }
   110:   bool containsByDiff(T const *item, Diff diff, void *extra=NULL) const
   111:     { return list.containsByDiff((void*)item, (VoidDiff)diff, extra); }
   112: 
   113:   // treating the pointer values themselves as the basis for comparison
   114:   bool equalAsPointerLists(ObjList const &otherList) const
   115:     { return list.equalAsPointerLists(otherList.list); }
   116:   bool equalAsPointerSets(ObjList const &otherList) const
   117:     { return list.equalAsPointerSets(otherList.list); }
   118: 
   119:   // debugging: two additional invariants
   120:   void selfCheck() const {
   121:     list.selfCheck();
   122:     list.checkHeapDataPtrs();
   123:     list.checkUniqueDataPtrs();
   124:   }
   125: };
   126: 
   127: 
   128: template <class T>
   129: void ObjList<T>::deleteAll()
   130: {
   131:   while (!list.isEmpty()) {
   132:     deleteAt(0);
   133:   }
   134: }
   135: 
   136: 
   137: // for traversing the list and modifying it (nodes and/or structure)
   138: // NOTE: no list-modification fns should be called on 'list' while this
   139: //       iterator exists, and only one such iterator should exist for
   140: //       any given list
   141: template <class T>
   142: class ObjListMutator {
   143:   friend class ObjListIter<T>;
   144: 
   145: protected:
   146:   VoidListMutator mut;       // underlying mutator
   147: 
   148: public:
   149:   ObjListMutator(ObjList<T> &lst)     : mut(lst.list) { reset(); }
   150:   ~ObjListMutator()                    {}
   151: 
   152:   void reset()                          { mut.reset(); }
   153: 
   154:   // iterator copying; safe *only* until one of the mutators modifies
   155:   // the list structure (by inserting or removing), at which time all
   156:   // other iterators might be in limbo
   157:   ObjListMutator(ObjListMutator const &obj)             : mut(obj.mut) {}
   158:   ObjListMutator& operator=(ObjListMutator const &obj)  { mut = obj.mut;  return *this; }
   159:     // requires that 'this' and 'obj' already refer to the same 'list'
   160: 
   161:   // iterator actions
   162:   bool isDone() const                   { return mut.isDone(); }
   163:   void adv()                            { mut.adv(); }
   164:   T *data()                             { return (T*)mut.data(); }
   165:   T *&dataRef()                         { return (T*&)mut.dataRef(); }
   166: 
   167:   // insertion
   168:   void insertBefore(T *item)            { mut.insertBefore((void*)item); }
   169:     // 'item' becomes the new 'current', and the current 'current' is
   170:     // pushed forward (so the next adv() will make it current again)
   171: 
   172:   void insertAfter(T *item)             { mut.insertAfter((void*)item); }
   173:     // 'item' becomes what we reach with the next adv();
   174:     // isDone() must be false
   175: 
   176:   void append(T *item)                  { mut.append((void*)item); }
   177:     // only valid while isDone() is true, it inserts 'item' at the end of
   178:     // the list, and advances such that isDone() remains true; equivalent
   179:     // to { xassert(isDone()); insertBefore(item); adv(); }
   180: 
   181:   // removal
   182:   T *remove()                           { return (T*)mut.remove(); }
   183:     // 'current' is removed from the list and returned, and whatever was
   184:     // next becomes the new 'current'
   185: 
   186:   void deleteIt()                       { delete (T*)mut.remove(); }
   187:     // same as remove(), except item is deleted also
   188: 
   189:   // debugging
   190:   void selfCheck() const                { mut.selfCheck(); }
   191: };
   192: 
   193: #define MUTATE_EACH_OBJLIST(T, list, iter) \
   194:   for(ObjListMutator< T > iter(list); !iter.isDone(); iter.adv())
   195: 
   196: 
   197: // for traversing the list without modifying it (neither nodes nor structure)
   198: // NOTE: no list-modification fns should be called on 'list' while this
   199: //       iterator exists
   200: template <class T>
   201: class ObjListIter {
   202: protected:
   203:   VoidListIter iter;      // underlying iterator
   204: 
   205: public:
   206:   ObjListIter(ObjList<T> const &list) : iter(list.list) {}
   207:   ObjListIter(ObjList<T> const &list, int pos) : iter(list.list, pos) {}
   208:   ~ObjListIter()                       {}
   209: 
   210:   void reset(ObjList<T> const &list)   { iter.reset(list.list); }
   211: 
   212:   // iterator copying; generally safe
   213:   ObjListIter(ObjListIter const &obj)             : iter(obj.iter) {}
   214:   ObjListIter& operator=(ObjListIter const &obj)  { iter = obj.iter;  return *this; }
   215: 
   216:   // but copying from a mutator is less safe; see above
   217:   ObjListIter(ObjListMutator<T> &obj)             : iter(obj.mut) {}
   218: 
   219:   // iterator actions
   220:   bool isDone() const                   { return iter.isDone(); }
   221:   void adv()                            { iter.adv(); }
   222:   T const *data() const                 { return (T const*)iter.data(); }
   223: };
   224: 
   225: #define FOREACH_OBJLIST(T, list, iter) \
   226:   for(ObjListIter< T > iter(list); !iter.isDone(); iter.adv())
   227: 
   228: 
   229: // intermediate to the above two, this allows modification of the
   230: // objects stored on the list, but not the identity or order of
   231: // the objects in the list
   232: template <class T>
   233: class ObjListIterNC {
   234: protected:
   235:   VoidListIter iter;      // underlying iterator
   236: 
   237: public:
   238:   ObjListIterNC(ObjList<T> &list) : iter(list.list) {}
   239:   ObjListIterNC(ObjList<T> &list, int pos) : iter(list.list, pos) {}
   240:   ~ObjListIterNC()                     {}
   241: 
   242:   void reset(ObjList<T> &list)         { iter.reset(list.list); }
   243: 
   244:   // iterator copying; generally safe
   245:   ObjListIterNC(ObjListIterNC const &obj)             : iter(obj.iter) {}
   246:   ObjListIterNC& operator=(ObjListIterNC const &obj)  { iter = obj.iter;  return *this; }
   247: 
   248:   // but copying from a mutator is less safe; see above
   249:   ObjListIterNC(ObjListMutator<T> &obj)               : iter(obj.mut) {}
   250: 
   251:   // iterator actions
   252:   bool isDone() const                   { return iter.isDone(); }
   253:   void adv()                            { iter.adv(); }
   254:   T *data() const                       { return (T*)iter.data(); }
   255: };
   256: 
   257: #define FOREACH_OBJLIST_NC(T, list, iter) \
   258:   for(ObjListIterNC< T > iter(list); !iter.isDone(); iter.adv())
   259: 
   260: 
   261: // iterate over the combined elements of two or more lists
   262: template <class T>
   263: class ObjListMultiIter {
   264: private:
   265:   // all the lists
   266:   ObjList<T> **lists;                // serf array of serf list pointers
   267:   int numLists;                      // length of this array
   268: 
   269:   // current element
   270:   int curList;                       // which list we're working on
   271:   ObjListIter<T> iter;               // current element of that list
   272: 
   273:   // invariant:
   274:   //   either curList==numLists, or
   275:   //   iter is not 'done'
   276: 
   277: public:
   278:   ObjListMultiIter(ObjList<T> **L, int n)
   279:     : lists(L),
   280:       numLists(n),
   281:       curList(0),
   282:       iter(*(lists[0]))
   283:   {
   284:     xassert(n > 0);
   285:     normalize();
   286:   }
   287: 
   288:   // advance the iterator to the next element of the next non-empty list;
   289:   // establishes invariant above
   290:   void normalize();
   291: 
   292:   bool isDone() const {
   293:     return curList == numLists;
   294:   }
   295: 
   296:   T const *data() const {
   297:     return iter.data();
   298:   }
   299: 
   300:   void adv() {
   301:     iter.adv();
   302:     normalize();
   303:   }
   304: };
   305: 
   306: // this was originally inline, but that was causing some strange
   307: // problems (compiler bug?)
   308: template <class T>
   309: void ObjListMultiIter<T>::normalize()
   310: {
   311:   while (iter.isDone() && curList < numLists) {
   312:     curList++;
   313:     if (curList < numLists) {
   314:       iter.reset(*(lists[curList]));
   315:     }
   316:   }
   317: }
   318: 
   319: 
   320: #endif // OBJLIST_H
End C section to elk/sm_objlist.h[1]
Start C section to elk/sm_objpool.h[1 /1 ]
     1: #line 3011 "./lpsrc/sm.pak"
     2: // objpool.h            see license.txt for copyright and terms of use
     3: // custom allocator: array of objects meant to be
     4: // re-used frequently, with high locality
     5: 
     6: #ifndef OBJPOOL_H
     7: #define OBJPOOL_H
     8: 
     9: #include "sm_array.h"
    10: 
    11: // the class T should have:
    12: //   // a link in the free list; it is ok for T to re-use this
    13: //   // member while the object is not free in the pool
    14: //   T *nextInFreeList;
    15: //
    16: //   // object is done being used for now
    17: //   void deinit();
    18: //
    19: //   // needed so we can make arrays
    20: //   T::T();
    21: 
    22: template <class T>
    23: class ObjectPool {
    24: private:     // data
    25:   // when the pool needs to expand, it expands by allocating an
    26:   // additional 'rackSize' objects; I use a linear (instead of
    27:   // exponential) expansion strategy because these are supposed
    28:   // to be used for small sets of rapidly-reused objects, not
    29:   // things allocated for long-term storage
    30:   int rackSize;
    31: 
    32:   // growable array of pointers to arrays of 'rackSize' T objects
    33:   ArrayStack<T*> racks;
    34: 
    35:   // head of the free list; NULL when empty
    36:   T *head;
    37: 
    38: private:     // funcs
    39:   void expandPool();
    40: 
    41: public:      // funcs
    42:   ObjectPool(int rackSize);
    43:   ~ObjectPool();
    44: 
    45:   // yields a pointer to an object ready to be used; typically,
    46:   // T should have some kind of init method to play the role a
    47:   // constructor ordinarily does; this might cause the pool to
    48:   // expand (but previously allocated objects do *not* move)
    49:   inline T *alloc();
    50: 
    51:   // return an object to the pool of objects; dealloc internally
    52:   // calls obj->deinit()
    53:   inline void dealloc(T *obj);
    54: 
    55:   // same as 'dealloc', but without the call to 'deinit'
    56:   inline void deallocNoDeinit(T *obj);
    57: 
    58:   // available for diagnostic purposes
    59:   int freeObjectsInPool() const;
    60: 
    61:   // low-level access for heavily-optimized client code; clients that
    62:   // use these functions accept the burden of possibly needing to
    63:   // change if internals of ObjectPool change
    64:   T *private_getHead() { return head; }
    65:   void private_setHead(T *h) { head = h; }
    66: };
    67: 
    68: 
    69: template <class T>
    70: ObjectPool<T>::ObjectPool(int rs)
    71:   : rackSize(rs),
    72:     racks(5),
    73:     head(NULL)
    74: {}
    75: 
    76: template <class T>
    77: ObjectPool<T>::~ObjectPool()
    78: {
    79:   // deallocate all the objects in the racks
    80:   for (int i=0; i < racks.length(); i++) {
    81:     delete[] racks[i];
    82:   }
    83: }
    84: 
    85: 
    86: template <class T>
    87: inline T *ObjectPool<T>::alloc()
    88: {
    89:   if (!head) {
    90:     // need to expand the pool
    91:     expandPool();
    92:   }
    93: 
    94:   T *ret = head;                     // prepare to return this one
    95:   head = ret->nextInFreeList;        // move to next free node
    96: 
    97:   #ifndef NDEBUG
    98:     ret->nextInFreeList = NULL;        // paranoia
    99:   #endif
   100: 
   101:   return ret;
   102: }
   103: 
   104: 
   105: // this is pulled out of 'alloc' so alloc can be inlined
   106: // without causing excessive object code bloat
   107: template <class T>
   108: void ObjectPool<T>::expandPool()
   109: {
   110:   T *rack = new T[rackSize];
   111:   racks.push(rack);
   112: 
   113:   // thread new nodes into a free list
   114:   for (int i=rackSize-1; i>=0; i--) {
   115:     rack[i].nextInFreeList = head;
   116:     head = &(rack[i]);
   117:   }
   118: }
   119: 
   120: 
   121: // usually I want the convenience of dealloc calling deinit; however,
   122: // in the inner loop of a performance-critical section of code, I
   123: // want finer control
   124: template <class T>
   125: inline void ObjectPool<T>::deallocNoDeinit(T *obj)
   126: {
   127:   // I don't check that nextInFreeList == NULL, despite having set it
   128:   // that way in alloc(), because I want to allow for users to make
   129:   // nextInFreeList share storage (e.g. with a union) with some other
   130:   // field that gets used while the node is allocated
   131: 
   132:   // prepend the object to the free list; will be next yielded
   133:   obj->nextInFreeList = head;
   134:   head = obj;
   135: }
   136: 
   137: 
   138: template <class T>
   139: inline void ObjectPool<T>::dealloc(T *obj)
   140: {
   141:   // call obj's pseudo-dtor (the decision to have dealloc do this is
   142:   // motivated by not wanting to have to remember to call deinit
   143:   // before dealloc)
   144:   obj->deinit();
   145: 
   146:   deallocNoDeinit(obj);
   147: }
   148: 
   149: 
   150: template <class T>
   151: int ObjectPool<T>::freeObjectsInPool() const
   152: {
   153:   T *p = head;
   154:   int ct = 0;
   155: 
   156:   while (p) {
   157:     ct++;
   158:     p = p->nextInFreeList;
   159:   }
   160: 
   161:   return ct;
   162: }
   163: 
   164: 
   165: #endif // OBJPOOL_H
End C section to elk/sm_objpool.h[1]
Start C section to elk/sm_objstack.h[1 /1 ]
     1: #line 3177 "./lpsrc/sm.pak"
     2: // objstack.h            see license.txt for copyright and terms of use
     3: // stack of objects, owned by the stack
     4: 
     5: #ifndef OBJSTACK_H
     6: #define OBJSTACK_H
     7: 
     8: #include "sm_objlist.h"
     9: 
    10: template <class T>
    11: class ObjStack {
    12: private:      // data
    13:   // will implement the stack as a list, with prepend and removeAt(0)
    14:   ObjList<T> list;
    15: 
    16: public:       // funcs
    17:   ObjStack()                            : list() {}
    18:   ~ObjStack()                           {}
    19: 
    20:   int count() const                     { return list.count(); }
    21:   bool isEmpty() const                  { return list.isEmpty(); }
    22:   bool isNotEmpty() const               { return list.isNotEmpty(); }
    23: 
    24:   T const *topC() const                 { return list.firstC(); }
    25:   T * /*serf*/ top()                    { return list.first(); }
    26: 
    27:   T * /*owner*/ pop()                   { return list.removeAt(0); }
    28:   void delPop()                         { list.deleteAt(0); }
    29:   void push(T *item)                    { list.prepend(item); }
    30:   void clear()                          { list.deleteAll(); }
    31: 
    32:   bool contains(T const *item) const    { return list.contains((void*)item); }
    33: };
    34: 
    35: #endif // OBJSTACK_H
End C section to elk/sm_objstack.h[1]
Start C section to elk/sm_ohashtbl.h[1 /1 ]
     1: #line 3213 "./lpsrc/sm.pak"
     2: // ohashtbl.h            see license.txt for copyright and terms of use
     3: // hash table that owns the values; uses void* keys
     4: // see hashtbl.h for more detail on the semantics of the member fns
     5: 
     6: #ifndef OHASHTBL_H
     7: #define OHASHTBL_H
     8: #include "sm_typ.h"
     9: #include "sm_hashtbl.h"
    10: 
    11: template <class T> class OwnerHashTableIter;
    12: 
    13: template <class T>
    14: class OwnerHashTable {
    15: public:     // types
    16:   friend class OwnerHashTableIter<T>;
    17: 
    18:   // see hashtbl.h
    19:   typedef void const* (*GetKeyFn)(T *data);
    20:   typedef unsigned (*HashFn)(void const *key);
    21:   typedef bool (*EqualKeyFn)(void const *key1, void const *key2);
    22: 
    23: private:    // data
    24:   // inner table that does the hash mapping
    25:   HashTable table;
    26: 
    27: public:     // funcs
    28:   OwnerHashTable(GetKeyFn gk, HashFn hf, EqualKeyFn ek,
    29:                  int initSize = HashTable::defaultSize)
    30:     : table((HashTable::GetKeyFn)gk, hf, ek, initSize) {}
    31:   ~OwnerHashTable() { empty(1); }
    32: 
    33:   int getNumEntries() const               { return table.getNumEntries(); }
    34:   T *get(void const *key) const           { return (T*)table.get(key); }
    35:   void add(void const *key, T *value)     { table.add(key, value); }
    36:   T *remove(void const *key)              { return (T*)table.remove(key); }
    37:   void empty(int initSize = HashTable::defaultSize);
    38:   void setEnableShrink(bool en)           { table.setEnableShrink(en); }
    39:   void selfCheck() const                  { table.selfCheck(); }
    40: 
    41:   // this simply drops all the entries without deleting them; it is
    42:   // useful when the objects have been taken out via iteration
    43:   void disownAndForgetAll(int initSize = HashTable::defaultSize)
    44:                                           { table.empty(initSize); }
    45: };
    46: 
    47: template <class T>
    48: void OwnerHashTable<T>::empty(int initSize)
    49: {
    50:   HashTableIter iter(table);
    51:   for (; !iter.isDone(); iter.adv()) {
    52:     delete (T*)iter.data();
    53:   }
    54:   table.empty(initSize);
    55: }
    56: 
    57: 
    58: template <class T>
    59: class OwnerHashTableIter {
    60: private:      // data
    61:   HashTableIter iter;      // internal iterator
    62: 
    63: public:       // funcs
    64:   OwnerHashTableIter(OwnerHashTable<T> &table)
    65:     : iter(table.table) {}
    66: 
    67:   bool isDone() const      { return iter.isDone(); }
    68:   void adv()               { iter.adv(); }
    69:   T *data()                { return (T*)iter.data(); }
    70: };
    71: 
    72: #endif // OHASHTBL_H
End C section to elk/sm_ohashtbl.h[1]
Start C section to elk/sm_okhasharr.h[1 /1 ]
     1: #line 3286 "./lpsrc/sm.pak"
     2: // okhasharr.h            see license.txt for copyright and terms of use
     3: // combination of an owner hash table and an array/stack
     4: //
     5: // in its present form, it's ideal for a worklist, but not
     6: // for a 'finished' list, due to inability to randomly remove
     7: 
     8: #ifndef OKHASHARR_H
     9: #define OKHASHARR_H
    10: 
    11: #include "sm_array.h"
    12: #include "sm_okhashtbl.h"
    13: 
    14: // T is value, K is key
    15: template <class T, class K>
    16: class OwnerKHashArray {
    17: private:    // data
    18:   OwnerKHashTable<T,K> hash;
    19:   ArrayStack<T*> stack;
    20: 
    21: public:     // funcs
    22:   OwnerKHashArray(typename OwnerKHashTable<T,K>::GetKeyFn gk,
    23:                   typename OwnerKHashTable<T,K>::HashFn hf,
    24:                   typename OwnerKHashTable<T,K>::EqualKeyFn ek,
    25:                   int initSize = HashTable::defaultSize)
    26:     : hash(gk, hf, ek, initSize),
    27:       stack(initSize)
    28:   {
    29:     hash.setEnableShrink(false);
    30:   }
    31:   ~OwnerKHashArray();
    32: 
    33:   // # elts in the structure
    34:   int count() const                  { return stack.length(); }
    35:   bool isEmpty() const               { return stack.isEmpty(); }
    36:   bool isNotEmpty() const            { return !isEmpty(); }
    37: 
    38:   // access as a hashtable
    39:   T *lookup(K const *key) const      { return hash.get(key); }
    40:   K const *callGetKeyFn(T *data)     { return hash.callGetKeyFn(data); }
    41: 
    42:   // TODO: make a new base-level implementation so I can support
    43:   // removal of arbitrary objects efficiently
    44: 
    45:   // access as a stack
    46:   void push(K const *key, T *value) {
    47:     hash.add(key, value);
    48:     stack.push(value);
    49:   }
    50: 
    51:   T *pop() {
    52:     T *ret = stack.pop();
    53:     hash.remove(hash.callGetKeyFn(ret));
    54:     return ret;
    55:   }
    56: };
    57: 
    58: 
    59: template <class T, class K>
    60: OwnerKHashArray<T,K>::~OwnerKHashArray()
    61: {}
    62: 
    63: 
    64: #endif // OKHASHARR_H
End C section to elk/sm_okhasharr.h[1]
Start C section to elk/sm_okhashtbl.h[1 /1 ]
     1: #line 3351 "./lpsrc/sm.pak"
     2: // okhashtbl.h            see license.txt for copyright and terms of use
     3: // version of ohasharr.h with type-safe keys ("k" for keys)
     4: 
     5: #ifndef OKHASHTBL_H
     6: #define OKHASHTBL_H
     7: 
     8: #include "sm_typ.h"
     9: #include "sm_hashtbl.h"
    10: 
    11: template <class T, class K> class OwnerKHashTableIter;
    12: 
    13: // T is the value type, K is the key type
    14: template <class T, class K>
    15: class OwnerKHashTable {
    16: public:     // types
    17:   friend class OwnerKHashTableIter<T,K>;
    18: 
    19:   // see hashtbl.h
    20:   typedef K const* (*GetKeyFn)(T *data);
    21:   typedef unsigned (*HashFn)(K const *key);
    22:   typedef bool (*EqualKeyFn)(K const *key1, K const *key2);
    23: 
    24: private:    // data
    25:   // inner table that does the hash mapping
    26:   HashTable table;
    27: 
    28: public:     // funcs
    29:   OwnerKHashTable(GetKeyFn gk, HashFn hf, EqualKeyFn ek,
    30:                   int initSize = HashTable::defaultSize)
    31:     : table((HashTable::GetKeyFn)gk,
    32:             (HashTable::HashFn)hf,
    33:             (HashTable::EqualKeyFn)ek,
    34:             initSize) {}
    35:   ~OwnerKHashTable() { empty(1); }
    36: 
    37:   int getNumEntries() const               { return table.getNumEntries(); }
    38:   T *get(K const *key) const              { return (T*)table.get(key); }
    39:   void add(K const *key, T *value)        { table.add(key, value); }
    40:   T *remove(K const *key)                 { return (T*)table.remove(key); }
    41:   void empty(int initSize = HashTable::defaultSize);
    42:   void setEnableShrink(bool en)           { table.setEnableShrink(en); }
    43:   K const *callGetKeyFn(T *data)          { return (K const*)table.callGetKeyFn(data); }
    44:   void selfCheck() const                  { table.selfCheck(); }
    45: 
    46:   // this simply drops all the entries without deleting them; it is
    47:   // useful when the objects have been taken out via iteration
    48:   void disownAndForgetAll(int initSize = HashTable::defaultSize)
    49:                                           { table.empty(initSize); }
    50: };
    51: 
    52: template <class T, class K>
    53: void OwnerKHashTable<T,K>::empty(int initSize)
    54: {
    55:   HashTableIter iter(table);
    56:   for (; !iter.isDone(); iter.adv()) {
    57:     delete (T*)iter.data();
    58:   }
    59:   table.empty(initSize);
    60: }
    61: 
    62: 
    63: template <class T, class K>
    64: class OwnerKHashTableIter {
    65: private:      // data
    66:   HashTableIter iter;      // internal iterator
    67: 
    68: public:       // funcs
    69:   OwnerKHashTableIter(OwnerKHashTable<T,K> &table)
    70:     : iter(table.table) {}
    71: 
    72:   bool isDone() const      { return iter.isDone(); }
    73:   void adv()               { iter.adv(); }
    74:   T *data()                { return (T*)iter.data(); }
    75: };
    76: 
    77: #endif // OKHASHTBL_H
End C section to elk/sm_okhashtbl.h[1]
Start C section to elk/sm_oobjmap.h[1 /1 ]
     1: #line 3429 "./lpsrc/sm.pak"
     2: // oobjmap.h            see license.txt for copyright and terms of use
     3: // owner object map, implemented with a hash table
     4: // maps pointer-to-key-object to pointer-to-value-object, and owns
     5: // all instances of value-object (they are deallocated when the
     6: // map itself goes away)
     7: 
     8: // I had envisioned an interface which didn't require the function
     9: // that maps values back to keys.. given that OwnerHashTable requires
    10: // this, I'll suspend this line of work for now and keep using
    11: // OwnerHashTable directly
    12: #error This does not work quite right
    13: 
    14: #ifndef OOBJMAP_H
    15: #define OOBJMAP_H
    16: 
    17: #include "sm_ohashtbl.h"
    18: 
    19: template <class Key, class Value> class OObjMapIter;
    20: 
    21: template <class Key, class Value>
    22: class OObjMap {
    23: public:     // types
    24:   friend class OObjMapIter<Key, Value>;
    25: 
    26: private:    // data
    27:   OwnerHashTable<Value> table;            // implementation
    28: 
    29: public:     // funcs
    30:   OObjMap() {}
    31:   ~OObjMap() {}                           // deallocates Value objects
    32: 
    33:   int getNumEntries() const               { return table.getNumEntries(); }
    34:   Value *get(Key const *key) const        { return table.get(key); }
    35:   void add(Key const *key, Value *value)  { table.add(key, value); }
    36:   Value *remove(Key const *key)           { return table.remove(key); }
    37:   void empty()                            { table.empty(); }
    38:   void selfCheck() const                  { table.selfCheck(); }
    39: };
    40: 
    41: 
    42: template <class Key, class Value>
    43: class OObjMapIter {
    44: private:    // data
    45:   OwnerHashTableIter<Value> iter;         // implementation
    46: 
    47: public:
    48:   OObjMapIter(OObjMap<Key,Value> &map)    : iter(map.table) {}
    49: 
    50:   bool isDone() const                     { return iter.isDone(); }
    51:   void adv()                              { iter.adv(); }
    52:   Value *data()                           { return iter.data(); }
    53: };
    54: 
    55: 
    56: #endif // OOBJMAP_H
End C section to elk/sm_oobjmap.h[1]
Start C section to elk/sm_owner.h[1 /1 ]
     1: #line 3486 "./lpsrc/sm.pak"
     2: // owner.h            see license.txt for copyright and terms of use
     3: // a stab at an owner ptr abstraction
     4: 
     5: #ifndef OWNER_H
     6: #define OWNER_H
     7: 
     8: #include "sm_typ.h"
     9: 
    10: #ifdef DEBUG_OWNER
    11:   #include <stdio.h>    // printf, temporary
    12:   #define DBG(fn) printf("%s(%p)\n", fn, ptr)
    13: #else
    14:   #define DBG(fn)
    15: #endif
    16: 
    17: template <class T>
    18: class Owner {
    19: private:    // data
    20:   T *ptr;                // the real pointer
    21: 
    22: private:    // funcs
    23:   Owner(Owner&);         // not allowed
    24: 
    25: public:     // funcs
    26:   Owner(T *p = NULL) : ptr(p) { DBG("ctor"); }
    27:   ~Owner() { DBG("dtor"); del(); }
    28: 
    29:   // take ownership (no transitive = here)
    30:   void operator= (T *p) { DBG("op=ptr"); del(); ptr=p; }
    31:   void operator= (Owner<T> &obj) { DBG("op=obj"); del(); ptr=obj.ptr; obj.ptr=NULL; }
    32: 
    33:   // release ownership
    34:   T *xfr() { DBG("xfr"); T *temp = ptr; ptr = NULL; return temp; }
    35: 
    36:   // free
    37:   void del() { DBG("del"); delete ptr; ptr = NULL; }    // relies on delete(NULL) being ok
    38: 
    39:   // some operators that make Owner behave more or less like
    40:   // a native C++ pointer.. note that some compilers to really
    41:   // bad handling the "ambiguity", so the non-const versions
    42:   // can be disabled at compile time
    43:   operator T const* () const { DBG("opcT*"); return ptr; }
    44:   T const & operator* () const { DBG("opc*"); return *ptr; }
    45:   T const * operator-> () const { DBG("opc->"); return ptr; }
    46: 
    47:   // according to http://www.google.com/search?q=cache:zCRFFDMZvVUC:people.we.mediaone.net/stanlipp/converops.htm+conversion+sequence+for+the+argument+is+better&hl=en&ie=ISO-8859-1,
    48:   // a solution to the gcc "conversion sequence is better" complaint
    49:   // is to define this version
    50:   operator T const* () { DBG("opcT*_nc"); return ptr; }
    51: 
    52:   #ifndef NO_OWNER_NONCONST
    53:   operator T* () { DBG("opT*"); return ptr; }
    54:   T& operator* () { DBG("op*"); return *ptr; }
    55:   T* operator-> () { DBG("op->"); return ptr; }
    56:   #endif
    57: 
    58:   // escape hatch for when operators flake out on us
    59:   T *get() { DBG("get"); return ptr; }
    60:   T const *getC() const { DBG("getC"); return ptr; }
    61: 
    62:   // even more dangerous escape; only use where the caller
    63:   // agrees to restore the owner invariant!
    64:   T *&getRef() { DBG("getRed"); return ptr; }
    65: 
    66:   // swaps are interesting because they don't require checking
    67:   void swapWith(Owner<T> &obj) {
    68:     T *tmp = ptr;
    69:     ptr = obj.ptr;
    70:     obj.ptr = tmp;
    71:   }
    72: };
    73: 
    74: 
    75: template <class T>
    76: void swap(Owner<T> &obj1, Owner<T> &obj2)
    77: {
    78:   obj1.swapWith(obj2);
    79: }
    80: 
    81: 
    82: // not used with Owner objects, but rather with
    83: // simple pointers (Foo*) that are used as owners
    84: template <class T>
    85: T *xfr(T *&ptr)
    86: {
    87:   T *ret = ptr;
    88:   ptr = NULL;
    89:   return ret;
    90: }
    91: 
    92: 
    93: #endif // OWNER_H
End C section to elk/sm_owner.h[1]
Start C section to elk/sm_point.h[1 /1 ]
     1: #line 3580 "./lpsrc/sm.pak"
     2: // point.h            see license.txt for copyright and terms of use
     3: // 2-dimensional point
     4: // derived from Melee's prmtvs2.hpp
     5: 
     6: #ifndef __POINT_H
     7: #define __POINT_H
     8: 
     9: #include "sm_typ.h"
    10: 
    11: // point defined over arbitrary underlying types
    12: template <class num>
    13: class TPoint {
    14: public:
    15:   num x, y;
    16: 
    17: public:
    18:   TPoint() {}
    19:   TPoint(num nx, num ny) { set(nx,ny); }
    20:   TPoint(TPoint<num> const &obj) : x(obj.x), y(obj.y) {}
    21: 
    22:   TPoint const& operator = (TPoint<num> const &obj)
    23:     { x=obj.x; y=obj.y; return *this; }
    24: 
    25:   void set(num nx, num ny) { x=nx; y=ny; }
    26:   void get(num *gx, num *gy) const { *gx=x; *gy=y; }
    27:   bool zero() const { return x==0 && y==0; }
    28:   bool gtez() const { return x>=0 && y>=0; }
    29: 
    30:   TPoint<num> operator - () const
    31:     { return TPoint<num>(-x, -y); }
    32:   TPoint<num> absval() const
    33:     { return TPoint<num>(x<0? -x : x, y<0? -y : y); }
    34:       // don't call abs() because that requires stdlib.h
    35:       // also, don't call the function "abs" because abs() is usually
    36:       //   implemented as a macro
    37: 
    38:   TPoint<num> operator + (TPoint<num> const &obj) const
    39:     { return TPoint<num>(x+obj.x, y+obj.y); }
    40:   TPoint<num> operator - (TPoint<num> const &obj) const
    41:     { return TPoint<num>(x-obj.x, y-obj.y); }
    42:   num dotprod(TPoint<num> const &obj) const
    43:     { return x*obj.x + y*obj.y; }
    44: 
    45:   // for the arithmetic functions of the form point <op> num, <op> num is
    46:   // applied to x and y independently
    47: 
    48:   TPoint<num> operator * (num factor) const
    49:     { return TPoint<num>(x*factor, y*factor); }
    50:   TPoint<num> operator / (num factor) const
    51:     { return TPoint<num>(x/factor, y/factor); }
    52: 
    53:   TPoint<num> operator * (TPoint<num> const &factor) const
    54:     { return TPoint<num>(x*factor.x, y*factor.y); }
    55:   TPoint<num> operator / (TPoint<num> const &factor) const
    56:     { return TPoint<num>(x/factor.x, y/factor.y); }
    57: 
    58:   TPoint<num> operator += (TPoint<num> const &obj)
    59:     { x+=obj.x; y+=obj.y; return *this; }
    60:   TPoint<num> operator -= (TPoint<num> const &obj)
    61:     { x-=obj.x; y-=obj.y; return *this; }
    62:   TPoint<num> operator *= (num factor)
    63:     { x*=factor; y*=factor; return *this; }
    64:   TPoint<num> operator /= (num factor)
    65:     { x/=factor; y/=factor; return *this; }
    66: 
    67:   bool operator == (TPoint<num> const &obj) const
    68:     { return x==obj.x && y==obj.y; }
    69:   bool operator != (TPoint<num> const &obj) const
    70:     { return ! operator==(obj); }
    71: 
    72:   // use with care; note that each relation requires the relation to hold for
    73:   // *both* x and y for it to be true (this is different than, for example, the
    74:   // way STL does relations between pairs)
    75:   bool operator > (TPoint<num> const &obj) const
    76:     { return x>obj.x && y>obj.y; }
    77:   bool operator >= (TPoint<num> const &obj) const
    78:     { return x>=obj.x && y>=obj.y; }
    79:   bool operator < (TPoint<num> const &obj) const
    80:     { return x<obj.x && y<obj.y; }
    81:   bool operator <= (TPoint<num> const &obj) const
    82:     { return x<=obj.x && y<=obj.y; }
    83: };
    84: 
    85: 
    86: // common incarnations
    87: typedef TPoint<int> point;
    88: typedef TPoint<double> fpoint;
    89: 
    90: 
    91: // and we can then define sm_stringBuilder output ops for them
    92: class ELK_EXTERN sm_stringBuilder;
    93: sm_stringBuilder& operator<< (sm_stringBuilder &sb, point const &pt);
    94: sm_stringBuilder& operator<< (sm_stringBuilder &sb, fpoint const &pt);
    95: 
    96: 
    97: // iterate: 0,0    1,0    2,0    ... x-1,0    and then
    98: //          0,1    1,1    2,1    ... x-1,1    and then
    99: //           .                          .
   100: //           .                          .     (each line in succession)
   101: //           .                          .
   102: //          0,y-1  1,y-1  2,y-1  ... x-1,y-1  done
   103: // can 'break' out of the loop at any time
   104: 
   105: #define FOREACH_point(size, var)   \
   106:   if ((size).x > 0)                \
   107:     for(point var(0,0); var.y < (size).y; ++var.x == (size).x && (var.x=0,var.y++))
   108: 
   109: 
   110: #endif // ___POINT_H
   111: 
End C section to elk/sm_point.h[1]
Start C section to elk/sm_pprint.h[1 /1 ]
     1: #line 3692 "./lpsrc/sm.pak"
     2: // pprint.h
     3: // pretty-print code while emitting it
     4: 
     5: 
     6: 
     7: // NOTE: This module is a little simpler to use, but much less
     8: // powerful than the 'boxprint' module.  I'm leaving this module
     9: // here for now, but will probably delete it at some point.
    10: 
    11: 
    12: 
    13: // inspired by:
    14: //   CIL's 'pretty' module
    15: //   http://www.cs.berkeley.edu/~necula/cil/index.html
    16: // and
    17: //   Caml pretty-print module (boxes, etc.)
    18: //   http://caml.inria.fr/FAQ/format-eng.html
    19: 
    20: // special characters:
    21: //   '\n' - hard linebreak
    22: //   '\r' - optional linebreak; is 1 space if the break isn't taken
    23: //   '\b' - begin a break group (return to <here>)
    24: //   '\a' - alternate begin group (return to <this_line_ind> + altIndent)
    25: //   '\f' - finish a break group
    26: 
    27: #ifndef PPRINT_H
    28: #define PPRINT_H
    29: 
    30: #include <iostream>      // std::ostream
    31: #include "sm_str.h"
    32: #include "sm_array.h"
    33: 
    34: 
    35: // output interface for PPrint.. I'd like to just start using the
    36: // C++ istd::ostreams interfaces, but reading on the net I get the
    37: // impression they're still a little too much in flux
    38: class PPrintOut {
    39: public:
    40:   virtual void write(char const *text) = 0;
    41:   virtual ~PPrintOut(){}
    42: };
    43: 
    44: class PPrintStringOut : public PPrintOut {
    45:   sm_stringBuilder &sb;
    46: public:
    47:   PPrintStringOut(sm_stringBuilder &s) : sb(s) {}
    48:   virtual void write(char const *text);
    49: };
    50: 
    51: class PPrintOstreamOut : public PPrintOut {
    52:   std::ostream &os;
    53: public:
    54:   PPrintOstreamOut(std::ostream &o) : os(o) {}
    55:   virtual void write(char const *text);
    56: };
    57: 
    58: 
    59: // pretty printer formatting engine
    60: class PPrint {
    61: private:     // types
    62:   // manages the line-setting algorithm
    63:   class Setter {
    64:   private:     // data
    65:     // inter-line information
    66:     PPrint &pprint;
    67: 
    68:     // emitted text in the current line
    69:     sm_stringBuilder curLine;
    70: 
    71:     // indentation used for 'curLine'
    72:     int curLineInd;
    73: 
    74:     // place in the 'line' buffer; all the chars up to this point
    75:     // have been sent out
    76:     int lineIndex;
    77: 
    78:     // stack of columns at which indent groups opened
    79:     ArrayStack<int> indentGroups;
    80: 
    81:   private:     // funcs
    82:     // add 'amt' spaces to 'curLine'
    83:     void indent(int amt);
    84: 
    85:     // copy characters [lineIndex,lineIndex+p-1] from 'line' into
    86:     // 'curLine', moving 'lineIndex' along so eventually it equals
    87:     // 'p'; also maintain 'indentGroups'
    88:     void emitTo(int p);
    89: 
    90:     // send all of 'curLine' to 'pprint.out', and clear 'curLine'
    91:     void flush();
    92: 
    93:   public:      // funcs
    94:     Setter(PPrint &p)
    95:       : pprint(p),
    96:         curLine(),
    97:         curLineInd(0),
    98:         lineIndex(0),
    99:         indentGroups()
   100:     {}
   101:     ~Setter();
   102: 
   103:     void set();
   104:   };
   105:   friend class Setter;
   106: 
   107: private:     // data
   108:   // the contents of each line, up to a hard linebreak, is accumulated here
   109:   ArrayStack<char> line;
   110: 
   111: public:      // data
   112:   // current indentation level for the beginning of a complete line
   113:   // (one preceded by a hard linebreak)
   114:   int lineIndent;
   115: 
   116:   // desired right margin; we'll try to set text so it doesn't go
   117:   // beyond that many columns; defaults to 72
   118:   int margin;
   119: 
   120:   // incremental indentation for '\a' groups; defaults to 2
   121:   int altIndent;
   122: 
   123:   // if not NULL, text to emit at the start of every line; intended
   124:   // for emitting text into a comment or other embedded context;
   125:   // defaults to NULL; not counted against the margin
   126:   char const *startText;
   127: 
   128:   // where to send output
   129:   PPrintOut &out;
   130: 
   131:   // When true, and we find that the grouping is unbalanced at
   132:   // the end of setting a line, pring a warning.  This defaults
   133:   // to 'true'.  Note that while too many '\b's will only trigger
   134:   // this warning, too many '\f's can cause an assertion failure
   135:   // when the indentation stack underflows.
   136:   static bool warnWhenUnbalanced;
   137: 
   138: private:     // funcs
   139:   // take the current line buffer and break it up into output
   140:   // lines, sending them to 'out'
   141:   void set();
   142: 
   143: public:      // funcs
   144:   PPrint(PPrintOut &out);
   145:   ~PPrint();
   146: 
   147:   // basic printing routine; the text can contain the special
   148:   // characters listed above; whenever a '\n' is seen, the current
   149:   // line is set and emitted to 'out'
   150:   void print(char const *text);
   151: 
   152:   // convenience
   153:   PPrint& operator<< (int i);
   154:   PPrint& operator<< (char const *s);
   155: 
   156:   // manage the line-start indentation
   157:   void ind(int amt) { lineIndent += amt; }
   158: };
   159: 
   160: 
   161: class PPrintToString : public PPrint {
   162: public:
   163:   sm_stringBuilder sb;            // output (set) lines accumulate here
   164:   PPrintStringOut sbOut;       // helper
   165: 
   166: public:
   167:   PPrintToString()
   168:     : PPrint(sbOut), sb(), sbOut(sb) {}
   169:   ~PPrintToString();
   170: };
   171: 
   172: class PPrintToOstream : public PPrint {
   173:   PPrintOstreamOut osOut;
   174: 
   175: public:
   176:   PPrintToOstream(std::ostream &os)
   177:     : PPrint(osOut), osOut(os) {}
   178: };
   179: 
   180: 
   181: #endif // PPRINT_H
End C section to elk/sm_pprint.h[1]
Start C section to elk/sm_ptrmap.h[1 /1 ]
     1: #line 3874 "./lpsrc/sm.pak"
     2: // ptrmap.h
     3: // map from KEY* to VALUE* for arbitrary types KEY and VALUE
     4: // (neither are owned by the table)
     5: 
     6: // for const purposes, I regard the mapping itself as the only
     7: // thing that cannot be modified in a "const" map; in particular,
     8: // I allow a non-const VALUE* to be extracted
     9: 
    10: #ifndef PTRMAP_H
    11: #define PTRMAP_H
    12: 
    13: #include "sm_vptrmap.h"
    14: #include "sm_typ.h"
    15: 
    16: 
    17: template <class KEY, class VALUE>
    18: class PtrMap {
    19: private:     // data
    20:   // underlying map implementation, around which this class
    21:   // is a type-safe wrapper
    22:   VoidPtrMap map;
    23: 
    24: public:      // funcs
    25:   PtrMap()                         : map() {}
    26:   ~PtrMap()                        {}
    27: 
    28:   // query # of mapped entries
    29:   int getNumEntries() const        { return map.getNumEntries(); }
    30:   bool isEmpty() const             { return getNumEntries() == 0; }
    31:   bool isNotEmpty() const          { return !isEmpty(); }
    32: 
    33:   // if this key has a mapping, return it; otherwise, return NULL
    34:   VALUE *get(KEY const *key) const { return (VALUE*)map.get((void const*)key); }
    35: 
    36:   // add a mapping from 'key' to 'value'; replaces existing
    37:   // mapping, if any
    38:   void add(KEY *key, VALUE *value) { map.add((void*)key, (void*)value); }
    39: 
    40:   // remove all mappings
    41:   void empty()                     { map.empty(); }
    42: 
    43: 
    44: public:      // iterators
    45:   class Iter {
    46:   private:     // data
    47:     // underlying iterator state
    48:     VoidPtrMap::Iter iter;
    49: 
    50:   public:      // fucs
    51:     Iter(PtrMap<KEY,VALUE> const &map)   : iter(map.map) {}
    52:     ~Iter()                              {}
    53: 
    54:     bool isDone() const            { return iter.isDone(); }
    55:     void adv()                     { return iter.adv(); }
    56: 
    57:     // return information about the currently-referenced table entry
    58:     KEY *key() const               { return (KEY*)iter.key(); }
    59:     VALUE *value() const           { return (VALUE*)iter.value(); }
    60:   };
    61:   friend class Iter;
    62: };
    63: 
    64: 
    65: // a set based on PtrMap
    66: template <class KEY>
    67: class PtrSet : private PtrMap<KEY, KEY> {
    68:   public:
    69:   PtrSet() {}
    70:   ~PtrSet() {}
    71: 
    72:   // query # of mapped entries
    73:   int getNumEntries() const        { return PtrMap<KEY, KEY>::getNumEntries(); }
    74:   bool isEmpty() const             { return PtrMap<KEY, KEY>::isEmpty(); }
    75:   bool isNotEmpty() const          { return PtrMap<KEY, KEY>::isNotEmpty(); }
    76: 
    77:   // if this key has a mapping, return it; otherwise, return NULL
    78:   bool contains(KEY const *key) const { return PtrMap<KEY, KEY>::get(key)!=NULL; }
    79: 
    80:   // add key to the set
    81:   void add(KEY *key) { PtrMap<KEY, KEY>::add(key, key); }
    82: 
    83:   // make the set empty; FIX: this would be better named makeEmpty(),
    84:   // as it could be confused with the meaning of isEmpty(); however I
    85:   // reflect the naming of PtrMap, where the same criticism applies.
    86:   void empty()                     { PtrMap<KEY, KEY>::empty(); }
    87: };
    88: 
    89: 
    90: #endif // PTRMAP_H
End C section to elk/sm_ptrmap.h[1]
Start C section to elk/sm_flexlexer.h[1 /1 ]
     1: #line 3965 "./lpsrc/sm.pak"
     2: // sm_flexlexer.h
     3: // a layer of indirection to try to work with different
     4: // installed versions of Flex
     5: 
     6: 
     7: // Basically, what has happened is the last version of flex created by
     8: // Vern Paxson et al. is 2.5.4, and a lot of distributions use that.
     9: // More recently some other people started a project hosted at
    10: // Sourceforge, and their current (only?) release is flex-2.5.31.
    11: // Unfortunately, the authors of 2.5.31 do not appear concerned with
    12: // maintaining compatibility for C++ scanners.
    13: //
    14: // See bug "[ 751550 ] c++ derived scanners will not compile",
    15: // http://sourceforge.net/tracker/index.php?func=detail&aid=751550&group_id=72099&atid=533377
    16: //
    17: // So in an attempt to make Elkhound/Elsa work, this file is where
    18: // I'll put my hacks.
    19: 
    20: 
    21: // workaround for flex-2.5.31 bug
    22: #ifdef yyFlexLexer
    23:   #undef yyFlexLexer
    24: #endif
    25: 
    26: // now get the installed FlexLexer.h.. this nominally lives in
    27: // /usr/include or /usr/local/include, depending on how Flex is
    28: // installed
    29: //#include <FlexLexer.h>
    30: 
    31: 
    32: // for now I keep the rest but I think I may be able to delete
    33: // it soon..
    34: //#if 0     // terminates at EOF
    35: // FELIX uses this text, never the system FlexLexer.h
    36: #if 1
    37: 
    38: 
    39: // copy of /usr/include/FlexLexer.h from flex-2.5.4
    40: 
    41: 
    42: // To use this file, use the same flex version as listed above.
    43: 
    44: 
    45: // Why is this file distributed with smbase?
    46: //
    47: // 1. I want my distribution tarballs to include the *output* of
    48: // tools like flex/bison, but flex's output has an #include of
    49: // FlexLexer.h.  Since I want the distribution to be self-contained,
    50: // that means distributing the .h file as well.
    51: //
    52: // 2. It avoids version mismatch.  Some people have flex-2.5.31,
    53: // some have 2.5.4.  Some of my classes depend on the internals of
    54: // the FlexLexer class which have changed between the versions, and
    55: // making it compatible with both is difficult.
    56: 
    57: 
    58: // What is its copyright status?
    59: //
    60: // I've put most of smbase into the public domain.  However, the
    61: // Regents retain copyright on this file.  I am in effect just
    62: // providing a convenient distribution mechanism for smbase users
    63: // (users *could* just download it themselves, for free, just like
    64: // smbase).
    65: 
    66: 
    67: // ------------------ original file below -----------------
    68: // $Header: /cvsroot/felix/lpsrc/sm.pak,v 1.19 2006/03/15 12:30:20 skaller Exp $
    69: 
    70: // FlexLexer.h -- define interfaces for lexical analyzer classes generated
    71: //                by flex
    72: 
    73: // Copyright (c) 1993 The Regents of the University of California.
    74: // All rights reserved.
    75: //
    76: // This code is derived from software contributed to Berkeley by
    77: // Kent Williams and Tom Epperly.
    78: //
    79: // Redistribution and use in source and binary forms with or without
    80: // modification are permitted provided that: (1) source distributions retain
    81: // this entire copyright notice and comment, and (2) distributions including
    82: // binaries display the following acknowledgement:  ``This product includes
    83: // software developed by the University of California, Berkeley and its
    84: // contributors'' in the documentation or other materials provided with the
    85: // distribution and in all advertising materials mentioning features or use
    86: // of this software.  Neither the name of the University nor the names of
    87: // its contributors may be used to endorse or promote products derived from
    88: // this software without specific prior written permission.
    89: 
    90: // THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
    91: // WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
    92: // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    93: 
    94: // This file defines FlexLexer, an abstract class which specifies the
    95: // external interface provided to flex C++ lexer objects, and yyFlexLexer,
    96: // which defines a particular lexer class.
    97: //
    98: // If you want to create multiple lexer classes, you use the -P flag
    99: // to rename each yyFlexLexer to some other xxFlexLexer.  You then
   100: // include <FlexLexer.h> in your other sources once per lexer class:
   101: //
   102: //      #undef yyFlexLexer
   103: //      #define yyFlexLexer xxFlexLexer
   104: //      #include <FlexLexer.h>
   105: //
   106: //      #undef yyFlexLexer
   107: //      #define yyFlexLexer zzFlexLexer
   108: //      #include <FlexLexer.h>
   109: //      ...
   110: 
   111: #ifndef __FLEX_LEXER_H
   112: // Never included before - need to define base class.
   113: #define __FLEX_LEXER_H
   114: #include <iostream>
   115: 
   116: // FELIX: always C++ compiles anyhow
   117: //extern "C++" {
   118: 
   119: struct yy_buffer_state;
   120: typedef int yy_state_type;
   121: 
   122: class FlexLexer {
   123: public:
   124:         virtual ~FlexLexer()    { }
   125: 
   126:         const char* YYText()    { return yytext; }
   127:         int YYLeng()            { return yyleng; }
   128: 
   129:         virtual void
   130:                 yy_switch_to_buffer( struct yy_buffer_state* new_buffer ) = 0;
   131:         virtual struct yy_buffer_state*
   132:                 yy_create_buffer( std::istream* s, int size ) = 0;
   133:         virtual void yy_delete_buffer( struct yy_buffer_state* b ) = 0;
   134:         virtual void yyrestart( std::istream* s ) = 0;
   135: 
   136:         virtual int yylex() = 0;
   137: 
   138:         // Call yylex with new input/output sources.
   139:         int yylex( std::istream* new_in, std::ostream* new_out = 0 )
   140:                 {
   141:                 switch_streams( new_in, new_out );
   142:                 return yylex();
   143:                 }
   144: 
   145:         // Switch to new input/output streams.  A nil stream pointer
   146:         // indicates "keep the current one".
   147:         virtual void switch_streams( std::istream* new_in = 0,
   148:                                         std::ostream* new_out = 0 ) = 0;
   149: 
   150:         int lineno() const              { return yylineno; }
   151: 
   152:         int debug() const               { return yy_flex_debug; }
   153:         void set_debug( int flag )      { yy_flex_debug = flag; }
   154: 
   155: protected:
   156:         char* yytext;
   157:         int yyleng;
   158:         int yylineno;           // only maintained if you use %option yylineno
   159:         int yy_flex_debug;      // only has effect with -d or "%option debug"
   160: };
   161: 
   162: //FELIX: always C++
   163: //}
   164: 
   165: #endif
   166: 
   167: #if defined(yyFlexLexer) || ! defined(yyFlexLexerOnce)
   168: // Either this is the first time through (yyFlexLexerOnce not defined),
   169: // or this is a repeated include to define a different flavor of
   170: // yyFlexLexer, as discussed in the flex man page.
   171: #define yyFlexLexerOnce
   172: 
   173: class yyFlexLexer : public FlexLexer {
   174: public:
   175:         // arg_yyin and arg_yyout default to the cin and cout, but we
   176:         // only make that assignment when initializing in yylex().
   177:         yyFlexLexer( std::istream* arg_yyin = 0, std::ostream* arg_yyout = 0 );
   178: 
   179:         virtual ~yyFlexLexer();
   180: 
   181:         void yy_switch_to_buffer( struct yy_buffer_state* new_buffer );
   182:         struct yy_buffer_state* yy_create_buffer( std::istream* s, int size );
   183:         void yy_delete_buffer( struct yy_buffer_state* b );
   184:         void yyrestart( std::istream* s );
   185: 
   186:         virtual int yylex();
   187:         virtual void switch_streams( std::istream* new_in, std::ostream* new_out );
   188: 
   189: protected:
   190:         virtual int LexerInput( char* buf, int max_size );
   191:         virtual void LexerOutput( const char* buf, int size );
   192:         virtual void LexerError( const char* msg );
   193: 
   194:         void yyunput( int c, char* buf_ptr );
   195:         int yyinput();
   196: 
   197:         void yy_load_buffer_state();
   198:         void yy_init_buffer( struct yy_buffer_state* b, std::istream* s );
   199:         void yy_flush_buffer( struct yy_buffer_state* b );
   200: 
   201:         int yy_start_stack_ptr;
   202:         int yy_start_stack_depth;
   203:         int* yy_start_stack;
   204: 
   205:         void yy_push_state( int new_state );
   206:         void yy_pop_state();
   207:         int yy_top_state();
   208: 
   209:         yy_state_type yy_get_previous_state();
   210:         yy_state_type yy_try_NUL_trans( yy_state_type current_state );
   211:         int yy_get_next_buffer();
   212: 
   213:         std::istream* yyin;  // input source for default LexerInput
   214:         std::ostream* yyout; // output sink for default LexerOutput
   215: 
   216:         struct yy_buffer_state* yy_current_buffer;
   217: 
   218:         // yy_hold_char holds the character lost when yytext is formed.
   219:         char yy_hold_char;
   220: 
   221:         // Number of characters read into yy_ch_buf.
   222:         int yy_n_chars;
   223: 
   224:         // Points to current character in buffer.
   225:         char* yy_c_buf_p;
   226: 
   227:         int yy_init;            // whether we need to initialize
   228:         int yy_start;           // start state number
   229: 
   230:         // Flag which is used to allow yywrap()'s to do buffer switches
   231:         // instead of setting up a fresh yyin.  A bit of a hack ...
   232:         int yy_did_buffer_switch_on_eof;
   233: 
   234:         // The following are not always needed, but may be depending
   235:         // on use of certain flex features (like REJECT or yymore()).
   236: 
   237:         yy_state_type yy_last_accepting_state;
   238:         char* yy_last_accepting_cpos;
   239: 
   240:         yy_state_type* yy_state_buf;
   241:         yy_state_type* yy_state_ptr;
   242: 
   243:         char* yy_full_match;
   244:         int* yy_full_state;
   245:         int yy_full_lp;
   246: 
   247:         int yy_lp;
   248:         int yy_looking_for_trail_begin;
   249: 
   250:         int yy_more_flag;
   251:         int yy_more_len;
   252:         int yy_more_offset;
   253:         int yy_prev_more_offset;
   254: };
   255: 
   256: #endif
   257: 
   258: #endif // 0, from top of file
End C section to elk/sm_flexlexer.h[1]
Start C section to elk/sm_regexp.h[1 /1 ]
     1: #line 4224 "./lpsrc/sm.pak"
     2: // smregexp.h
     3: // regular expression matching, etc.
     4: 
     5: // the "sm" prefix in the name is to avoid a name conflict with something
     6: // in my version of glibc..
     7: 
     8: // The regular expression language is, for now, the "Extended" regexp
     9: // language described in the regex(7) man page, itself a description
    10: // of POSIX 1003.2, section 2.8 (Regular Expression Notation).
    11: 
    12: // The interface is based on the POSIX regex functions too, but I
    13: // don't actually include regex.h here since I want to allow a
    14: // different implementation, if that becomes necessary.
    15: 
    16: #ifndef REGEXP_H
    17: #define REGEXP_H
    18: 
    19: #include "sm_macros.h"
    20: 
    21: 
    22: // ----------------- Regexp class -------------------
    23: // This class represents a compiled regexp pattern.  For maximum
    24: // efficiency, repeated uses of the same pattern should use the
    25: // same Regexp object each time instead of making a new one.
    26: class Regexp {
    27: public:      // types
    28:   // compilation flags
    29:   enum CFlags {
    30:     C_NONE     = 0x00,         // no flags
    31:     ICASE      = 0x02,         // case insensitive
    32:     NOSUB      = 0x08,         // subsm_string matches are not needed
    33:     //NEWLINE                  // still not sure what this really means
    34:   };
    35: 
    36:   // execution flags
    37:   enum EFlags {
    38:     E_NONE     = 0x00,         // no flags
    39:     NOTBOL     = 0x01,         // treat 'str' as not including beginning of line
    40:     NOTEOL     = 0x02,         // ............................ end of line
    41:   };
    42: 
    43: private:     // data
    44:   void *impl;                  // implementation data
    45:   //int numLParens;              // # of left-parens in the pattern
    46: 
    47: private:     // funcs
    48:   // not allowed
    49:   Regexp(Regexp&);
    50:   void operator=(Regexp&);
    51: 
    52:   void err(int code) NORETURN;
    53: 
    54: public:      // funcs
    55:   Regexp(char const *exp, CFlags flags = C_NONE);
    56:   ~Regexp();
    57: 
    58:   bool match(char const *str, EFlags flags = E_NONE);
    59: };
    60: 
    61: // allow '|' to be used on the flags
    62: ENUM_BITWISE_OR(Regexp::CFlags)
    63: ENUM_BITWISE_OR(Regexp::EFlags)
    64: 
    65: 
    66: // TODO: Add support for subsm_string matches by building a class to
    67: // remember the subsm_string offsets (enable 'numLParens' above)
    68: // efficiently.  Major question: do I always make an internal copy of
    69: // the sm_string in which we searched?  Leaning towards yes...
    70: 
    71: 
    72: 
    73: // --------------- convenience functions ---------------
    74: // The functions in this section are built on top of the
    75: // Regexp class in the obvious way.
    76: 
    77: // return true if 'str' matches 'exp'
    78: bool regexpMatch(char const *str, char const *exp);
    79: 
    80: 
    81: #endif // REGEXP_H
End C section to elk/sm_regexp.h[1]
Start C section to elk/sm_sobjlist.h[1 /1 ]
     1: #line 4306 "./lpsrc/sm.pak"
     2: // sobjlist.h
     3: // serf list of arbitrary objects
     4: // NOTE: automatically generated from xobjlist.h -- do not edit directly
     5: 
     6: // Author: Scott McPeak, 2000
     7: 
     8: #ifndef SOBJLIST_H
     9: #define SOBJLIST_H
    10: 
    11: #include "sm_voidlist.h"
    12: 
    13: 
    14: // forward declarations of template classes, so we can befriend them in SObjList
    15: // (not required by Borland C++ 4.5, but GNU wants it...)
    16: template <class T> class SObjListIter;
    17: template <class T> class SObjListMutator;
    18: template <class T> class SObjListIterNC;
    19: 
    20: 
    21: // the list is considered to not own any of the items; it's ok to
    22: // insert items multiple times or into multiple lists
    23: template <class T>
    24: class SObjList {
    25: private:
    26:   friend class SObjListIter<T>;
    27:   friend class SObjListMutator<T>;
    28:   friend class SObjListIterNC<T>;
    29: 
    30: protected:
    31:   VoidList list;                        // list itself
    32: 
    33: public:
    34:   // make shallow copies
    35:   SObjList(SObjList const &obj)         : list(obj.list) {}
    36:   SObjList& operator= (SObjList const &src)         { list = src.list; return *this; }
    37: 
    38: public:
    39:   SObjList()                            : list() {}
    40:   ~SObjList()                           {}    /* all items removed */
    41: 
    42:   // The difference function should return <0 if left should come before
    43:   // right, 0 if they are equivalent, and >0 if right should come before
    44:   // left.  For example, if we are sorting numbers into ascending order,
    45:   // then 'diff' would simply be subtraction.
    46:   typedef int (*Diff)(T const *left, T const *right, void *extra);
    47: 
    48:   // selectors
    49:   int count() const                     { return list.count(); }
    50:   bool isEmpty() const                  { return list.isEmpty(); }
    51:   bool isNotEmpty() const               { return list.isNotEmpty(); }
    52:   T *nth(int which)                     { return (T*)list.nth(which); }
    53:   T const *nthC(int which) const        { return (T const*)list.nth(which); }
    54:   T *first()                            { return (T*)list.first(); }
    55:   T const *firstC() const               { return (T const*)list.first(); }
    56:   T *last()                             { return (T*)list.last(); }
    57:   T const *lastC() const                { return (T const*)list.last(); }
    58: 
    59:   // insertion
    60:   void prepend(T *newitem)              { list.prepend((void*)newitem); }
    61:   void append(T *newitem)               { list.append((void*)newitem); }
    62:   void insertAt(T *newitem, int index)  { list.insertAt((void*)newitem, index); }
    63:   void insertSorted(T *newitem, Diff diff, void *extra=NULL)
    64:     { list.insertSorted((void*)newitem, (VoidDiff)diff, extra); }
    65: 
    66:   // removal
    67:   T *removeAt(int index)                { return (T*)list.removeAt(index); }
    68:   T *removeFirst()                      { return (T*)list.removeFirst(); }
    69:   void removeAll()                      { list.removeAll(); }
    70: 
    71:   // list-as-set: selectors
    72:   int indexOf(T const *item) const      { return list.indexOf((void*)item); }
    73:   int indexOfF(void *item) const        { return list.indexOfF((void*)item); }
    74:   bool contains(T const *item) const    { return list.contains((void*)item); }
    75: 
    76:   // list-as-set: mutators
    77:   bool prependUnique(T *newitem)        { return list.prependUnique((void*)newitem); }
    78:   bool appendUnique(T *newitem)         { return list.appendUnique((void*)newitem); }
    79:   void removeItem(T const *item)        { list.removeItem((void*)item); }    // whether the arg should be const is debatable..
    80:   bool removeIfPresent(T const *item)   { return list.removeIfPresent((void*)item); }
    81: 
    82:   // complex modifiers
    83:   void reverse()                                    { list.reverse(); }
    84:   void insertionSort(Diff diff, void *extra=NULL)   { list.insertionSort((VoidDiff)diff, extra); }
    85:   void mergeSort(Diff diff, void *extra=NULL)       { list.mergeSort((VoidDiff)diff, extra); }
    86: 
    87:   // and a related test
    88:   bool isSorted(Diff diff, void *extra=NULL) const  { return list.isSorted((VoidDiff)diff, extra); }
    89: 
    90:   // multiple lists
    91:   void concat(SObjList &tail)                       { list.concat(tail.list); }
    92:   void appendAll(SObjList const &tail)              { list.appendAll(tail.list); }
    93: 
    94:   // steal
    95:   void stealTailAt(int index, SObjList &tail)       { list.stealTailAt(index, tail.list); }
    96: 
    97:   // equal items in equal positions
    98:   bool equalAsLists(SObjList const &otherList, Diff diff, void *extra=NULL) const
    99:     { return list.equalAsLists(otherList.list, (VoidDiff)diff, extra); }
   100:   int compareAsLists(SObjList const &otherList, Diff diff, void *extra=NULL) const
   101:     { return list.compareAsLists(otherList.list, (VoidDiff)diff, extra); }
   102: 
   103:   // last-as-set: comparisons (NOT efficient)
   104:   bool equalAsSets(SObjList const &otherList, Diff diff, void *extra=NULL) const
   105:     { return list.equalAsSets(otherList.list, (VoidDiff)diff, extra); }
   106:   bool isSubsetOf(SObjList const &otherList, Diff diff, void *extra=NULL) const
   107:     { return list.isSubsetOf(otherList.list, (VoidDiff)diff, extra); }
   108:   bool containsByDiff(T const *item, Diff diff, void *extra=NULL) const
   109:     { return list.containsByDiff((void*)item, (VoidDiff)diff, extra); }
   110: 
   111:   // treating the pointer values themselves as the basis for comparison
   112:   bool equalAsPointerLists(SObjList const &otherList) const
   113:     { return list.equalAsPointerLists(otherList.list); }
   114:   bool equalAsPointerSets(SObjList const &otherList) const
   115:     { return list.equalAsPointerSets(otherList.list); }
   116: 
   117:   // debugging: no invariants beyond VoidList
   118:   void selfCheck() const                { list.selfCheck(); }
   119: 
   120:   // but export the additional checks for cases where they apply anyway
   121:   void checkHeapDataPtrs() const        { list.checkHeapDataPtrs(); }
   122:   void checkUniqueDataPtrs() const      { list.checkUniqueDataPtrs(); }
   123: };
   124: 
   125: 
   126: // for traversing the list and modifying it (nodes and/or structure)
   127: // NOTE: no list-modification fns should be called on 'list' while this
   128: //       iterator exists, and only one such iterator should exist for
   129: //       any given list
   130: template <class T>
   131: class SObjListMutator {
   132:   friend class SObjListIter<T>;
   133: 
   134: protected:
   135:   VoidListMutator mut;       // underlying mutator
   136: 
   137: public:
   138:   SObjListMutator(SObjList<T> &lst)     : mut(lst.list) { reset(); }
   139:   ~SObjListMutator()                    {}
   140: 
   141:   void reset()                          { mut.reset(); }
   142: 
   143:   // iterator copying; safe *only* until one of the mutators modifies
   144:   // the list structure (by inserting or removing), at which time all
   145:   // other iterators might be in limbo
   146:   SObjListMutator(SObjListMutator const &obj)             : mut(obj.mut) {}
   147:   SObjListMutator& operator=(SObjListMutator const &obj)  { mut = obj.mut;  return *this; }
   148:     // requires that 'this' and 'obj' already refer to the same 'list'
   149: 
   150:   // iterator actions
   151:   bool isDone() const                   { return mut.isDone(); }
   152:   void adv()                            { mut.adv(); }
   153:   T *data()                             { return (T*)mut.data(); }
   154:   T *&dataRef()                         { return (T*&)mut.dataRef(); }
   155: 
   156:   // insertion
   157:   void insertBefore(T *item)            { mut.insertBefore((void*)item); }
   158:     // 'item' becomes the new 'current', and the current 'current' is
   159:     // pushed forward (so the next adv() will make it current again)
   160: 
   161:   void insertAfter(T *item)             { mut.insertAfter((void*)item); }
   162:     // 'item' becomes what we reach with the next adv();
   163:     // isDone() must be false
   164: 
   165:   void append(T *item)                  { mut.append((void*)item); }
   166:     // only valid while isDone() is true, it inserts 'item' at the end of
   167:     // the list, and advances such that isDone() remains true; equivalent
   168:     // to { xassert(isDone()); insertBefore(item); adv(); }
   169: 
   170:   // removal
   171:   T *remove()                           { return (T*)mut.remove(); }
   172:     // 'current' is removed from the list and returned, and whatever was
   173:     // next becomes the new 'current'
   174: 
   175:   // debugging
   176:   void selfCheck() const                { mut.selfCheck(); }
   177: };
   178: 
   179: #define SMUTATE_EACH_OBJLIST(T, list, iter) \
   180:   for(SObjListMutator< T > iter(list); !iter.isDone(); iter.adv())
   181: 
   182: 
   183: // for traversing the list without modifying it (neither nodes nor structure)
   184: // NOTE: no list-modification fns should be called on 'list' while this
   185: //       iterator exists
   186: template <class T>
   187: class SObjListIter {
   188: protected:
   189:   VoidListIter iter;      // underlying iterator
   190: 
   191: public:
   192:   SObjListIter(SObjList<T> const &list) : iter(list.list) {}
   193:   SObjListIter(SObjList<T> const &list, int pos) : iter(list.list, pos) {}
   194:   ~SObjListIter()                       {}
   195: 
   196:   void reset(SObjList<T> const &list)   { iter.reset(list.list); }
   197: 
   198:   // iterator copying; generally safe
   199:   SObjListIter(SObjListIter const &obj)             : iter(obj.iter) {}
   200:   SObjListIter& operator=(SObjListIter const &obj)  { iter = obj.iter;  return *this; }
   201: 
   202:   // but copying from a mutator is less safe; see above
   203:   SObjListIter(SObjListMutator<T> &obj)             : iter(obj.mut) {}
   204: 
   205:   // iterator actions
   206:   bool isDone() const                   { return iter.isDone(); }
   207:   void adv()                            { iter.adv(); }
   208:   T const *data() const                 { return (T const*)iter.data(); }
   209: };
   210: 
   211: #define SFOREACH_OBJLIST(T, list, iter) \
   212:   for(SObjListIter< T > iter(list); !iter.isDone(); iter.adv())
   213: 
   214: 
   215: // intermediate to the above two, this allows modification of the
   216: // objects stored on the list, but not the identity or order of
   217: // the objects in the list
   218: template <class T>
   219: class SObjListIterNC {
   220: protected:
   221:   VoidListIter iter;      // underlying iterator
   222: 
   223: public:
   224:   SObjListIterNC(SObjList<T> &list) : iter(list.list) {}
   225:   SObjListIterNC(SObjList<T> &list, int pos) : iter(list.list, pos) {}
   226:   ~SObjListIterNC()                     {}
   227: 
   228:   void reset(SObjList<T> &list)         { iter.reset(list.list); }
   229: 
   230:   // iterator copying; generally safe
   231:   SObjListIterNC(SObjListIterNC const &obj)             : iter(obj.iter) {}
   232:   SObjListIterNC& operator=(SObjListIterNC const &obj)  { iter = obj.iter;  return *this; }
   233: 
   234:   // but copying from a mutator is less safe; see above
   235:   SObjListIterNC(SObjListMutator<T> &obj)               : iter(obj.mut) {}
   236: 
   237:   // iterator actions
   238:   bool isDone() const                   { return iter.isDone(); }
   239:   void adv()                            { iter.adv(); }
   240:   T *data() const                       { return (T*)iter.data(); }
   241: };
   242: 
   243: #define SFOREACH_OBJLIST_NC(T, list, iter) \
   244:   for(SObjListIterNC< T > iter(list); !iter.isDone(); iter.adv())
   245: 
   246: 
   247: // iterate over the combined elements of two or more lists
   248: template <class T>
   249: class SObjListMultiIter {
   250: private:
   251:   // all the lists
   252:   SObjList<T> **lists;               // serf array of serf list pointers
   253:   int numLists;                      // length of this array
   254: 
   255:   // current element
   256:   int curList;                       // which list we're working on
   257:   SObjListIter<T> iter;              // current element of that list
   258: 
   259:   // invariant:
   260:   //   either curList==numLists, or
   261:   //   iter is not 'done'
   262: 
   263: public:
   264:   SObjListMultiIter(SObjList<T> **L, int n)
   265:     : lists(L),
   266:       numLists(n),
   267:       curList(0),
   268:       iter(*(lists[0]))
   269:   {
   270:     xassert(n > 0);
   271:     normalize();
   272:   }
   273: 
   274:   // advance the iterator to the next element of the next non-empty list;
   275:   // establishes invariant above
   276:   void normalize();
   277: 
   278:   bool isDone() const {
   279:     return curList == numLists;
   280:   }
   281: 
   282:   T const *data() const {
   283:     return iter.data();
   284:   }
   285: 
   286:   void adv() {
   287:     iter.adv();
   288:     normalize();
   289:   }
   290: };
   291: 
   292: // this was originally inline, but that was causing some strange
   293: // problems (compiler bug?)
   294: template <class T>
   295: void SObjListMultiIter<T>::normalize()
   296: {
   297:   while (iter.isDone() && curList < numLists) {
   298:     curList++;
   299:     if (curList < numLists) {
   300:       iter.reset(*(lists[curList]));
   301:     }
   302:   }
   303: }
   304: 
   305: 
   306: #endif // SOBJLIST_H
End C section to elk/sm_sobjlist.h[1]
Start C section to elk/sm_sobjset.h[1 /1 ]
     1: #line 4613 "./lpsrc/sm.pak"
     2: // sobjset.h            see license.txt for copyright and terms of use
     3: // non-owning set of objects (identified by address),
     4: // implemented with a hashtable
     5: 
     6: #ifndef SOBJSET_H
     7: #define SOBJSET_H
     8: 
     9: #include "sm_hashtbl.h"
    10: 
    11: template <class T> class SObjSetIter;
    12: 
    13: 
    14: // experiment: to try to support const-polymorphism,
    15: // I expect T to either be Foo* or Foo const *
    16: 
    17: template <class T>
    18: class SObjSet : private HashTable {
    19:   friend class SObjSetIter<T>;
    20: 
    21: public:
    22:   SObjSet()
    23:     : HashTable(identityKeyFn, lcprngHashFn, pointerEqualKeyFn) {}
    24: 
    25:   // # of distinct elements in the set
    26:   int size() const               { return HashTable::getNumEntries(); }
    27: 
    28:   // true if 'elt' is in the set
    29:   bool contains(T elt) const     { return !!HashTable::get((void const*)elt); }
    30: 
    31:   // add 'elt' to the set; if it is already in, this has no effect
    32:   void add(T elt)                { if (!contains(elt)) { HashTable::add((void const*)elt, (void*)elt); } }
    33: 
    34:   // remove 'elt' from the set; if it's not there, this has no effect
    35:   void remove(T elt)             { if (contains(elt)) { HashTable::remove((void const*)elt); } }
    36: 
    37:   // remove all elements
    38:   void empty()                   { HashTable::empty(); }
    39: 
    40:   // debug check which throws an exception if there's a problem
    41:   void selfCheck()               { HashTable::selfCheck(); }
    42: };
    43: 
    44: 
    45: template <class T>
    46: class SObjSetIter : private HashTableIter {
    47: public:
    48:   SObjSetIter(SObjSet<T> &table)
    49:     : HashTableIter(table) {}    // I'm a friend, I can see it's a HashTable inside
    50: 
    51:   bool isDone() const          { return HashTableIter::isDone(); }
    52:   void adv()                   { return HashTableIter::adv(); }
    53:   T data() const               { return (T)HashTableIter::data(); }
    54: };
    55: 
    56: 
    57: #endif // SOBJSET_H
End C section to elk/sm_sobjset.h[1]
Start C section to elk/sm_sobjstack.h[1 /1 ]
     1: #line 4671 "./lpsrc/sm.pak"
     2: // sobjstack.h            see license.txt for copyright and terms of use
     3: // stack of objects, *not* owned by the stack
     4: 
     5: #ifndef SOBJSTACK_H
     6: #define SOBJSTACK_H
     7: 
     8: #include "sm_sobjlist.h"
     9: 
    10: template <class T>
    11: class SObjStack {
    12: public:       // data
    13:   // will implement the stack as a list, with prepend and removeAt(0)
    14:   SObjList<T> list;
    15: 
    16: public:       // funcs
    17:   SObjStack()                           : list() {}
    18:   ~SObjStack()                          {}
    19: 
    20:   int count() const                     { return list.count(); }
    21:   bool isEmpty() const                  { return list.isEmpty(); }
    22:   bool isNotEmpty() const               { return list.isNotEmpty(); }
    23: 
    24:   T const *topC() const                 { return list.firstC(); }
    25:   T *top()                              { return list.first(); }
    26: 
    27:   T *pop()                              { return list.removeAt(0); }
    28:   void push(T *item)                    { list.prepend(item); }
    29: 
    30:   bool contains(T const *item) const    { return list.contains((void*)item); }
    31: };
    32: 
    33: 
    34: // utility class for maintaining a first-class sub-stack of the AST
    35: // stack isomorphic to the stackframe stack; Note that the fact that
    36: // nothing happens if 'obj' is NULL is a feature: sometimes you can't
    37: // map the need to call this class completely onto the control flow,
    38: // and so some dataflow is involved; since the dtor for this class is
    39: // used as a kind of finally statement, we can't nest its construction
    40: // in an 'if' statement!  Instead pass in NULL if you want a no-op
    41: // effect.
    42: template<class T>
    43: class StackMaintainer {
    44:   SObjStack<T> &s;
    45:   T *obj;
    46: 
    47:   StackMaintainer(StackMaintainer&); // forbid copying
    48: 
    49: public:
    50:   explicit StackMaintainer(SObjStack<T> &s0, T *obj0)
    51:     : s(s0)
    52:     , obj(obj0)
    53:   {
    54:     if (obj) {
    55:       s.push(obj);
    56:     }
    57:   }
    58: 
    59:   ~StackMaintainer() {
    60:     if (obj) {
    61:       T *obj0 = s.pop();
    62:       xassert(obj0 == obj);
    63:     }
    64:   }
    65: };
    66: 
    67: 
    68: #endif // SOBJSTACK_H
End C section to elk/sm_sobjstack.h[1]
Start C section to elk/sm_srcloc.h[1 /1 ]
     1: #line 4740 "./lpsrc/sm.pak"
     2: // srcloc.h            see license.txt for copyright and terms of use
     3: // source location information, efficiently represented as one word
     4: 
     5: // The fundamental assumption in this module is that source location
     6: // information is frequently created, stored and passed around, but
     7: // infrequently decoded into human-readable form.  Therefore the
     8: // module uses a single word to store the information, and appeals
     9: // to several index structures when decoding is necessary.
    10: //
    11: // Since decoding, when it happens, also usually has high locality,
    12: // the data structures include caches to make accesses to nearby
    13: // locations fast.
    14: //
    15: // No attempt is made to fold creation of SourceLocs into other
    16: // file-processing activities, such as traditional lexical analysis.
    17: // The complexity of doing that would be substantial, with little gain
    18: // in efficiency, due to the large buffer caches in modern OSes.  The
    19: // main drawback is the inability to work with non-seekable inputs
    20: // (like pipes) because we consume the whole input when its line
    21: // counts are computed.
    22: 
    23: #ifndef SRCLOC_H
    24: #define SRCLOC_H
    25: 
    26: #include "sm_str.h"
    27: #include "sm_objlist.h"
    28: #include "flx_elk_config.hpp"
    29: 
    30: class HashLineMap;    // hashline.h
    31: 
    32: 
    33: // This is a source location.  It's interpreted as an integer
    34: // specifying the byte offset within a hypothetical file created by
    35: // concatenating all the sources together.  Its type is 'enum' so I
    36: // can overload functions to accept SourceLoc without confusion.
    37: // I assume the compiler will use a machine word for this (and check
    38: // that assumption in the .cc file).
    39: //
    40: // I would love to be able to annotate this so that the C++ compiler
    41: // would not allow variables of this type to be created
    42: // uninitialized.. that's the one drawback of calling this an 'enum'
    43: // instead of a 'class': I don't get to write a constructor.
    44: enum SourceLoc {
    45:   // entity is defined within the translator's initialization code
    46:   SL_INIT=-1,
    47: 
    48:   // location is unknown for some reason
    49:   SL_UNKNOWN=0
    50: };
    51: 
    52: 
    53: // This class manages all the data associated with creating and
    54: // interpreting SourceLocs.  It's expected to be a singleton in the
    55: // program, though within this module that assumption is confined to
    56: // the 'toString' function at the end.
    57: class ELK_EXTERN SourceLocManager {
    58: private:     // types
    59:   // a triple which identifies a line boundary in a file (it's
    60:   // implicit which file it is) with respect to all of the relevant
    61:   // spaces
    62:   class Marker {
    63:   public:
    64:     // character offset, starting with 0
    65:     int charOffset;
    66: 
    67:     // line offset, starting with 1
    68:     int lineOffset;
    69: 
    70:     // offset into the 'lineLengths' array; this is not simply
    71:     // lineOffset-1 because of the possible presence of lines with
    72:     // length longer than 254 chars
    73:     int arrayOffset;
    74: 
    75:   public:
    76:     Marker() {}      // for creation in arrays
    77:     Marker(int c, int L, int a)
    78:       : charOffset(c), lineOffset(L), arrayOffset(a) {}
    79:     Marker(Marker const &obj)
    80:       : DMEMB(charOffset), DMEMB(lineOffset), DMEMB(arrayOffset) {}
    81:   };
    82: 
    83: public:      // types
    84:   // describes a file we know about
    85:   class File {
    86:   public:    // data
    87:     // file name; we consider two files to be the same if and only
    88:     // if their names are equal, i.e. there is no checking done to
    89:     // see if their names happen to be aliases in the filesystem
    90:     sm_string name;
    91: 
    92:     // start offset in the SourceLoc space
    93:     SourceLoc startLoc;
    94: 
    95:     // number of chars in the file
    96:     int numChars;
    97: 
    98:     // number of lines in the file
    99:     int numLines;
   100: 
   101:     // average number of chars per line; this is used for estimating
   102:     // whether the index should be consulted for some lookups (and
   103:     // it's stored instead of computed to save a division)
   104:     int avgCharsPerLine;
   105: 
   106:     // known #line directives for this file; NULL if none are known
   107:     HashLineMap *hashLines;          // (nullable owner)
   108: 
   109:   private:   // data
   110:     // an array of line lengths; to handle lines longer than 255
   111:     // chars, we use runs of '\xFF' chars to (in unary) encode
   112:     // multiples of 254 (one less than 255) chars, plus the final
   113:     // short count to give the total length
   114:     unsigned char *lineLengths;      // (owner)
   115: 
   116:     // # of elements in 'lineLengths'
   117:     int lineLengthsSize;
   118: 
   119:     // this marker and offset can name an arbitrary point
   120:     // in the array, including those that are not at the
   121:     // start of a line; we move this around when searching
   122:     // within the array
   123:     Marker marker;
   124:     int markerCol;      // 1-based column; it's usually 1
   125: 
   126:     // an index built on top of 'lineLengths' for faster random access
   127:     Marker *index;                   // (owner)
   128: 
   129:     // # of elements in 'index'
   130:     int indexSize;
   131: 
   132:   private:   // funcs
   133:     File(File&);                     // disallowed
   134:     void resetMarker();
   135:     void advanceMarker();
   136: 
   137:   public:    // funcs
   138:     // this builds both the array and the index
   139:     File(char const *name, SourceLoc startLoc);
   140:     ~File();
   141: 
   142:     // line number to character offset
   143:     int lineToChar(int lineNum);
   144: 
   145:     // line/col to offset, with truncation if col exceeds line length
   146:     int lineColToChar(int lineNum, int col);
   147: 
   148:     // char offset to line/col
   149:     void charToLineCol(int offset, int &line, int &col);
   150: 
   151:     // true if this file contains the specified location
   152:     bool hasLoc(SourceLoc sl) const
   153:       { return toInt(startLoc) <= sl &&
   154:                                   sl <= toInt(startLoc) + numChars; }
   155: 
   156:     // call this time each time a #line directive is encountered;
   157:     // same semantics as HashLineMap::addHashLine
   158:     void addHashLine(int ppLine, int origLine, char const *origFname);
   159:     void doneAdding();
   160:   };
   161: 
   162:   // this is used for SourceLocs where the file isn't reliably
   163:   // available, yet we'd like to be able to store some location
   164:   // information anyway; the queries below just return the static
   165:   // information stored, and incremental update is impossible
   166:   class StaticLoc {
   167:   public:
   168:     sm_string name;      // file name
   169:     int offset;       // char offset
   170:     int line, col;    // line,col
   171: 
   172:   public:
   173:     StaticLoc(char const *n, int o, int L, int c)
   174:       : name(n), offset(o), line(L), col(c) {}
   175:     StaticLoc(StaticLoc const &obj)
   176:       : DMEMB(name), DMEMB(offset), DMEMB(line), DMEMB(col) {}
   177:     ~StaticLoc();
   178:   };
   179: 
   180: private:     // data
   181:   // list of files; it would be possible to use a data structure
   182:   // that is faster to search, but the cache ought to exploit
   183:   // query locality to the extent that it doesn't matter
   184:   ObjList<File> files;
   185: 
   186:   // most-recently accessed File; this is a cache
   187:   File *recent;                      // (serf)
   188: 
   189:   // list of StaticLocs; any SourceLoc less than 0 is interpreted
   190:   // as an index into this list
   191:   ObjList<StaticLoc> statics;
   192: 
   193:   // next source location to assign
   194:   SourceLoc nextLoc;
   195: 
   196:   // next static (negative) location
   197:   SourceLoc nextStaticLoc;
   198: 
   199: public:      // data
   200:   // number of static locations at which we print a warning message;
   201:   // defaults to 100
   202:   int maxStaticLocs;
   203: 
   204:   // when true, we automatically consult the #line maps when decoding;
   205:   // defaults to true; NOTE: when this is true, encode and decode are
   206:   // not necessarily inverses of each other
   207:   bool useHashLines;
   208: 
   209:   // count the # of times we had to truncate a char offset because
   210:   // the #line map pointed at a line shorter than the column number
   211:   // we expected to use; this is initially 0; calling code can use
   212:   // this to tell if the offset information across a given call or
   213:   // sequence of calls is perfect or truncated
   214:   static int shortLineCount;
   215: 
   216: private:     // funcs
   217:   // let File know about these functions
   218:   friend class SourceLocManager::File;
   219: 
   220:   static SourceLoc toLoc(int L) {
   221:     SourceLoc ret = (SourceLoc)L;
   222: 
   223:     // in debug mode, we verify that SourceLoc is wide enough
   224:     // to encode this integer
   225:     xassertdb(toInt(ret) == L);
   226: 
   227:     return ret;
   228:   }
   229:   static int toInt(SourceLoc loc) { return (int)loc; }
   230: 
   231:   File *findFile(char const *name);
   232:   File *getFile(char const *name);
   233: 
   234:   public:                       // dsw: I need this so I have an object to annotate with a VoidVoidDict
   235:   File *findFileWithLoc(SourceLoc loc);
   236:   private:
   237:   StaticLoc const *getStatic(SourceLoc loc);
   238: 
   239: public:      // funcs
   240:   SourceLocManager();
   241:   ~SourceLocManager();
   242: 
   243:   // origins:
   244:   //   character offsets start at 0
   245:   //   lines start at 1
   246:   //   columns start at 1
   247: 
   248:   // encode from scratch
   249:   SourceLoc encodeOffset(char const *filename, int charOffset);
   250:   SourceLoc encodeBegin(char const *filename)
   251:     { return encodeOffset(filename, 0 /*offset*/); }
   252:   SourceLoc encodeLineCol(char const *filename, int line, int col);
   253: 
   254:   // some care is required with 'encodeStatic', since each call makes
   255:   // a new location with a new entry in the static array to back it
   256:   // up, so the caller should ensure a given static location is not
   257:   // encoded more than once, if possible
   258:   SourceLoc encodeStatic(StaticLoc const &obj);
   259:   SourceLoc encodeStatic(char const *fname, int offset, int line, int col)
   260:     { return encodeStatic(StaticLoc(fname, offset, line, col)); }
   261:   static bool isStatic(SourceLoc loc) { return toInt(loc) <= 0; }
   262: 
   263:   // encode incremental; these are the methods we expect are called
   264:   // the most frequently; this interface is supposed to allow an
   265:   // implementation which uses explicit line/col, even though that
   266:   // is not what is used here
   267:   static SourceLoc advCol(SourceLoc base, int colOffset)
   268:     { xassert(!isStatic(base)); return toLoc(toInt(base) + colOffset); }
   269:   static SourceLoc advLine(SourceLoc base)     // from end of line to beginning of next
   270:     { xassert(!isStatic(base)); return toLoc(toInt(base) + 1); }
   271:   static SourceLoc advText(SourceLoc base, char const * /*text*/, int textLen)
   272:     { xassert(!isStatic(base)); return toLoc(toInt(base) + textLen); }
   273: 
   274:   // decode
   275:   void decodeOffset(SourceLoc loc, char const *&filename, int &charOffset);
   276:   void decodeLineCol(SourceLoc loc, char const *&filename, int &line, int &col);
   277: 
   278:   // more specialized decode
   279:   char const *getFile(SourceLoc loc);
   280:   int getOffset(SourceLoc loc);
   281:   int getLine(SourceLoc loc);
   282:   int getCol(SourceLoc loc);
   283: 
   284:   // get access to the File itself, for adding #line directives
   285:   File *getInternalFile(char const *fname)
   286:     { return getFile(fname); }
   287: 
   288:   // render as sm_string in "file:line:col" format
   289:   sm_string getString(SourceLoc loc);
   290: 
   291:   // "line:col" format
   292:   sm_string getLCString(SourceLoc loc);
   293: };
   294: 
   295: 
   296: // singleton pointer, set automatically by the constructor
   297: extern SourceLocManager *sourceLocManager;
   298: 
   299: // dsw: So that gdb can find it please DO NOT inline this; also the
   300: // unique public name is intensional: I don't want gdb doing
   301: // overloading and sometimes getting it wrong, which it does
   302: ELK_EXTERN sm_string locToStr(SourceLoc sl);
   303: 
   304: inline sm_string toString(SourceLoc sl)
   305:   { return locToStr(sl); }
   306: 
   307: inline sm_stringBuilder& operator<< (sm_stringBuilder &sb, SourceLoc sl)
   308:   { return sb << toString(sl); }
   309: 
   310: inline sm_string toLCString(SourceLoc sl)
   311:   { return sourceLocManager->getLCString(sl); }
   312: 
   313: 
   314: // macro for obtaining a source location that points at the
   315: // point in the source code where this macro is invoked
   316: #define HERE_SOURCELOC \
   317:   (sourceLocManager->encodeStatic(__FILE__, 0, __LINE__, 1))
   318: 
   319: 
   320: // it's silly to demand mention of 'SourceLocManager' just to update
   321: // the locations, esp. since SourceLoc is its own type and therefore
   322: // overloading will avoid any possible collisions
   323: inline SourceLoc advCol(SourceLoc base, int colOffset)
   324:   { return SourceLocManager::advCol(base, colOffset); }
   325: inline SourceLoc advLine(SourceLoc base)
   326:   { return SourceLocManager::advLine(base); }
   327: inline SourceLoc advText(SourceLoc base, char const *text, int textLen)
   328:   { return SourceLocManager::advText(base, text, textLen); }
   329: 
   330: 
   331: #endif // SRCLOC_H
End C section to elk/sm_srcloc.h[1]
Start C section to elk/sm_strdict.h[1 /1 ]
     1: #line 5072 "./lpsrc/sm.pak"
     2: // strdict.h            see license.txt for copyright and terms of use
     3: // sm_string dictionary
     4: // (c) Scott McPeak, 2000
     5: 
     6: // entire module is case sensitive
     7: 
     8: #ifndef __STRDICT_H
     9: #define __STRDICT_H
    10: 
    11: #include <iostream>   // std::ostream
    12: #include "sm_str.h"
    13: #include "sm_macros.h"
    14: #include "sm_xassert.h"
    15: #include "sm_typ.h"
    16: 
    17: class StringDict {
    18: private:    // types
    19:   class Node {
    20:   public:
    21:     Node *next;
    22:     sm_string key, value;
    23: 
    24:   public:
    25:     Node(char const *k, char const *v, Node *n = NULL)
    26:       : next(n), key(k), value(v) {}
    27:     ~Node() {}
    28:   };
    29: 
    30: public:     // types
    31:   // Note: some care must be taken when dealing with Iters, because
    32:   //       they can be invalidated when held across modifications to
    33:   //       structure of the underlying dictionary
    34:   class Iter {
    35:   private:
    36:     Node *current;
    37: 
    38:   public:
    39:     Iter(Node *n) : current(n) {}
    40:     Iter(StringDict &dict) { operator=(dict.getIter()); }
    41:     Iter(Iter const &obj) : DMEMB(current) {}
    42:     Iter& operator= (Iter const &obj) { CMEMB(current); return *this; }
    43: 
    44:     bool isDone() const { return current == NULL; }
    45:     Iter& next() { xassert(current); current = current->next; return *this; }
    46:       // 'next' returns a value primarily to allow use in for-loop comma exprs
    47: 
    48:     sm_string& key() const { return current->key; }
    49:     sm_string& value() const { return current->value; }
    50:   };
    51:   friend class Iter;
    52: 
    53:   // iterator that can't modify the dictionary entries
    54:   class IterC : protected Iter {
    55:   public:
    56:     IterC(Node const *n) : Iter(const_cast<Node*>(n)) {}
    57:     IterC(StringDict const &dict) : Iter(const_cast<StringDict&>(dict)) {}
    58:     IterC(IterC const &obj) : Iter(obj) {}
    59:     IterC& operator= (IterC const &obj) { Iter::operator=(obj); return *this; }
    60: 
    61:     // some operations can be made available unchanged
    62:     Iter::isDone;
    63:     Iter::next;
    64: 
    65:     // others must be const-ified
    66:     sm_string const &key() const { return Iter::key(); }
    67:     sm_string const &value() const { return Iter::value(); }
    68:   };
    69: 
    70: private:    // data
    71:   Node *top;             // first list node (possibly NULL)
    72: 
    73: protected:  // funcs
    74:   void selfCheck() const;      // throw exception if invariants violated
    75: 
    76:   void verifySorted() const;   // throw exception if list isn't sorted
    77: 
    78:   void /*mutable*/ sort();     // arrange nodes in alphabetically sorted order
    79:     // (mutable because this isn't supposed to be visible from the outside)
    80: 
    81:   // invariants:
    82:   //   list is well-formed structurally
    83: 
    84: public:
    85:   StringDict();          // initializes to empty dictionary
    86:   StringDict(StringDict const &obj);
    87:   ~StringDict();
    88: 
    89:   StringDict& operator= (StringDict const &obj);
    90: 
    91:   bool operator== (StringDict const &obj) const;
    92:   NOTEQUAL_OPERATOR(StringDict)
    93: 
    94:   // ------- selectors ---------
    95:   int size() const;
    96:     // retrieve # of mappings
    97: 
    98:   bool isEmpty() const;
    99:     // returns true if size() is 0
   100: 
   101:   bool isNotEmpty() const
   102:     { return !isEmpty(); }
   103: 
   104:   bool query(char const *key, sm_string &value) const;
   105:     // if 'key' is mapped to a value, put it into 'value' and return true;
   106:     // otherwise, return false
   107: 
   108:   sm_string queryf(char const *key) const;
   109:     // return the value corresponding to 'key', or throw an exception of it's
   110:     // not mapped
   111: 
   112:   bool isMapped(char const *key) const;
   113:     // return true if 'key' is mapped to a value
   114: 
   115:   // -------- mutators -----------
   116:   void add(char const *key, char const *value);
   117:     // add a mapping from 'key' to 'value'; 'key' must initially be unmapped
   118: 
   119:   void modify(char const *key, char const *newValue);
   120:     // change the existing value for 'key', which must exist, to 'newValue'
   121: 
   122:   void remove(char const *key);
   123:     // remove the mapping from 'key', which must exist
   124: 
   125:   void empty();
   126:     // remove all mappings
   127: 
   128:   // --------- iters -------------
   129:   Iter getIter();
   130:     // retrieve an iterator (the iterator remains valid only as long as
   131:     // the structure of the dictionary does not get modified);
   132:     // values will be iterated in *alphabetical* order
   133: 
   134:   IterC getIterC() const;
   135:     // retrieve a const iterator
   136: 
   137:   Iter find(char const *key);
   138:     // return an iterator pointing to 'key', or an iterator
   139:     // that isDone() if 'key' isn't mapped
   140: 
   141:   // ------------ misc --------------
   142:   INSERT_OSTREAM(StringDict)
   143:   sm_string toString() const;
   144: };
   145: 
   146: #endif // __STRDICT_H
End C section to elk/sm_strdict.h[1]
Start C section to elk/sm_str.h[1 /1 ]
     1: #line 5219 "./lpsrc/sm.pak"
     2: // str.h            see license.txt for copyright and terms of use
     3: // a sm_string class
     4: // the representation uses just one char*, so that a smart compiler
     5: //   can pass the entire object as a single word
     6: // Scott McPeak, 1995-2000  This file is public domain.
     7: 
     8: #ifndef STR_H
     9: #define STR_H
    10: 
    11: // this should eventually be put someplace more general...
    12: #ifndef va_copy
    13:   #ifdef __va_copy
    14:     #define va_copy(a,b) __va_copy(a,b)
    15:   #else
    16:     #define va_copy(a,b) (a)=(b)
    17:   #endif
    18: #endif
    19: // MOVE THE ABOVE TO PLACE MORE GENERAL
    20: 
    21: #include "flx_elk_config.hpp"
    22: #include "sm_typ.h"
    23: #include <iostream>    // std::istream, std::ostream
    24: #include <stdarg.h>      // va_list
    25: 
    26: class Flatten;           // flatten.h
    27: class ELK_EXTERN sm_string;
    28: 
    29: class ELK_EXTERN sm_string {
    30: protected:     // data
    31:   // 10/12/00: switching to never letting s be NULL
    32:   char *s;                             // sm_string contents; never NULL
    33:   static char * const empty;           // a global ""; should never be modified
    34: 
    35: protected:     // funcs
    36:   void dup(char const *source);        // copies, doesn't dealloc first
    37:   void kill();                         // dealloc if str != 0
    38: 
    39: public:        // funcs
    40:   sm_string(sm_string const &src) { dup(src.s); }
    41:   sm_string(char const *src) { dup(src); }
    42:   sm_string(char const *src, int length);    // grab a subsm_string
    43:   sm_string(int length) { s=empty; setlength(length); }
    44:   sm_string() { s=empty; }
    45: 
    46:   ~sm_string() { kill(); }
    47: 
    48:   sm_string(Flatten&);
    49:   void xfer(Flatten &flat);
    50: 
    51:   // simple queries
    52:   int length() const;           // returns number of non-null chars in the sm_string; length of "" is 0
    53:   bool isempty() const { return s[0]==0; }
    54:   bool contains(char c) const;
    55: 
    56:   // array-like access
    57:   char& operator[] (int i) { return s[i]; }
    58:   char operator[] (int i) const { return s[i]; }
    59: 
    60:   // subsm_string
    61:   sm_string subsm_string(int startIndex, int length) const;
    62: 
    63:   // conversions
    64:   //operator char* () { return s; }      // ambiguities...
    65:   operator char const* () const { return s; }
    66:   char *pchar() { return s; }
    67:   char const *pcharc() const { return s; }
    68: 
    69:   // assignment
    70:   sm_string& operator=(sm_string const &src)
    71:     { if (&src != this) { kill(); dup(src.s); } return *this; }
    72:   sm_string& operator=(char const *src)
    73:     { if (src != s) { kill(); dup(src); } return *this; }
    74: 
    75:   // allocate 'newlen' + 1 bytes (for null); initial contents is ""
    76:   sm_string& setlength(int newlen);
    77: 
    78:   // comparison; return value has same meaning as strcmp's return value:
    79:   //   <0   if   *this < src
    80:   //   0    if   *this == src
    81:   //   >0   if   *this > src
    82:   int compareTo(sm_string const &src) const;
    83:   int compareTo(char const *src) const;
    84:   bool equals(char const *src) const { return compareTo(src) == 0; }
    85: 
    86:   #define MAKEOP(op)                                                             \
    87:     bool operator op (sm_string const &src) const { return compareTo(src) op 0; }   \
    88:     /*bool operator op (const char *src) const { return compareTo(src) op 0; }*/ \
    89:     /* killed stuff with char* because compilers are too flaky; use compareTo */
    90:   MAKEOP(==)  MAKEOP(!=)
    91:   MAKEOP(>=)  MAKEOP(>)
    92:   MAKEOP(<=)  MAKEOP(<)
    93:   #undef MAKEOP
    94: 
    95:   // concatenation (properly handles sm_string growth)
    96:   // uses '&' instead of '+' to avoid char* coercion problems
    97:   sm_string operator& (sm_string const &tail) const;
    98:   sm_string& operator&= (sm_string const &tail);
    99: 
   100:   // input/output
   101:   friend std::istream& operator>> (std::istream &is, sm_string &obj)
   102:     { obj.readline(is); return is; }
   103:   friend std::ostream& operator<< (std::ostream &os, sm_string const &obj)
   104:     { obj.write(os); return os; }
   105: 
   106:   // note: the read* functions are currently implemented in a fairly
   107:   // inefficient manner (one char at a time)
   108: 
   109:   void readdelim(std::istream &is, char const *delim);
   110:     // read from is until any character in delim is encountered; consumes that
   111:     // character, but does not put it into the sm_string; if delim is null or
   112:     // empty, reads until EOF
   113: 
   114:   void readall(std::istream &is) { readdelim(is, NULL); }
   115:     // read all remaining chars of is into this
   116: 
   117:   void readline(std::istream &is) { readdelim(is, "\n"); }
   118:     // read a line from input stream; consumes the \n, but doesn't put it into
   119:     // the sm_string
   120: 
   121:   void write(std::ostream &os) const;
   122:     // writes all stored characters (but not '\0')
   123: 
   124:   // debugging
   125:   void selfCheck() const;
   126:     // fail an assertion if there is a problem
   127: };
   128: 
   129: 
   130: // replace() and trimWhiteSpace() have been moved to strutil.h
   131: 
   132: 
   133: // this class is specifically for appending lots of things
   134: class ELK_EXTERN sm_stringBuilder : public sm_string {
   135: protected:
   136:   enum { EXTRA_SPACE = 30 };    // extra space allocated in some situations
   137:   char *end;          // current end of the sm_string (points to the NUL character)
   138:   int size;           // amount of space (in bytes) allocated starting at 's'
   139: 
   140: protected:
   141:   void init(int initSize);
   142:   void dup(char const *src);
   143: 
   144: public:
   145:   sm_stringBuilder(int length=0);    // creates an empty sm_string
   146:   sm_stringBuilder(char const *str);
   147:   sm_stringBuilder(char const *str, int length);
   148:   sm_stringBuilder(sm_string const &str) { dup((char const*)str); }
   149:   sm_stringBuilder(sm_stringBuilder const &obj) { dup((char const*)obj); }
   150:   ~sm_stringBuilder() {}
   151: 
   152:   sm_stringBuilder& operator= (char const *src);
   153:   sm_stringBuilder& operator= (sm_string const &s) { return operator= ((char const*)s); }
   154:   sm_stringBuilder& operator= (sm_stringBuilder const &s) { return operator= ((char const*)s); }
   155: 
   156:   int length() const { return end-s; }
   157:   bool isempty() const { return length()==0; }
   158: 
   159:   sm_stringBuilder& setlength(int newlen);    // change length, forget current data
   160: 
   161:   // make sure we can store 'someLength' non-null chars; grow if necessary
   162:   void ensure(int someLength) { if (someLength >= size) { grow(someLength); } }
   163: 
   164:   // grow the sm_string's length (retaining data); make sure it can hold at least
   165:   // 'newMinLength' non-null chars
   166:   void grow(int newMinLength);
   167: 
   168:   // this can be useful if you modify the sm_string contents directly..
   169:   // it's not really the intent of this class, though
   170:   void adjustend(char* newend);
   171: 
   172:   // make the sm_string be the empty sm_string, but don't change the
   173:   // allocated space
   174:   void clear() { adjustend(s); }
   175: 
   176:   // concatenation, which is the purpose of this class
   177:   sm_stringBuilder& operator&= (char const *tail);
   178: 
   179:   // useful for appending subsm_strings or sm_strings with NUL in them
   180:   void append(char const *tail, int length);
   181: 
   182:   // append a given number of spaces; meant for contexts where we're
   183:   // building a multi-line sm_string; returns '*this'
   184:   sm_stringBuilder& indent(int amt);
   185: 
   186:   // sort of a mixture of Java compositing and C++ i/o strstream
   187:   // (need the coercion versions (like int) because otherwise gcc
   188:   // spews mountains of f-ing useless warnings)
   189:   sm_stringBuilder& operator << (char const *text) { return operator&=(text); }
   190:   sm_stringBuilder& operator << (char c);
   191:   sm_stringBuilder& operator << (unsigned char c) { return operator<<((char)c); }
   192:   sm_stringBuilder& operator << (long i);
   193:   sm_stringBuilder& operator << (unsigned long i);
   194:   sm_stringBuilder& operator << (int i) { return operator<<((long)i); }
   195:   sm_stringBuilder& operator << (unsigned i) { return operator<<((unsigned long)i); }
   196:   sm_stringBuilder& operator << (short i) { return operator<<((long)i); }
   197:   sm_stringBuilder& operator << (unsigned short i) { return operator<<((long)i); }
   198:   sm_stringBuilder& operator << (float d);
   199:   sm_stringBuilder& operator << (double d);
   200:   sm_stringBuilder& operator << (void *ptr);     // inserts address in hex
   201:   #ifdef HAVE_LONGLONG
   202:   sm_stringBuilder& operator << (long long i);
   203:   sm_stringBuilder& operator << (unsigned long long i);
   204:   #endif
   205:   #ifdef HAVE_LONGDOUBLE
   206:   sm_stringBuilder& operator << (long double d);
   207:   #endif
   208:   #ifndef LACKS_BOOL
   209:     sm_stringBuilder& operator << (bool b) { return operator<<((long)b); }
   210:   #endif // LACKS_BOOL
   211: 
   212:   // useful in places where long << expressions make it hard to
   213:   // know when arguments will be evaluated, but order does matter
   214:   typedef sm_stringBuilder& (*Manipulator)(sm_stringBuilder &sb);
   215:   sm_stringBuilder& operator<< (Manipulator manip);
   216: 
   217:   // stream readers
   218:   friend std::istream& operator>> (std::istream &is, sm_stringBuilder &sb)
   219:     { sb.readline(is); return is; }
   220:   void readall(std::istream &is) { readdelim(is, NULL); }
   221:   void readline(std::istream &is) { readdelim(is, "\n"); }
   222: 
   223:   void readdelim(std::istream &is, char const *delim);
   224: 
   225:   // an experiment: hex formatting (something I've sometimes done by resorting
   226:   // to sprintf in the past)
   227:   class Hex {
   228:   public:
   229:     unsigned long value;
   230: 
   231:     Hex(unsigned long v) : value(v) {}
   232:     Hex(Hex const &obj) : value(obj.value) {}
   233:   };
   234:   sm_stringBuilder& operator<< (Hex const &h);
   235:   #define SBHex sm_stringBuilder::Hex
   236: };
   237: 
   238: 
   239: // the real strength of this entire module: construct sm_strings in-place
   240: // using the same syntax as C++ istd::ostreams.  e.g.:
   241: //   puts(sm_stringb("x=" << x << ", y=" << y));
   242: #define sm_stringb(expr) (sm_stringBuilder() << expr)
   243: 
   244: // experimenting with dropping the () in favor of <<
   245: // (the "c" can be interpreted as "constructor", or maybe just
   246: // the successor to "b" above)
   247: #define sm_stringc sm_stringBuilder()
   248: 
   249: 
   250: // experimenting with using toString as a general method for datatypes
   251: sm_string toString(int i);
   252: sm_string toString(unsigned i);
   253: sm_string toString(char c);
   254: sm_string toString(long i);
   255: sm_string toString(char const *str);
   256: sm_string toString(float f);
   257: 
   258: 
   259: // printf-like construction of a sm_string; often very convenient, since
   260: // you can use any of the formatting characters (like %X) that your
   261: // libc's sprintf knows about
   262: sm_string sm_stringf(char const *format, ...);
   263: sm_string vsm_stringf(char const *format, va_list args);
   264: 
   265: 
   266: #endif // STR_H
End C section to elk/sm_str.h[1]
Start C section to elk/sm_strhash.h[1 /1 ]
     1: #line 5486 "./lpsrc/sm.pak"
     2: // strhash.h            see license.txt for copyright and terms of use
     3: // hash table mapping sm_strings to arbitrary pointers, where
     4: // the stored pointers can be used to derive the key, and
     5: // cannot be NULL
     6: 
     7: #ifndef STRHASH_H
     8: #define STRHASH_H
     9: 
    10: #include "sm_hashtbl.h"
    11: 
    12: class StringHash : private HashTable {
    13: public:     // types
    14:   // given a stored data pointer, retrieve the associated key
    15:   typedef char const* (*GetKeyFn)(void *data);
    16: 
    17: private:    // funcs
    18:   // disallowed
    19:   StringHash(StringHash&);
    20:   void operator=(StringHash&);
    21:   void operator==(StringHash&);
    22: 
    23: public:     // funcs
    24:   StringHash(GetKeyFn getKey);
    25:   ~StringHash();
    26: 
    27:   // utilities
    28:   static unsigned coreHash(char const *key);
    29:   static bool keyCompare(char const *key1, char const *key2);
    30: 
    31:   // return # of mapped entries
    32:   int getNumEntries() const
    33:     { return HashTable::getNumEntries(); }
    34: 
    35:   // if this key has a mapping, return it; otherwise,
    36:   // return NULL
    37:   void *get(char const *key) const
    38:     { return HashTable::get(key); }
    39: 
    40:   // add a mapping from 'key' to 'value'; there must not already
    41:   // be a mapping for this key
    42:   void add(char const *key, void *value)
    43:     { HashTable::add(key, value); }
    44: 
    45:   // remove the mapping for 'key' -- it must exist
    46:   void remove(char const *key)
    47:     { HashTable::remove(key); }
    48: 
    49:   // drop all entries
    50:   void empty()
    51:     { HashTable::empty(); }
    52: 
    53:   // check the data structure's invariants, and throw an exception
    54:   // if there is a problem
    55:   void selfCheck() const
    56:     { HashTable::selfCheck(); }
    57: };
    58: 
    59: 
    60: // type-safe template wrapper
    61: template <class T>
    62: class TStringHash : public StringHash {
    63: public:      // types
    64:   typedef char const* (*GetKeyFn)(T *data);
    65: 
    66: public:
    67:   TStringHash(GetKeyFn fn)            : StringHash((StringHash::GetKeyFn)fn) {}
    68:   ~TStringHash()                      {}
    69: 
    70:   int getNumEntries() const           { return StringHash::getNumEntries(); }
    71:   T *get(char const *key) const       { return (T*)StringHash::get(key); }
    72:   void add(char const *key, T *value) { StringHash::add(key, (void*)value); }
    73:   void remove(char const *key)        { StringHash::remove(key); }
    74:   void empty()                        { StringHash::empty(); }
    75: };
    76: 
    77: 
    78: #endif // STRHASH_H
End C section to elk/sm_strhash.h[1]
Start C section to elk/sm_stringset.h[1 /1 ]
     1: #line 5565 "./lpsrc/sm.pak"
     2: // sm_stringset.h            see license.txt for copyright and terms of use
     3: // set of character sm_strings
     4: 
     5: #ifndef STRINGSET_H
     6: #define STRINGSET_H
     7: 
     8: #include "sm_strsobjdict.h"
     9: 
    10: class StringSet {
    11: private:     // data
    12:   // represent using a dictionary of pointers to nothing
    13:   StringSObjDict<int> elts;
    14: 
    15: public:      // funcs
    16:   StringSet() : elts() {}
    17:   ~StringSet();
    18: 
    19:   // # elts in the set
    20:   int size() const                        { return elts.size(); }
    21: 
    22:   bool isEmpty() const                    { return elts.isEmpty(); }
    23:   bool isNotEmpty() const                 { return elts.isNotEmpty(); }
    24: 
    25:   // true if elt is in the set
    26:   bool contains(char const *elt) const    { return elts.isMapped(elt); }
    27: 
    28:   // add elt to the set; ok if it's already there
    29:   void add(char const *elt);
    30: 
    31:   // remove elt from the set; ok if it's not there now
    32:   void remove(char const *elt);
    33: 
    34:   // empty the set
    35:   void empty()                            { elts.empty(); }
    36: };
    37: 
    38: #endif // STRINGSET_H
End C section to elk/sm_stringset.h[1]
Start C section to elk/sm_strobjdict.h[1 /1 ]
     1: #line 5604 "./lpsrc/sm.pak"
     2: // strobjdict.h            see license.txt for copyright and terms of use
     3: // dictionary of objects, indexed by sm_string (case-sensitive)
     4: // (c) Scott McPeak, 2000
     5: 
     6: #ifndef __STROBJDICT_H
     7: #define __STROBJDICT_H
     8: 
     9: #include "sm_svdict.h"
    10: 
    11: 
    12: // the dictionary object is considered to own all of the things
    13: // contained, so constness means constness of the contained objects
    14: // as well as the mapping from sm_strings to them
    15: 
    16: template <class T>
    17: class StringObjDict {
    18: public:     // types
    19:   // 'foreach' iterator functions
    20:   typedef bool (*ForeachCFn)(sm_string const &key, T const *value, void *extra);
    21:   typedef bool (*ForeachFn)(sm_string const &key, T * /*serf*/ value, void *extra);
    22: 
    23:   // external iterator
    24:   class Iter {
    25:   private:
    26:     StringVoidDict::IterC iter;
    27: 
    28:   public:
    29:     Iter(StringObjDict const &dict) : iter(dict.dict) {}
    30:     Iter(Iter const &obj) : DMEMB(iter) {}
    31:     Iter& operator= (Iter const &obj) { CMEMB(iter); return *this; }
    32: 
    33:     bool isDone() const { return iter.isDone(); }
    34:     Iter& next() { iter.next(); return *this; }
    35: 
    36:     sm_string const &key() const { return iter.key(); }
    37:     T const *&value() const { return (T const *&)iter.value(); }
    38:   };
    39:   friend class Iter;
    40: 
    41: private:    // data
    42:   // underlying dictionary functionality
    43:   StringVoidDict dict;
    44: 
    45: private:    // funcs
    46:   // disallowed
    47:   StringObjDict(StringObjDict const &obj);
    48:   StringObjDict& operator= (StringObjDict const &obj);
    49:   bool operator== (StringObjDict const &obj) const;
    50: 
    51: public:     // funcs
    52:   StringObjDict() : dict() {}
    53:   ~StringObjDict() { empty(); }
    54: 
    55:   // due to similarity with StringVoidDict, see svdict.h for
    56:   // details on these functions' interfaces
    57: 
    58:   // ------- selectors ---------
    59:   int size() const                                     { return dict.size(); }
    60: 
    61:   bool isEmpty() const                                 { return dict.isEmpty(); }
    62:   bool isNotEmpty() const                              { return !isEmpty(); }
    63: 
    64:   bool queryC(char const *key, T const *&value) const  { return dict.query(key, (void*&)value); }
    65:   bool query(char const *key, T *&value)               { return queryC(key, (T const*&)value); }
    66: 
    67:   T const *queryfC(char const *key) const              { return (T const *)dict.queryf(key); }
    68:   T * /*serf*/ queryf(char const *key)                 { return (T*)dict.queryf(key); }
    69:   T * /*serf*/ queryif(char const *key)                { return (T*)dict.queryif(key); }
    70: 
    71:   bool isMapped(char const *key) const                 { return dict.isMapped(key); }
    72: 
    73:   // -------- mutators -----------
    74:   void add(char const *key, T *value)                  { dict.add(key, value); }
    75: 
    76:   T * /*owner*/ remove(char const *key)                { return (T*)dict.remove(key); }
    77:   void deleteAt(char const *key)                       { deleteObject(remove(key)); }
    78: 
    79:   void empty()               { dict.emptyAndDel((StringVoidDict::DelFn)deleteObject); }
    80: 
    81:   // --------- iters -------------
    82:   void foreachC(ForeachCFn func, void *extra=NULL) const
    83:     { dict.foreach((StringVoidDict::ForeachFn)func, extra); }
    84:   void foreach(ForeachFn func, void *extra=NULL)
    85:     { dict.foreach((StringVoidDict::ForeachFn)func, extra); }
    86: 
    87:   // ------------ misc --------------
    88:   static void deleteObject(T *obj);
    89: };
    90: 
    91: 
    92: template <class T>
    93: STATICDEF void StringObjDict<T>::deleteObject(T *obj)
    94: {
    95:   delete obj;
    96: }
    97: 
    98: #endif // __STROBJDICT_H
End C section to elk/sm_strobjdict.h[1]
Start C section to elk/sm_strsobjdict.h[1 /1 ]
     1: #line 5703 "./lpsrc/sm.pak"
     2: // strsobjdict.h            see license.txt for copyright and terms of use
     3: // dictionary of *serf* pointers to objects, indexed by sm_string (case-sensitive)
     4: // (c) Scott McPeak, 2000
     5: 
     6: // created by copying strobjdict and modifying.. nonideal..
     7: 
     8: #ifndef __STRSOBJDICT_H
     9: #define __STRSOBJDICT_H
    10: 
    11: #include "sm_svdict.h"
    12: 
    13: 
    14: // since the dictionary does not own the pointed-to objects,
    15: // it has the same constness model as StringVoidDict, namely
    16: // that const means the *mapping* is constant but not the
    17: // pointed-to objects
    18: 
    19: template <class T>
    20: class StringSObjDict {
    21: public:     // types
    22:   // 'foreach' iterator functions
    23:   typedef bool (*ForeachFn)(sm_string const &key, T *value, void *extra);
    24: 
    25:   // external iterator
    26:   class Iter {
    27:   private:
    28:     StringVoidDict::Iter iter;
    29: 
    30:   public:
    31:     Iter(StringSObjDict &dict) : iter(dict.dict) {}
    32:     Iter(Iter const &obj) : DMEMB(iter) {}
    33:     Iter& operator= (Iter const &obj) { CMEMB(iter); return *this; }
    34: 
    35:     bool isDone() const { return iter.isDone(); }
    36:     Iter& next() { iter.next(); return *this; }
    37: 
    38:     sm_string const &key() const { return iter.key(); }
    39:     T *&value() const { return (T *&)iter.value(); }
    40: 
    41:     //int private_getCurrent() const { return iter.private_getCurrent(); }
    42:     void *private_getCurrent() const { return iter.private_getCurrent(); }
    43:   };
    44:   friend class Iter;
    45: 
    46:   class IterC {
    47:   private:
    48:     StringVoidDict::IterC iter;
    49: 
    50:   public:
    51:     IterC(StringSObjDict const &dict) : iter(dict.dict) {}
    52:     IterC(IterC const &obj) : DMEMB(iter) {}
    53:     IterC& operator= (IterC const &obj) { CMEMB(iter); return *this; }
    54: 
    55:     bool isDone() const { return iter.isDone(); }
    56:     IterC& next() { iter.next(); return *this; }
    57: 
    58:     sm_string const &key() const { return iter.key(); }
    59:     T *value() const { return (T *)iter.value(); }
    60: 
    61:     //int private_getCurrent() const { return iter.private_getCurrent(); }
    62:     void *private_getCurrent() const { return iter.private_getCurrent(); }
    63:   };
    64:   friend class IterC;
    65: 
    66: private:    // data
    67:   // underlying dictionary functionality
    68:   StringVoidDict dict;
    69: 
    70: public:     // funcs
    71:   StringSObjDict() : dict() {}
    72:   StringSObjDict(StringSObjDict const &obj) : dict(obj.dict) {}
    73:   ~StringSObjDict() {}
    74: 
    75:   // comparison and assignment use *pointer* comparison/assignment
    76: 
    77:   StringSObjDict& operator= (StringSObjDict const &obj)    { dict = obj.dict; return *this; }
    78: 
    79:   bool operator== (StringSObjDict const &obj) const        { return dict == obj.dict; }
    80:   NOTEQUAL_OPERATOR(StringSObjDict)
    81: 
    82:   // due to similarity with StringVoidDict, see svdict.h for
    83:   // details on these functions' interfaces
    84: 
    85:   // ------- selectors ---------
    86:   int size() const                                     { return dict.size(); }
    87: 
    88:   bool isEmpty() const                                 { return dict.isEmpty(); }
    89:   bool isNotEmpty() const                              { return !isEmpty(); }
    90: 
    91:   bool query(char const *key, T *&value) const         { return dict.query(key, (void*&)value); }
    92:   T *queryf(char const *key) const                     { return (T*)dict.queryf(key); }
    93:   T *queryif(char const *key) const                    { return (T*)dict.queryif(key); }
    94: 
    95:   bool isMapped(char const *key) const                 { return dict.isMapped(key); }
    96: 
    97:   // -------- mutators -----------
    98:   void add(char const *key, T *value)                  { dict.add(key, (void*)value); }
    99: 
   100:   T *modify(char const *key, T *newValue)              { return (T*)dict.modify(key, (void*)newValue); }
   101: 
   102:   T *remove(char const *key)                           { return (T*)dict.remove(key); }
   103: 
   104:   void empty()                                         { dict.empty(); }
   105: 
   106:   // --------- iters -------------
   107:   void foreach(ForeachFn func, void *extra=NULL) const
   108:     { dict.foreach((StringVoidDict::ForeachFn)func, extra); }
   109: 
   110:   // debugging
   111:   //int private_getTopAddr() const { return dict.private_getTopAddr(); }
   112:   void *private_getTopAddr() const { return dict.private_getTopAddr(); }
   113: };
   114: 
   115: 
   116: #endif // __STRSOBJDICT_H
End C section to elk/sm_strsobjdict.h[1]
Start C section to elk/sm_strtokp.h[1 /1 ]
     1: #line 5820 "./lpsrc/sm.pak"
     2: // strtokp.h            see license.txt for copyright and terms of use
     3: // using std::strtok to parse an entire sm_string at once
     4: // Scott McPeak, 1997  This file is public domain.
     5: 
     6: #ifndef __STRTOKP_H
     7: #define __STRTOKP_H
     8: 
     9: #include "sm_str.h"
    10: #include <cstring>    // std::strlen
    11: 
    12: class StrtokParse {
    13:   sm_string buf;          // locally allocated storage
    14:   int _tokc;           // # of tokens found
    15:   char **_tokv;        // array of tokens themselves
    16: 
    17: private:
    18:   void validate(int which) const;
    19:     // throw an exception if which is invalid token
    20: 
    21: public:
    22:   StrtokParse(char const *str, char const *delim);
    23:     // parse 'str' into tokens delimited by chars from 'delim'
    24: 
    25:   ~StrtokParse();
    26:     // clean up'
    27: 
    28:   int tokc() const { return _tokc; }
    29:   operator int () const { return tokc(); }
    30:     // simple count of available tokens
    31: 
    32:   char const *tokv(int which) const;     // may throw xArrayBounds
    33:   char const* operator[] (int which) const { return tokv(which); }
    34:     // access to tokens; must make local copies to modify
    35: 
    36:   sm_string reassemble(int firstTok, int lastTok, char const *originalString) const;
    37:     // return the subsm_string of the original sm_string spanned by the
    38:     // given range of tokens; if firstTok==lastTok, only that token is
    39:     // returned (without any separators); must be that firstTok <=
    40:     // lastTok
    41: 
    42:   sm_string join(int firstTok, int lastTok, char const *separator) const;
    43:     // return a sm_string created by concatenating the given range of tokens
    44:     // together with 'separator' in between them
    45: 
    46:   int offset(int which) const;
    47:     // return a value that, when added to the original 'str' parameter,
    48:     // yields a pointer to where tokv(which) is, as a subsm_string, in that sm_string
    49: 
    50:   int offsetAfter(int which) const
    51:     { return offset(which) + std::strlen(tokv(which)); }
    52:     // offset for character just beyond last one in tokv (should be either
    53:     // a delimiter character, or 0)
    54: 
    55:   char **spawn_tokv_array() { return _tokv; }
    56:     // this is defined because it makes it convenient to generate
    57:     // spawn arguments, and should be limited to use for that purpose
    58:     // (because it exposes internal representation which is in
    59:     // principle subject to change)
    60: };
    61: 
    62: #endif // __STRTOKP_H
    63: 
End C section to elk/sm_strtokp.h[1]
Start C section to elk/sm_strutil.h[1 /1 ]
     1: #line 5884 "./lpsrc/sm.pak"
     2: // strutil.h            see license.txt for copyright and terms of use
     3: // various sm_string utilities built upon the 'str' module
     4: // Scott McPeak, July 2000
     5: 
     6: #ifndef STRUTIL_H
     7: #define STRUTIL_H
     8: 
     9: #include "sm_str.h"
    10: #include <stdio.h>    // FILE
    11: 
    12: // direct sm_string replacement, replacing instances of oldstr with newstr
    13: // (newstr may be "")
    14: sm_string replace(char const *src, char const *oldstr, char const *newstr);
    15: 
    16: // works like unix "tr": the source sm_string is translated character-by-character,
    17: // with occurrences of 'srcchars' replaced by corresponding characters from
    18: // 'destchars'; further, either set may use the "X-Y" notation to denote a
    19: // range of characters from X to Y
    20: sm_string translate(char const *src, char const *srcchars, char const *destchars);
    21: 
    22: // a simple example of using translate; it was originally inline, but a bug
    23: // in egcs made me move it out of line
    24: sm_string sm_stringToupper(char const *src);
    25: //  { return translate(src, "a-z", "A-Z"); }
    26: 
    27: 
    28: // remove any whitespace at the beginning or end of the sm_string
    29: sm_string trimWhitespace(char const *str);
    30: 
    31: 
    32: // encode a block of bytes as a sm_string with C backslash escape
    33: // sequences (but without the opening or closing quotes)
    34: sm_string encodeWithEscapes(char const *src, int len);
    35: 
    36: // safe when the text has no NUL characters
    37: sm_string encodeWithEscapes(char const *src);
    38: 
    39: // adds the quotes too
    40: sm_string quoted(char const *src);
    41: 
    42: 
    43: // decode an escaped sm_string; throw xFormat if there is a problem
    44: // with the escape syntax; if 'delim' is specified, it will also
    45: // make sure there are no unescaped instances of that
    46: void decodeEscapes(sm_string &dest, int &destLen, char const *src,
    47:                    char delim = 0, bool allowNewlines=false);
    48: 
    49: // given a sm_string with quotes and escapes, yield just the sm_string;
    50: // works if there are no escaped NULs
    51: sm_string parseQuotedString(char const *text);
    52: 
    53: 
    54: // this probably belongs in a dedicated module for time/date stuff..
    55: // returns asctime(localtime(time))
    56: sm_string localTimeString();
    57: 
    58: 
    59: // given a directory name like "a/b/c", return "c"
    60: // renamed from 'basename' because of conflict with something in sm_string.h
    61: sm_string sm_basename(char const *src);
    62: 
    63: // given a directory name like "a/b/c", return "a/b"; if 'src' contains
    64: // no slashes at all, return "."
    65: sm_string dirname(char const *src);
    66: 
    67: 
    68: // return 'prefix', pluralized if n!=1; for example
    69: //   plural(1, "egg") yields "egg", but
    70: //   plural(2, "egg") yields "eggs";
    71: // it knows about a few irregular pluralizations (see the source),
    72: // and the expectation is I'll add more irregularities as I need them
    73: sm_string plural(int n, char const *prefix);
    74: 
    75: // same as 'plural', but with the sm_stringized version of the number:
    76: //   pluraln(1, "egg") yields "1 egg", and
    77: //   pluraln(2, "eggs") yields "2 eggs"
    78: sm_string pluraln(int n, char const *prefix);
    79: 
    80: 
    81: // Sometimes it's useful to store a sm_string value in a static buffer;
    82: // most often this is so 'gdb' can see the result.  This function just
    83: // copies its input into a static buffer (of unspecified length, but
    84: // it checks bounds internally), and returns a pointer to that buffer.
    85: char *copyToStaticBuffer(char const *src);
    86: 
    87: 
    88: // true if the first part of 'str' matches 'prefix'
    89: bool prefixEquals(char const *str, char const *prefix);
    90: 
    91: // and similar for last part
    92: bool suffixEquals(char const *str, char const *suffix);
    93: 
    94: 
    95: // read/write sm_strings <-> files
    96: void writeStringToFile(char const *str, char const *fname);
    97: sm_string readStringFromFile(char const *fname);
    98: 
    99: 
   100: // read the next line from a FILE* (e.g. an AutoFILE); the
   101: // newline is returned if it is present (you can use 'chomp'
   102: // to remove it); returns false (and "") on EOF
   103: bool readLine(sm_string &dest, FILE *fp);
   104: 
   105: 
   106: // like perl 'chomp': remove a final newline if there is one
   107: sm_string chomp(char const *src);
   108: 
   109: 
   110: #endif // STRUTIL_H
End C section to elk/sm_strutil.h[1]
Start C section to elk/sm_svdict.h[1 /1 ]
     1: #line 5995 "./lpsrc/sm.pak"
     2: // svdict.h            see license.txt for copyright and terms of use
     3: // dictionary of void*, indexed by sm_string (case-sensitive)
     4: // (c) Scott McPeak, 2000
     5: 
     6: // created by modifying strdict; at some point strdict should
     7: // be rewritten to use this module
     8: 
     9: #ifndef __SVDICT_H
    10: #define __SVDICT_H
    11: 
    12: #include <iostream>   // std::ostream
    13: #include "sm_str.h"
    14: #include "sm_macros.h"
    15: #include "sm_xassert.h"
    16: #include "sm_typ.h"
    17: #include "sm_strhash.h"
    18: 
    19: 
    20: // constness: for this class, 'const' means the *mapping* from sm_string
    21: // to void* won't change; but I don't prevent the thing pointed-at
    22: // by the void* from changing (would like multiple levels of constness..)
    23: 
    24: class StringVoidDict {
    25: private:    // types
    26:   // 3/16/03: I believe the reason I stored the information both in a
    27:   // hash table and in a linked list is to be able to support efficient
    28:   // alphabetical iteration
    29:   class Node {
    30:   public:
    31:     Node *next;
    32:     sm_string key;
    33:     void *value;
    34: 
    35:   public:
    36:     Node(char const *k, void *v, Node *n = NULL)
    37:       : next(n), key(k), value(v) {}
    38:     ~Node() {}
    39: 
    40:     static char const *getKey(Node const *n);
    41:   };
    42: 
    43: public:     // types
    44:   // function for general foreach; return false to continue,
    45:   // true to stop iterating
    46:   typedef bool (*ForeachFn)(sm_string const &key, void *value, void *extra);
    47: 
    48:   // function type to delete void*s while emptying
    49:   typedef void (*DelFn)(void *value);
    50: 
    51:   // Note: some care must be taken when dealing with Iters, because
    52:   //       they can be invalidated when held across modifications to
    53:   //       structure of the underlying dictionary
    54:   class Iter {
    55:   private:
    56:     Node *current;
    57: 
    58:   public:
    59:     Iter(Node *n) : current(n) {}
    60:     Iter(StringVoidDict &dict) { operator=(dict.getIter()); }
    61:     Iter(Iter const &obj) : DMEMB(current) {}
    62:     Iter& operator= (Iter const &obj) { CMEMB(current); return *this; }
    63: 
    64:     bool isDone() const { return current == NULL; }
    65:     Iter& next() { xassert(current); current = current->next; return *this; }
    66:       // 'next' returns a value primarily to allow use in for-loop comma exprs
    67: 
    68:     sm_string &key() const { return current->key; }
    69:     void *&value() const { return current->value; }
    70: 
    71:     //int private_getCurrent() const { return (int)current; }
    72:     void *private_getCurrent() const { return current; }
    73:   };
    74:   friend class Iter;
    75: 
    76:   // iterator that can't modify the dictionary entries
    77:   class IterC : protected Iter {
    78:   public:
    79:     IterC(Node const *n) : Iter(const_cast<Node*>(n)) {}
    80:     IterC(StringVoidDict const &dict) : Iter(const_cast<StringVoidDict&>(dict)) {}
    81:     IterC(IterC const &obj) : Iter(obj) {}
    82:     IterC& operator= (IterC const &obj) { Iter::operator=(obj); return *this; }
    83: 
    84:     // some operations can be made available unchanged
    85:     Iter::isDone;
    86:     Iter::next;
    87:     Iter::private_getCurrent;
    88: 
    89:     // others must be const-ified
    90:     sm_string const &key() const { return Iter::key(); }
    91:     void const *&value() const { return (void const *&)Iter::value(); }
    92:   };
    93: 
    94: private:    // data
    95:   // first list node (possibly NULL)
    96:   Node *top;
    97: 
    98:   // hash table to improve lookup performance
    99:   StringHash hash;
   100: 
   101:   // invariants:
   102:   //   list is well-formed structurally
   103:   //   every node is in the hash table, and vice versa
   104: 
   105: protected:  // funcs
   106:   void selfCheck() const;      // throw exception if invariants violated
   107: 
   108:   void verifySorted() const;   // throw exception if list isn't sorted
   109: 
   110:   void /*mutable*/ sort();     // arrange nodes in alphabetically sorted order
   111:     // (mutable because this isn't supposed to be visible from the outside)
   112: 
   113: public:
   114:   StringVoidDict();          // initializes to empty dictionary
   115:   StringVoidDict(StringVoidDict const &obj);
   116:   ~StringVoidDict();
   117: 
   118:   StringVoidDict& operator= (StringVoidDict const &obj);
   119: 
   120:   // comparison is done by pointer equality of void*
   121:   bool operator== (StringVoidDict const &obj) const;
   122:   NOTEQUAL_OPERATOR(StringVoidDict)
   123: 
   124:   // ------- selectors ---------
   125:   int size() const;
   126:     // retrieve # of mappings
   127: 
   128:   bool isEmpty() const;
   129:     // returns true if size() is 0
   130: 
   131:   bool isNotEmpty() const
   132:     { return !isEmpty(); }
   133: 
   134:   bool query(char const *key, void *&value) const;
   135:     // if 'key' is mapped to a value, put it into 'value' and return true;
   136:     // otherwise, return false
   137: 
   138:   void *queryf(char const *key) const;
   139:     // return the value corresponding to 'key', or throw an exception of it's
   140:     // not mapped
   141: 
   142:   void *queryif(char const *key) const;
   143:     // return the value corresponding to 'key', or return NULL
   144: 
   145:   bool isMapped(char const *key) const;
   146:     // return true if 'key' is mapped to a value
   147: 
   148:   // -------- mutators -----------
   149:   void add(char const *key, void *value);
   150:     // add a mapping from 'key' to 'value'; 'key' must initially be unmapped
   151: 
   152:   void *modify(char const *key, void *newValue);
   153:     // change the existing value for 'key', which must exist, to 'newValue';
   154:     // the old value is returned
   155: 
   156:   void *remove(char const *key);
   157:     // remove the mapping from 'key', which must exist; it is returned
   158: 
   159:   void emptyAndDel(DelFn func);
   160:     // apply the deletion func to all values, during empty()
   161: 
   162:   void empty();
   163:     // remove all mappings
   164: 
   165:   // --------- iters -------------
   166:   Iter getIter();
   167:     // retrieve an iterator (the iterator remains valid only as long as
   168:     // the structure of the dictionary does not get modified);
   169:     // values will be iterated in *alphabetical* order
   170: 
   171:   IterC getIterC() const;
   172:     // retrieve a const iterator
   173: 
   174:   Iter find(char const *key);
   175:     // return an iterator pointing to 'key', or an iterator
   176:     // that isDone() if 'key' isn't mapped
   177: 
   178:   void foreach(ForeachFn func, void *extra=NULL) const;
   179:     // apply 'func' to every mapping, in alphabetical order
   180: 
   181:   // ------------ misc --------------
   182:   INSERT_OSTREAM(StringVoidDict)
   183:   sm_string toString() const;
   184: 
   185:   // debugging...
   186:   //int private_getTopAddr() const { return (int)top; }
   187:   void *private_getTopAddr() const { return top; }
   188: };
   189: 
   190: #endif // __SVDICT_H
End C section to elk/sm_svdict.h[1]
Start C section to elk/sm_syserr.h[1 /1 ]
     1: #line 6186 "./lpsrc/sm.pak"
     2: // syserr.h            see license.txt for copyright and terms of use
     3: // error-reporting exception for system calls that fail
     4: // Scott McPeak, 1999  This file is public domain.
     5: 
     6: // The intent here is to provide a way for portable *handling* of errors
     7: // that are generated by nonportable code.
     8: 
     9: #ifndef __SYSERR_H
    10: #define __SYSERR_H
    11: 
    12: #include "sm_exc.h"
    13: 
    14: class xSysError : public xBase {
    15: private:    // data
    16:   // error sm_strings for Reasons
    17:   static char const * const reasonStrings[];
    18: 
    19: public:     // data
    20:   // portable failure reasons (modelled loosely on errno.h)
    21:   // it is anticipated that, as certain errors become important on certain
    22:   //   platforms, that this list will be extended as necessary
    23:   enum Reason {
    24:     R_NO_ERROR,          // no error occurred
    25:     R_FILE_NOT_FOUND,
    26:     R_PATH_NOT_FOUND,
    27:     R_ACCESS_DENIED,
    28:     R_OUT_OF_MEMORY,
    29:     R_SEGFAULT,          // invalid address / pointer
    30:     R_FORMAT,            // bad data format
    31:     R_INVALID_ARGUMENT,
    32:     R_READ_ONLY,
    33:     R_ALREADY_EXISTS,
    34:     R_AGAIN,             // resource temporarily unavailable
    35:     R_BUSY,              // resource busy
    36:     R_INVALID_FILENAME,  // too long, bad chars, etc.
    37:     R_UNKNOWN,           // OS-specific, can't find out, just don't know, etc.
    38:     NUM_REASONS          // (must be last item in list)
    39:   } reason;
    40: 
    41:   // reason sm_string that corresponds to 'reason'
    42:   char const * const reasonString;
    43: 
    44:   // nonportable error code (errno on Unix, GetLastError() on Windows)
    45:   // (value is 0 when we don't have this information)
    46:   int sysErrorCode;
    47: 
    48:   // reason sm_string given by the OS, if any (might be NULL)
    49:   sm_string sysReasonString;
    50: 
    51:   // name of syscall or API function name
    52:   sm_string syscallName;
    53: 
    54:   // error context; what was being done (e.g., "opening an.important.file")
    55:   sm_string context;
    56: 
    57: public:    // funcs
    58:   xSysError(Reason r, int sysCode, char const *sysReason,
    59:             char const *syscall, char const *ctx);
    60:   xSysError(xSysError const &obj);
    61:   ~xSysError();
    62: 
    63:   // mapping functions used internally
    64:   static int getSystemErrorCode();
    65:     // retrieve the error code used by local convention
    66:     // [nonportable implementation]
    67: 
    68:   static Reason portablize(int sysErrorCode, sm_string &sysReason);
    69:     // return a portable equivalent of a system error code;
    70:     // returns R_UNKNOWN if the code is esoteric or invalid;
    71:     // sets 'sysmsg' to the system's message sm_string, if possible
    72:     // [nonportable implementation]
    73: 
    74:   static char const *getReasonString(Reason r);
    75:     // translate a Reason into a sm_string (if r is invalid, a sm_string
    76:     // saying to will be returned)
    77: 
    78:   static sm_string constructWhyString(Reason r, char const *sysReason,
    79:                                    char const *syscall, char const *ctx);
    80:     // construct the sm_string we throw as the 'why' of xBase; if ctx is NULL,
    81:     // the sm_string doesn't include it
    82: 
    83:   static void xsyserror(char const *syscallName, char const *context);
    84:     // does the throw
    85: };
    86: 
    87: 
    88: // function that does the throw
    89: inline void xsyserror(char const *syscallName, char const *context = NULL)
    90: {
    91:   xSysError::xsyserror(syscallName, context);
    92: }
    93: 
    94: 
    95: // get a representative sm_string, for logging etc.
    96: sm_string sysErrorCodeString(int systemErrorCode, char const *syscallName,
    97:                                                char const *context=NULL);
    98: 
    99: inline sm_string sysErrorString(char const *syscallName, char const *context=NULL)
   100: {
   101:   return sysErrorCodeString(xSysError::getSystemErrorCode(),
   102:                             syscallName, context);
   103: }
   104: 
   105: 
   106: #endif // __SYSERR_H
   107: 
End C section to elk/sm_syserr.h[1]
Start C section to elk/sm_taillist.h[1 /1 ]
     1: #line 6294 "./lpsrc/sm.pak"
     2: // taillist.h; see license.txt for copyright and terms of use
     3: // list wrapper around VoidTailList in the spirit of ASTList, but doesn't own
     4: 
     5: // taken almost verbatim from asttlist.h in smbase
     6: 
     7: #ifndef TAILLIST_H
     8: #define TAILLIST_H
     9: 
    10: #include "sm_vdtllist.h"
    11: 
    12: template <class T> class TailListIter;
    13: template <class T> class TailListIterNC;
    14: 
    15: // a list which does not own the items in it (will NOT deallocate
    16: // them), and has constant-time access to the last element
    17: template <class T>
    18: class TailList {
    19: private:
    20:   friend class TailListIter<T>;
    21:   friend class TailListIterNC<T>;
    22: 
    23: protected:
    24:   VoidTailList list;            // list itself
    25: 
    26: private:
    27:   TailList(TailList const &obj); // not allowed
    28: 
    29: public:
    30:   TailList()                             : list() {}
    31:   ~TailList()                            {  }
    32: 
    33:   // ctor to make singleton list; often quite useful
    34:   TailList(T *elt)                       : list() { prepend(elt); }
    35: 
    36:   // stealing ctor; among other things, since &src->list is assumed to
    37:   // point at 'src', this class can't have virtual functions;
    38:   // these ctors delete 'src'
    39:   TailList(TailList<T> *src)              : list(&src->list) {}
    40:   void steal(TailList<T> *src)           { list.steal(&src->list); }
    41: 
    42:   // selectors
    43:   int count() const                     { return list.count(); }
    44:   bool isEmpty() const                  { return list.isEmpty(); }
    45:   bool isNotEmpty() const               { return list.isNotEmpty(); }
    46:   T *nth(int which)                     { return (T*)list.nth(which); }
    47:   T const *nthC(int which) const        { return (T const*)list.nth(which); }
    48:   T *first()                            { return (T*)list.first(); }
    49:   T const *firstC() const               { return (T const*)list.first(); }
    50:   T *last()                             { return (T*)list.last(); }
    51:   T const *lastC() const                { return (T const*)list.last(); }
    52: 
    53:   // insertion
    54:   void prepend(T *newitem)              { list.prepend(newitem); }
    55:   void append(T *newitem)               { list.append(newitem); }
    56:   void insertAt(T *newitem, int index)  { list.insertAt(newitem, index); }
    57:   void concat(TailList<T> &tail)         { list.concat(tail.list); }
    58: 
    59:   // removal
    60:   T *removeFirst()                      { return (T*)list.removeFirst(); }
    61:   T *removeLast()                       { return (T*)list.removeLast(); }
    62:   T *removeAt(int index)                { return (T*)list.removeAt(index); }
    63:   void removeItem(T *item)              { list.removeItem((void*)item); }
    64: 
    65:   // list-as-set: selectors
    66:   int indexOf(T const *item) const      { return list.indexOf((void*)item); }
    67:   int indexOfF(T const *item) const     { return list.indexOfF((void*)item); }
    68:   bool contains(T const *item) const    { return list.contains((void*)item); }
    69: 
    70:   // list-as-set: mutators
    71:   bool prependUnique(T *newitem)        { return list.prependUnique(newitem); }
    72:   bool appendUnique(T *newitem)         { return list.appendUnique(newitem); }
    73: 
    74:   // debugging: two additional invariants
    75:   void selfCheck() const                { list.selfCheck(); }
    76: };
    77: 
    78: 
    79: template <class T>
    80: class TailListIter {
    81: protected:
    82:   VoidTailListIter iter;        // underlying iterator
    83: 
    84: public:
    85:   TailListIter(TailList<T> const &list) : iter(list.list) {}
    86:   ~TailListIter()                       {}
    87: 
    88:   void reset(TailList<T> const &list)   { iter.reset(list.list); }
    89: 
    90:   // iterator copying; generally safe
    91:   TailListIter(TailListIter const &obj)             : iter(obj.iter) {}
    92:   TailListIter& operator=(TailListIter const &obj)  { iter = obj.iter;  return *this; }
    93: 
    94:   // iterator actions
    95:   bool isDone() const                   { return iter.isDone(); }
    96:   void adv()                            { iter.adv(); }
    97:   T const *data() const                 { return (T const*)iter.data(); }
    98: };
    99: 
   100: #define FOREACH_TAILLIST(T, list, iter) \
   101:   for(TailListIter<T> iter(list); !iter.isDone(); iter.adv())
   102: 
   103: 
   104: // version of the above, but for non-const-element traversal
   105: template <class T>
   106: class TailListIterNC {
   107: protected:
   108:   VoidTailListIter iter;        // underlying iterator
   109: 
   110: public:
   111:   TailListIterNC(TailList<T> &list)      : iter(list.list) {}
   112:   ~TailListIterNC()                     {}
   113: 
   114:   void reset(TailList<T> &list)         { iter.reset(list.list); }
   115: 
   116:   // iterator copying; generally safe
   117:   TailListIterNC(TailListIterNC const &obj)             : iter(obj.iter) {}
   118:   TailListIterNC& operator=(TailListIterNC const &obj)  { iter = obj.iter;  return *this; }
   119: 
   120:   // iterator actions
   121:   bool isDone() const                   { return iter.isDone(); }
   122:   void adv()                            { iter.adv(); }
   123:   T *data() const                       { return (T*)iter.data(); }
   124: 
   125:   // iterator mutation; use with caution
   126:   void setDataLink(T *newData)          { iter.setDataLink((void*)newData); }
   127: };
   128: 
   129: #define FOREACH_TAILLIST_NC(T, list, iter) \
   130:   for(TailListIterNC<T> iter(list); !iter.isDone(); iter.adv())
   131: 
   132: #endif // TailLIST_H
End C section to elk/sm_taillist.h[1]
Start C section to elk/sm_test.h[1 /1 ]
     1: #line 6427 "./lpsrc/sm.pak"
     2: // test.h            see license.txt for copyright and terms of use
     3: // utilities for test code
     4: // Scott McPeak, 1999  This file is public domain.
     5: 
     6: #ifndef __TEST_H
     7: #define __TEST_H
     8: 
     9: #include <iostream>      // cout
    10: #include <stdio.h>         // printf
    11: #include "sm_exc.h"
    12: #include "sm_nonport.h"
    13: 
    14: 
    15: // reports uncaught exceptions
    16: //
    17: // 12/30/02: I used to print "uncaught exception: " before
    18: // printing the exception, but this is meaningless to the
    19: // user and the message usually has enough info anyway
    20: #define USUAL_MAIN                              \
    21: void entry();                                   \
    22: int main()                                      \
    23: {                                               \
    24:   try {                                         \
    25:     entry();                                    \
    26:     return 0;                                   \
    27:   }                                             \
    28:   catch (xBase &x) {                            \
    29:     std::cout << x << std::endl;                          \
    30:     return 4;                                   \
    31:   }                                             \
    32: }
    33: 
    34: // same as above, but with command-line args
    35: #define ARGS_MAIN                               \
    36: void entry(int argc, char *argv[]);             \
    37: int main(int argc, char *argv[])                \
    38: {                                               \
    39:   try {                                         \
    40:     entry(argc, argv);                          \
    41:     return 0;                                   \
    42:   }                                             \
    43:   catch (xBase &x) {                            \
    44:     std::cout << x << std::endl;                          \
    45:     return 4;                                   \
    46:   }                                             \
    47: }
    48: 
    49: 
    50: // convenient for printing the value of a variable or expression
    51: #define PVAL(val) std::cout << #val << " = " << (val) << std::endl
    52: 
    53: 
    54: // easy way to time a section of code
    55: class TimedSection {
    56:   char const *name;
    57:   long start;
    58: 
    59: public:
    60:   TimedSection(char const *n) : name(n) {
    61:     start = getMilliseconds();
    62:   }
    63:   ~TimedSection() {
    64:     std::cout << name << ": " << (getMilliseconds() - start) << " msecs\n";
    65:   }
    66: };
    67: 
    68: 
    69: #endif // __TEST_H
    70: 
End C section to elk/sm_test.h[1]
Start C section to elk/sm_thashtbl.h[1 /1 ]
     1: #line 6498 "./lpsrc/sm.pak"
     2: // thashtbl.h            see license.txt for copyright and terms of use
     3: // type-safe version of HashTable
     4: 
     5: #ifndef THASHTBL_H
     6: #define THASHTBL_H
     7: 
     8: #include "sm_hashtbl.h"
     9: 
    10: template <class KEY, class DATA> class THashTableIter;
    11: 
    12: template <class KEY, class DATA>
    13: class THashTable {
    14: private:    // types
    15:   friend class THashTableIter<KEY, DATA>;
    16: 
    17: public:     // types
    18:   // given a stored data pointer, retrieve the associated key
    19:   typedef KEY const* (*GetKeyFn)(DATA *data);
    20: 
    21:   // given a key, retrieve the associated hash value;
    22:   // this should be a 32-bit integer ready to be mod'd by the table size
    23:   typedef unsigned (*HashFn)(KEY const *key);
    24: 
    25:   // compare two keys; this is needed so we can handle collisions
    26:   // in the hash function; return true if they are equal
    27:   typedef bool (*EqualKeyFn)(KEY const *key1, KEY const *key2);
    28: 
    29: private:    // data
    30:   // underlying table
    31:   HashTable table;
    32: 
    33: private:    // funcs
    34:   // disallowed
    35:   THashTable(THashTable&);
    36:   void operator=(THashTable&);
    37:   void operator==(THashTable&);
    38: 
    39: public:     // funcs
    40:   THashTable(GetKeyFn gk, HashFn hf, EqualKeyFn ek,
    41:              int initSize = HashTable::defaultSize)
    42:     : table((HashTable::GetKeyFn)gk,
    43:             (HashTable::HashFn)hf,
    44:             (HashTable::EqualKeyFn)ek,
    45:             initSize)
    46:   {}
    47:   ~THashTable() {}
    48: 
    49:   // return # of mapped entries
    50:   int getNumEntries() const                 { return table.getNumEntries(); }
    51: 
    52:   // if this hash value has a mapping, return it; otherwise,
    53:   // return NULL
    54:   DATA *get(KEY const *key) const           { return (DATA*)table.get(key); }
    55: 
    56:   // add a mapping from 'key' to 'value'; there must not already
    57:   // be a mapping for this key
    58:   void add(KEY const *key, DATA *value)     { table.add(key, value); }
    59: 
    60:   // remove the mapping for 'key' -- it must exist;
    61:   // returns the removed item
    62:   DATA *remove(KEY const *key)              { return (DATA*)table.remove(key); }
    63: 
    64:   // remove all mappings
    65:   void empty(int initSize = HashTable::defaultSize)   { table.empty(initSize); }
    66: 
    67:   // set whether shrinkage is allowed; it's useful to be able to
    68:   // disable this to avoid any allocation in certain situations
    69:   void setEnableShrink(bool en)             { table.setEnableShrink(en); }
    70: 
    71:   // allow external access to an accessor function
    72:   KEY const *callGetKeyFn(DATA *data)       { return (KEY const*)table.getKey(data); }
    73: 
    74:   // check the data structure's invariants, and throw an exception
    75:   // if there is a problem
    76:   void selfCheck() const                    { table.selfCheck(); }
    77: };
    78: 
    79: 
    80: // iterate over all stored values in a THashTable
    81: // NOTE: you can't change the table while an iter exists
    82: template <class KEY, class DATA>
    83: class THashTableIter {
    84: private:      // data
    85:   HashTableIter iter;            // underlying iter
    86: 
    87: public:       // funcs
    88:   THashTableIter(THashTable<KEY,DATA> &table) : iter(table.table) {}
    89: 
    90:   bool isDone() const          { return iter.isDone(); }
    91:   void adv()                   { return iter.adv(); }
    92:   DATA *data() const           { return (DATA*)iter.data(); }
    93: };
    94: 
    95: 
    96: #endif // THASHTBL_H
End C section to elk/sm_thashtbl.h[1]
Start C section to elk/sm_trace.h[1 /1 ]
     1: #line 6595 "./lpsrc/sm.pak"
     2: // trace.h            see license.txt for copyright and terms of use
     3: // module for diagnostic tracing
     4: // see trace.html
     5: 
     6: #ifndef TRACE_H
     7: #define TRACE_H
     8: 
     9: #include <iostream>     // std::ostream
    10: 
    11: 
    12: // add a subsystem to the list of those being traced
    13: void traceAddSys(char const *sysName);
    14: 
    15: // remove a subsystem; must have been there
    16: void traceRemoveSys(char const *sysName);
    17: 
    18: // see if a subsystem is among those to trace
    19: bool tracingSys(char const *sysName);
    20: 
    21: // clear all tracing flags
    22: void traceRemoveAll();
    23: 
    24: 
    25: // trace; if the named system is active, this yields cout (after
    26: // sending a little output to identify the system); if not, it
    27: // yields an std::ostream attached to /dev/null; when using this
    28: // method, it is up to you to put the newline
    29: std::ostream &trace(char const *sysName);
    30: 
    31: // give an entire sm_string to trace; do *not* put a newline in it
    32: // (the tracer will do that)
    33: void trstr(char const *sysName, char const *traceString);
    34: 
    35: // trace macro which disables itself when NDEBUG is true,
    36: // and automatically supplies 'std::endl' when it's not true
    37: //
    38: // dsw: debugging *weakly* implies tracing: if we are debugging, do
    39: // tracing unless otherwise specified
    40: #ifndef NDEBUG
    41:   #ifndef DO_TRACE
    42:     #define DO_TRACE 1
    43:   #endif
    44: #endif
    45: // dsw: tracing *bidirectionally* configurable from the command line:
    46: // it may be turned on *or* off: any definition other than '0' counts
    47: // as true, such as -DDO_TRACE=1 or just -DDO_TRACE
    48: #ifndef DO_TRACE
    49:   #define DO_TRACE 0
    50: #endif
    51: #if DO_TRACE != 0
    52:   #define TRACE(tag, exp) trace(tag) << exp << std::endl /* user ; */
    53: #else
    54:   #define TRACE(tag, exp) ((void)0)
    55: #endif
    56: 
    57: 
    58: // special for "progress" tracing; prints time too;
    59: // 'level' is level of detail -- 1 is highest level, 2 is
    60: // more refined (and therefore usually not printed), etc.
    61: std::ostream &traceProgress(int level=1);
    62: 
    63: 
    64: // add one or more subsystems, separated by commas
    65: void traceAddMultiSys(char const *systemNames);
    66: 
    67: // if the first argument is a tracing directive, handle it, modify
    68: // argc and argv modified to effectively remove it, and return true
    69: // (argv[0] is assumed to be ignored by everything); this calls
    70: // 'traceAddFromEnvVar' too
    71: bool traceProcessArg(int &argc, char **&argv);
    72: 
    73: // so here's a simple loop that will consume any leading
    74: // trace arguments
    75: #define TRACE_ARGS() while (traceProcessArg(argc, argv)) {}
    76: 
    77: 
    78: // add tracing flags from the environment variable "TRACE",
    79: // unless 'ignoreTraceEnvVar' is true; this sets it to true,
    80: // so it's idempotent
    81: void traceAddFromEnvVar();
    82: extern bool ignoreTraceEnvVar;    // initially false
    83: 
    84: 
    85: #endif // TRACE_H
End C section to elk/sm_trace.h[1]
Start C section to elk/sm_trdelete.h[1 /1 ]
     1: #line 6681 "./lpsrc/sm.pak"
     2: // trdelete.h            see license.txt for copyright and terms of use
     3: // objects which trash their contents upon deletion
     4: // I would love to have implemented this as a base class and simply derive
     5: //   things from it, but a poor implementation choice by Borland makes this
     6: //   too costly in terms of performance
     7: 
     8: #ifdef _MSC_VER
     9:   // this module doesn't work under msvc, I don't care to figure out why
    10:   #define TRDELETE_H      // make it omit this file
    11:   #define TRASHINGDELETE  // and all references to it a no-op
    12: #endif
    13: 
    14: #ifndef TRDELETE_H
    15: #define TRDELETE_H
    16: 
    17: #include <stddef.h>      // size_t
    18: 
    19: void trashingDelete(void *blk, size_t size);
    20: void trashingDeleteArr(void *blk, size_t size);
    21: 
    22: // to use, include the TRASHINGDELETE macro in the public section of a class
    23: 
    24: #define TRASHINGDELETE                                                              \
    25:   void operator delete(void *blk, size_t size) { trashingDelete(blk, size); }       \
    26:   void operator delete[](void *blk, size_t size) { trashingDeleteArr(blk, size); }
    27: 
    28: #endif // TRDELETE_H
End C section to elk/sm_trdelete.h[1]
Start C section to elk/sm_typ.h[1 /1 ]
     1: #line 6710 "./lpsrc/sm.pak"
     2: // typ.h            see license.txt for copyright and terms of use
     3: // various types and definitions, some for portability, others for convenience
     4: // Scott McPeak, 1996-2000  This file is public domain.
     5: 
     6: #ifndef __TYP_H
     7: #define __TYP_H
     8: 
     9: // js: this crud is required to provide an integer type
    10: // to which a void* can be cast so the result can be
    11: // input to a hashing function
    12: 
    13: #include "../rtl/flx_elk_config.hpp"
    14: typedef FLX_RAWADDRESS SM_RAWADDRESS;
    15: 
    16: // byte
    17: typedef unsigned char byte;
    18: typedef signed char signed_byte;
    19: 
    20: 
    21: // int32 used to be here, but defined nonportably, and I don't use
    22: // it anyway, so I ripped it out
    23: 
    24: 
    25: // NULL
    26: #ifndef NULL
    27: #  define NULL 0
    28: #endif // NULL
    29: 
    30: 
    31: // bool
    32: #ifdef LACKS_BOOL
    33:   typedef int bool;
    34:   bool const false=0;
    35:   bool const true=1;
    36: #endif // LACKS_BOOL
    37: 
    38: 
    39: // min, max
    40: #undef min
    41: #undef max
    42: 
    43: template <class T>
    44: inline T min(T const &a, T const &b)
    45: {
    46:   return a<b? a:b;
    47: }
    48: 
    49: template <class T>
    50: inline T max(T const &a, T const &b)
    51: {
    52:   return a>b? a:b;
    53: }
    54: 
    55: 
    56: #if 0   // old
    57:   #ifndef __MINMAX_DEFINED
    58:   # ifndef min
    59:   #  define min(a,b) ((a)<(b)?(a):(b))
    60:   # endif
    61:   # ifndef max
    62:   #  define max(a,b) ((a)>(b)?(a):(b))
    63:   # endif
    64:   # define __MINMAX_DEFINED
    65:   #endif // __MINMAX_DEFINED
    66: #endif // 0
    67: 
    68: 
    69: // tag for definitions of static member functions; there is no
    70: // compiler in existence for which this is useful, but I like
    71: // to see *something* next to implementations of static members
    72: // saying that they are static, and this seems slightly more
    73: // formal than just a comment
    74: #define STATICDEF /*static*/
    75: 
    76: 
    77: // often-useful number-of-entries function
    78: #define TABLESIZE(tbl) ((int)(sizeof(tbl)/sizeof((tbl)[0])))
    79: 
    80: 
    81: // concise way to loop on an integer range
    82: #define loopi(end) for(int i=0; i<(int)(end); i++)
    83: #define loopj(end) for(int j=0; j<(int)(end); j++)
    84: #define loopk(end) for(int k=0; k<(int)(end); k++)
    85: 
    86: 
    87: // for using selfCheck methods
    88: // to explicitly check invariants in debug mode
    89: //
    90: // dsw: debugging *weakly* implies selfchecking: if we are debugging,
    91: // do selfcheck unless otherwise specified
    92: #ifndef NDEBUG
    93:   #ifndef DO_SELFCHECK
    94:     #define DO_SELFCHECK 1
    95:   #endif
    96: #endif
    97: // dsw: selfcheck *bidirectionally* configurable from the command line: it
    98: // may be turned on *or* off: any definition other than '0' counts as
    99: // true, such as -DDO_SELFCHECK=1 or just -DDO_SELFCHECK
   100: #ifndef DO_SELFCHECK
   101:   #define DO_SELFCHECK 0
   102: #endif
   103: #if DO_SELFCHECK != 0
   104:   #define SELFCHECK() selfCheck()
   105: #else
   106:   #define SELFCHECK() ((void)0)
   107: #endif
   108: 
   109: 
   110: // division with rounding towards +inf
   111: // (when operands are positive)
   112: template <class T>
   113: inline T div_up(T const &x, T const &y)
   114: { return (x + y - 1) / y; }
   115: 
   116: 
   117: // mutable
   118: #ifdef __BORLANDC__
   119: #  define MUTABLE
   120: #  define EXPLICIT
   121: #else
   122: #  define MUTABLE mutable
   123: #  define EXPLICIT explicit
   124: #endif
   125: 
   126: 
   127: #define SWAP(a,b) \
   128:   temp = a;       \
   129:   a = b;          \
   130:   b = temp /*user supplies semicolon*/
   131: 
   132: 
   133: // verify something is true at compile time (will produce
   134: // a compile error if it isn't)
   135: // update: use STATIC_ASSERT defined in macros.h instead
   136: //#define staticAssert(cond) extern int dummyArray[cond? 1 : 0]
   137: 
   138: 
   139: #endif // __TYP_H
   140: 
End C section to elk/sm_typ.h[1]
Start C section to elk/sm_vdtllist.h[1 /1 ]
     1: #line 6851 "./lpsrc/sm.pak"
     2: // vdtllist.h            see license.txt for copyright and terms of use
     3: // list of void*, with a pointer maintained to the last (tail)
     4: // element, for constant-time append
     5: 
     6: #ifndef VDTLLIST_H
     7: #define VDTLLIST_H
     8: 
     9: #include "sm_voidlist.h"
    10: 
    11: // inherit privately so I can choose what to expose
    12: class VoidTailList : private VoidList {
    13: private:
    14:   // by making this a friend, it should see VoidList as a
    15:   // base class, and thus simply work
    16:   // but it doesn't..
    17:   //friend VoidListIter;
    18: 
    19:   friend class VoidTailListIter;
    20: 
    21:   // no mutator for now
    22: 
    23: protected:
    24:   VoidNode *tail;       // (serf) last element of list, or NULL if list is empty
    25:   VoidNode *getTop() const { return VoidList::getTop(); }
    26: 
    27: private:
    28:   VoidTailList(VoidTailList const &obj);    // not allowed
    29: 
    30:   void adjustTail();
    31: 
    32: public:
    33:   VoidTailList()                     { tail = NULL; }
    34:   ~VoidTailList()                    {}
    35: 
    36:   // special ctor which steals the list and then deallocates the header
    37:   VoidTailList(VoidTailList *src)    { tail = NULL; steal(src); }
    38:   void steal(VoidTailList *src);     // deletes 'src'
    39: 
    40:   // this syntax just makes the implementation inherited from
    41:   // 'VoidList' public, whereas it would default to private,
    42:   // since it was inherited privately
    43:   VoidList::count;
    44: 
    45:   // see voidlist.h for documentation of each of these functions
    46:   VoidList::isEmpty;
    47:   VoidList::isNotEmpty;
    48:   VoidList::nth;
    49:   VoidList::first;
    50:   void *last() const                 { xassert(tail); return tail->data; }
    51: 
    52:   // insertion
    53:   void prepend(void *newitem);
    54:   void append(void *newitem);
    55:   void insertAt(void *newitem, int index);
    56:   void concat(VoidTailList &tail);
    57: 
    58:   // removal
    59:   void *removeFirst();               // remove first, return data; must exist
    60:   void *removeLast();
    61:   void *removeAt(int index);
    62:   void removeAll();
    63:   VoidList::removeItem;
    64: 
    65:   // list-as-set: selectors
    66:   VoidList::indexOf;
    67:   VoidList::indexOfF;
    68:   VoidList::contains;
    69: 
    70:   // list-as-set: mutators
    71:   bool prependUnique(void *newitem);
    72:   bool appendUnique(void *newitem);
    73:   //void removeItem(void *item);
    74:   //bool removeIfPresent(void *item);
    75: 
    76:   // debugging
    77:   void selfCheck() const;
    78:   VoidList::debugPrint;
    79: };
    80: 
    81: 
    82: // copied from voidlist.h because g++ won't do what I want..
    83: class VoidTailListIter {
    84: protected:
    85:   VoidNode *p;                        // (serf) current item
    86: 
    87: public:
    88:   VoidTailListIter(VoidTailList const &list)  { reset(list); }
    89:   ~VoidTailListIter()                         {}
    90: 
    91:   void reset(VoidTailList const &list)        { p = list.getTop(); }
    92: 
    93:   // iterator copying; generally safe
    94:   VoidTailListIter(VoidTailListIter const &obj)             { p = obj.p; }
    95:   VoidTailListIter& operator=(VoidTailListIter const &obj)  { p = obj.p; return *this; }
    96: 
    97:   // but copying from a mutator is less safe; see above
    98:   //VoidTailListIter(VoidListMutator &obj)      { p = obj.current; }
    99: 
   100:   // iterator actions
   101:   bool isDone() const                         { return p == NULL; }
   102:   void adv()                                  { p = p->next; }
   103:   void *data() const                          { return p->data; }
   104: 
   105:   // iterator mutation; use with caution
   106:   void setDataLink(void *newData)             { p->data = newData; }
   107: };
   108: 
   109: 
   110: 
   111: #endif // VDTLLIST_H
End C section to elk/sm_vdtllist.h[1]
Start C section to elk/sm_voidlist.h[1 /1 ]
     1: #line 6963 "./lpsrc/sm.pak"
     2: // voidlist.h            see license.txt for copyright and terms of use
     3: // list of void*
     4: 
     5: // Author: Scott McPeak, 2000
     6: 
     7: #ifndef __VOIDLIST_H
     8: #define __VOIDLIST_H
     9: 
    10: #include "sm_xassert.h"
    11: #include "sm_typ.h"
    12: #include "sm_trdelete.h"
    13: 
    14: // -------------------------- non-typesafe core -----------------------------
    15: // non-typesafe list node
    16: class VoidNode {
    17: public:
    18:   TRASHINGDELETE
    19: 
    20:   VoidNode *next;           // (owner) next item in list, or NULL if last item
    21:   void *data;               // whatever it is the list is holding
    22: 
    23:   VoidNode(void *aData=NULL, VoidNode *aNext=NULL) { data=aData; next=aNext; }
    24: };
    25: 
    26: 
    27: // forward decls for 'friend' decls
    28: class VoidListIter;
    29: class VoidListMutator;
    30: 
    31: 
    32: // The difference function should return <0 if left should come before
    33: // right, 0 if they are equivalent, and >0 if right should come before
    34: // left.  For example, if we are sorting numbers into ascending order,
    35: // then 'diff' could simply be subtraction.
    36: typedef int (*VoidDiff)(void *left, void *right, void *extra);
    37: 
    38: 
    39: // list of void*; at this level, the void* are completely opaque;
    40: // the list won't attempt to delete(), compare them, or anything else
    41: // (well, some comparison has creeped in now... but only via VoidDiff)
    42: class VoidList {
    43: private:
    44:   friend class VoidListIter;
    45:   friend class VoidListMutator;
    46: 
    47: protected:
    48:   VoidNode *top;                     // (owner) first node, or NULL if list is empty
    49:   VoidNode *getTop() const { return top; } // for iterator, below
    50: 
    51: public:
    52:   VoidList()                         { top=NULL; }
    53:   VoidList(VoidList const &obj);     // makes a (shallow) copy of the contents
    54:   ~VoidList()                        { removeAll(); }
    55: 
    56:   // selectors
    57:   int count() const;                 // # of items in list
    58:   bool isEmpty() const               { return top == NULL; }
    59:   bool isNotEmpty() const            { return top != NULL; }
    60:   void *nth(int which) const;        // get particular item, 0 is first (item must exist)
    61:   void *first() const { return nth(0); }
    62:   void *last() const { return nth(count()-1); }
    63: 
    64:   // insertion
    65:   void prepend(void *newitem);       // insert at front
    66:   void append(void *newitem);        // insert at rear
    67:   void insertAt(void *newitem, int index);
    68:     // new item is inserted such that its index becomdes 'index'
    69:   void insertSorted(void *newitem, VoidDiff diff, void *extra=NULL);
    70:     // insert into an already-sorted list so that the list is sorted afterwards
    71: 
    72:   // removal
    73:   void *removeAt(int index);         // remove from list (must exist), and return removed item
    74:   void *removeFirst()                { return removeAt(0); }
    75:   void removeAll();
    76: 
    77:   // list-as-set: selectors
    78:   int indexOf(void *item) const;     // returns index of *first* occurrance, or -1 if not present
    79:   int indexOfF(void *item) const;    // same as indexOf, but throws exception if not present
    80:   bool contains(void *item) const    // true if the item appears in the list
    81:     { return indexOf(item) >= 0; }
    82: 
    83:   // list-as-set: mutators
    84:   bool prependUnique(void *newitem); // prepend only if not already there
    85:   bool appendUnique(void *newitem);  // append   "            "
    86:   void removeItem(void *item);       // remove first occurrance -- must exist
    87:   bool removeIfPresent(void *item);  // remove first occurrance; return true if changed
    88: 
    89:   // complex modifiers
    90:   void reverse();
    91:   void insertionSort(VoidDiff diff, void *extra=NULL);
    92:   void mergeSort(VoidDiff diff, void *extra=NULL);
    93: 
    94:   // and a related test
    95:   bool isSorted(VoidDiff diff, void *extra=NULL) const;
    96: 
    97:   // multiple lists
    98:   void concat(VoidList &tail);           // tail is emptied, nodes appended to this
    99:   void appendAll(VoidList const &tail);  // tail is untouched.. but its contents are now exposed to non-constness... ug... oh well
   100:   VoidList& operator= (VoidList const &src);  // afterwards, 'this' and 'src' have same contents
   101: 
   102:   // steal (become the container for) the tail of a source list at any
   103:   // point; if 'index' is 0, the entire 'source' is stolen (i.e.
   104:   // index=0 is equivalent to 'concat', above); stolen items appended
   105:   // to 'this'
   106:   void stealTailAt(int index, VoidList &source);
   107: 
   108:   // equal items in equal positions
   109:   bool equalAsLists(VoidList const &otherList, VoidDiff diff, void *extra=NULL) const;
   110: 
   111:   // if equal, returns 0; otherwise, return order (-1/+1) according to
   112:   // the first differing pair of elements; a shorter (but otherwise
   113:   // idential list) will compare as being less
   114:   int compareAsLists(VoidList const &otherList, VoidDiff diff, void *extra=NULL) const;
   115: 
   116:   // last-as-set: comparisons (NOT efficient)
   117:   bool equalAsSets(VoidList const &otherList, VoidDiff diff, void *extra=NULL) const;
   118:     // A subset of B, and vice-versa
   119:   bool isSubsetOf(VoidList const &otherList, VoidDiff diff, void *extra=NULL) const;
   120:     // uses slow elementwise containment
   121:   bool containsByDiff(void *item, VoidDiff diff, void *extra=NULL) const;
   122: 
   123:   // treating the pointer values themselves as the basis for comparison
   124:   static int pointerAddressDiff(void *left, void *right, void*);
   125:   bool equalAsPointerLists(VoidList const &otherList) const
   126:     { return equalAsLists(otherList, pointerAddressDiff); }
   127:   bool equalAsPointerSets(VoidList const &otherList) const
   128:     { return equalAsSets(otherList, pointerAddressDiff); }
   129: 
   130:   // debugging
   131:   void selfCheck() const;            // test this list; fail assertion if malformed
   132:   void debugPrint() const;           // print list contents to stdout
   133:   void checkHeapDataPtrs() const;    // fail assertion if any 'data' ptr isn't valid heap ptr
   134:   void checkUniqueDataPtrs() const;  // fail assertion if any 'data' ptr matches any other in this list
   135: };
   136: 
   137: 
   138: // for traversing the list and modifying it
   139: // NOTE: no list-modification fns should be called on 'list' while this
   140: //       iterator exists, and only one such iterator should exist for
   141: //       any given list
   142: class VoidListMutator {
   143:   friend class VoidListIter;
   144: 
   145: protected:
   146:   VoidList &list;         // underlying list
   147:   VoidNode *prev;         // (serf) previous node; NULL if at list's head
   148:   VoidNode *current;      // (serf) node we're considered to be pointing at
   149: 
   150: public:
   151:   VoidListMutator(VoidList &lst)   : list(lst) { reset(); }
   152:   ~VoidListMutator()               {}
   153: 
   154:   void reset()                     { prev = NULL;  current = list.top; }
   155: 
   156:   // iterator copying; safe *only* until one of the mutators modifies
   157:   // the list structure (by inserting or removing), at which time all
   158:   // other iterators might be in limbo
   159:   VoidListMutator(VoidListMutator const &obj)
   160:     : list(obj.list), prev(obj.prev), current(obj.current) {}
   161:   VoidListMutator& operator=(VoidListMutator const &obj);
   162:     // requires that 'this' and 'obj' already refer to the same 'list'
   163: 
   164:   // iterator actions
   165:   bool isDone() const              { return current == NULL; }
   166:   void adv()                       { prev = current;  current = current->next; }
   167:   void *data()                     { return current->data; }
   168:   void *&dataRef()                 { return current->data; }
   169: 
   170:   // insertion
   171:   void insertBefore(void *item);
   172:     // 'item' becomes the new 'current', and the current 'current' is
   173:     // pushed forward (so the next adv() will make it current again)
   174: 
   175:   void insertAfter(void *item);
   176:     // 'item' becomes what we reach with the next adv();
   177:     // isDone() must be false
   178: 
   179:   void append(void *item);
   180:     // only valid while isDone() is true, it inserts 'item' at the end of
   181:     // the list, and advances such that isDone() remains true; equivalent
   182:     // to { xassert(isDone()); insertBefore(item); adv(); }
   183: 
   184:   // removal
   185:   void *remove();
   186:     // 'current' is removed from the list and returned, and whatever was
   187:     // next becomes the new 'current'
   188: 
   189:   // debugging
   190:   void selfCheck() const
   191:     { xassert((prev->next == current  &&  current != list.top) ||
   192:               (prev==NULL && current==list.top)); }
   193: };
   194: 
   195: 
   196: // for traversing the list without modifying it
   197: // NOTE: no list-modification fns should be called on 'list' while this
   198: //       iterator exists
   199: class VoidListIter {
   200: protected:
   201:   VoidNode *p;                        // (serf) current item
   202: 
   203: public:
   204:   VoidListIter(VoidList const &list)  { reset(list); }
   205:   VoidListIter(VoidList const &list, int pos);    // advance 'pos' times
   206:   ~VoidListIter()                     {}
   207: 
   208:   void reset(VoidList const &list)    { p = list.getTop(); }
   209: 
   210:   // iterator copying; generally safe
   211:   VoidListIter(VoidListIter const &obj)             { p = obj.p; }
   212:   VoidListIter& operator=(VoidListIter const &obj)  { p = obj.p;  return *this; }
   213: 
   214:   // but copying from a mutator is less safe; see above
   215:   VoidListIter(VoidListMutator &obj)  { p = obj.current; }
   216: 
   217:   // iterator actions
   218:   bool isDone() const                 { return p == NULL; }
   219:   void adv()                          { p = p->next; }
   220:   void *data() const                  { return p->data; }
   221: };
   222: 
   223: 
   224: #endif // __VOIDLIST_H
End C section to elk/sm_voidlist.h[1]
Start C section to elk/sm_vptrmap.h[1 /1 ]
     1: #line 7188 "./lpsrc/sm.pak"
     2: // vptrmap.h
     3: // map from void* to void*
     4: // interface based partly on hashtbl.h
     5: 
     6: // Design considerations:
     7: //
     8: // Keys are pointers to objects.  They are likely to have the same
     9: // high bits (page) and low bits (alignment), and thus be
    10: // distinguished primarily by the bits in the middle.  No key is NULL.
    11: //
    12: // Deletion of a single mapping is not supported.  To delete some
    13: // mappings you have to rebuild the table.
    14: //
    15: // No adversary is present; hash function is fixed in advance.
    16: 
    17: 
    18: #ifndef VPTRMAP_H
    19: #define VPTRMAP_H
    20: 
    21: 
    22: class VoidPtrMap {
    23: private:     // types
    24:   // single entry in the hash table
    25:   struct Entry {
    26:     void *key;               // NULL only for unused entries
    27:     void *value;             // NULL if key is NULL
    28:   };
    29: 
    30: private:     // data
    31:   // hash table itself; collision is resolved with double hashing,
    32:   // which is why efficient deletion is impossible
    33:   Entry *hashTable;
    34: 
    35:   // number of (allocated) slots in the hash table; this is always a
    36:   // power of 2
    37:   int tableSize;
    38: 
    39:   // tableSize always equals 1 << tableSizeBits
    40:   int tableSizeBits;
    41: 
    42:   // number of mappings (i.e. key!=NULL); always numEntries < tableSize
    43:   int numEntries;
    44: 
    45:   // number of outstanding iterators; used to check that we don't
    46:   // modify the table while one is active (experimental)
    47:   mutable int iterators;
    48: 
    49: public:      // data
    50:   // total # of lookups
    51:   static int lookups;
    52: 
    53:   // total # of entries examined during lookups; perfect hashing
    54:   // would yield lookups==probes
    55:   static int probes;
    56: 
    57: private:     // funcs
    58:   // 'bits' becomes tableSizeBits; also set hashTable and tableSize
    59:   void alloc(int bits);
    60: 
    61:   // multiplicative hash function
    62:   inline unsigned hashFunc(unsigned multiplier, unsigned key) const;
    63: 
    64:   // return the first entry in key's probe sequence that has either
    65:   // a NULL key or a key equal to 'key'
    66:   Entry &findEntry(void const *key) const;
    67: 
    68:   // make the table twice as big, and move all the entries into
    69:   // that new table
    70:   void expand();
    71: 
    72:   // disallowed
    73:   VoidPtrMap(VoidPtrMap &obj);
    74:   void operator=(VoidPtrMap &obj);
    75:   void operator==(VoidPtrMap &obj);
    76: 
    77: public:      // funcs
    78:   VoidPtrMap();              // empty map
    79:   ~VoidPtrMap();
    80: 
    81:   // return # of mapped entries
    82:   int getNumEntries() const { return numEntries; }
    83: 
    84:   // if this key has a mapping, return it; otherwise, return NULL
    85:   void *get(void const *key) const { return findEntry(key).value; }
    86: 
    87:   // add a mapping from 'key' to 'value'; replaces existing
    88:   // mapping, if any
    89:   void add(void *key, void *value);
    90: 
    91:   // remove all mappings
    92:   void empty();
    93: 
    94: 
    95: public:      // iterators
    96:   // iterate over all stored values in a VoidPtrMap
    97:   // NOTE: you can't change the table while an iter exists
    98:   class Iter {
    99:   private:      // data
   100:     VoidPtrMap const ↦       // table we're iterating over
   101:     int index;                   // current slot to return in adv(); -1 when done
   102: 
   103:   public:       // funcs
   104:     Iter(VoidPtrMap const &map);
   105:     ~Iter();
   106: 
   107:     bool isDone() const { return index < 0; }
   108:     void adv();            // must not be isDone()
   109: 
   110:     // return information about the currently-referenced table entry
   111:     void *key() const      // key (never NULL)
   112:       { return map.hashTable[index].key; }
   113:     void *value() const    // associated value
   114:       { return map.hashTable[index].value; }
   115:   };
   116:   friend class Iter;
   117: };
   118: 
   119: 
   120: #endif // VPTRMAP_H
End C section to elk/sm_vptrmap.h[1]
Start C section to elk/sm_warn.h[1 /1 ]
     1: #line 7309 "./lpsrc/sm.pak"
     2: // warn.h            see license.txt for copyright and terms of use
     3: // module to facilitate providing operational warnings to the user
     4: // Scott McPeak, 1999  This file is public domain.
     5: 
     6: #ifndef __WARN_H
     7: #define __WARN_H
     8: 
     9: // note: In retrospect, this module was either a bad idea, or I didn't
    10: //       implement it well.  Either way, don't use it for anything new.
    11: 
    12: // non-disjoint warning classification scheme
    13: // (add more classes as necessary)
    14: enum WarnLevel {
    15:   WARN_PERFORMANCE     = 0x01,
    16:     // may cause suboptimal performance
    17: 
    18:   WARN_SECURITY        = 0x02,
    19:     // possible compromise of private data, unauthrorized
    20:     // access, authentication warning, etc.
    21: 
    22:   WARN_COMPATIBILITY   = 0x04,
    23:     // interoperability with other software (including
    24:     // different versions of this software) may be
    25:     // adversely affected
    26: 
    27:   WARN_DEBUG           = 0x08,
    28:     // of use during debugging only; setting this flag means
    29:     // the warning handler should alert an attached debugger
    30: 
    31:   WARN_INFORMATION     = 0x10,
    32:     // I'm not sure when/why this would be used...
    33:     // Note: This is *not* to be used as a diagnostic 'printf'.
    34: 
    35:   WARN_ALL             = 0x1F,
    36:     // logical-or of all flags
    37: 
    38:   WARN_NONE            = 0x0,
    39:     // no warnings
    40: };
    41: 
    42: 
    43: // user interface
    44: // --------------
    45: // call this to report a warning
    46: //   level   - logical-or of applicable conditions
    47: //   message - user-intelligible message (should *not* contain a newline)
    48: void warning(WarnLevel level, char const *message);
    49: 
    50: 
    51: // handler interface
    52: // -----------------
    53: // the warning() function calls warningHandler(), so new
    54: // handlers are installed by changing that value
    55: typedef void (*WarningHandler)(WarnLevel level, char const *message);
    56: extern WarningHandler warningHandler;
    57: 
    58: 
    59: // default handler
    60: // ---------------
    61: // the default warning handler masks the input level with two
    62: // global variables:
    63: //   logWarnLevel - messages are written to a log file, "warning.log"
    64: //   displayWarnLevel - messages are written to stderr via stdio 'stderr'
    65: extern WarnLevel logWarnLevel;       // default: WARN_ALL, minus WARN_DEBUG ifdef NDEBUG
    66: extern WarnLevel displayWarnLevel;   // default: ifdef NDEBUG, WARN_NONE, else WARN_ALL
    67: 
    68: // handler functions (handler dispatches to logger and printer)
    69: void defaultWarningHandler(WarnLevel level, char const *message);
    70: void defaultWarningLogger(WarnLevel level, char const *message);
    71: void defaultWarningPrinter(WarnLevel level, char const *message);
    72: 
    73: 
    74: #endif // __WARN_H
    75: 
End C section to elk/sm_warn.h[1]
Start C section to elk/sm_xassert.h[1 /1 ]
     1: #line 7385 "./lpsrc/sm.pak"
     2: // xassert.h            see license.txt for copyright and terms of use
     3: // replacement for assert that throws an exception on failure
     4: // (x_assert_fail is defined in exc.cpp)
     5: // Scott McPeak, 1997-1998  This file is public domain.
     6: 
     7: #ifndef XASSERT_H
     8: #define XASSERT_H
     9: 
    10: #include "sm_macros.h"
    11: 
    12: // linkdepend: exc.cpp
    13: 
    14: void x_assert_fail(char const *cond, char const *file, int line) NORETURN;
    15: 
    16: // Ordinary 'xassert' *can* be turned off, but the nominal intent
    17: // is that it be left on, under the "ship what you test" theory.
    18: // I advocate using NDEBUG_NO_ASSERTIONS only as a way to gauge the
    19: // performance impact of the existing assertions.
    20: #if !defined(NDEBUG_NO_ASSERTIONS)
    21:   #define xassert(cond) \
    22:     ((cond)? (void)0 : x_assert_fail(#cond, __FILE__, __LINE__))
    23: #else
    24:   #define xassert(cond) ((void)0)
    25: #endif
    26: 
    27: // Here's a version which will turn off with ordinary NDEBUG.  It
    28: // is for more expensive checks that need not ship.
    29: #if !defined(NDEBUG)
    30:   #define xassertdb(cond) xassert(cond)
    31: #else
    32:   #define xassertdb(cond) ((void)0)
    33: #endif
    34: 
    35: // call when state is known to be bad; will *not* return
    36: #define xfailure(why) x_assert_fail(why, __FILE__, __LINE__)
    37: 
    38: 
    39: // Quick note: one prominent book on writing code recommends that
    40: // assertions *not* include the failure condition, since the file
    41: // and line number are sufficient, and the condition sm_string uses
    42: // memory.  The problem is that sometimes a compiled binary is
    43: // out of date w/respect to the code, and line numbers move, so
    44: // the condition sm_string provides a good way to find the right
    45: // assertion.
    46: 
    47: 
    48: /*
    49:   Why throw an exception after an assertion?
    50: 
    51:   The standard assert() calls abort() after printing its message.
    52:   This is like throwing an exception all the way to the calling
    53:   process.  This is fine if programs are small.
    54: 
    55:   But when a program is large enough, it may contain subsystems at
    56:   several layers, such that a higher level module is capable of
    57:   recovering from the failure of a lower level module.  Using abort(),
    58:   one would have to resort to catching signals, which is messy.
    59: 
    60:   An exception is much nicer to catch, and has the added benefit that
    61:   intermediate layers can catch and rethrow, appending little bits of
    62:   context, if they want to make the message more informative.
    63: 
    64:   In most of my programs, the 'x_assert' exception is only caught in
    65:   main() (implicitly, by catching 'xBase'), and hence 'xassert' acts
    66:   very much like 'assert'.  But by using 'xassert' consistenty, any
    67:   time I *do* have a large program with recovery, all the lower-level
    68:   modules are all ready to cooperate.
    69: 
    70:   Speaking of recovery: Be aware that when a module fails an
    71:   assertion, its internal state is most likely inconsistent.  Recovery
    72:   actions need to be fairly conservative about what code gets
    73:   re-entered and state re-used after a failure.  This is no different
    74:   than with 'assert', as a program could have inconsistent state *on
    75:   disk* that gets reactivated upon being restarted, but persistent
    76:   (across process boundaries) inconsistent state is simply less
    77:   common.
    78: 
    79: */
    80: 
    81: #endif // XASSERT_H
    82: 
End C section to elk/sm_xassert.h[1]
Start C section to elk/sm_xobjlist.h[1 /1 ]
     1: #line 7468 "./lpsrc/sm.pak"
     2: m4_dnl // xobjlist.h            see license.txt for copyright and terms of use
     3: m4_dnl // template file to be processed with m4 to generate one
     4: m4_dnl // of the wrappers around VoidList
     5: m4_dnl
     6: m4_changequote([, ])m4_dnl      // for this section
     7: m4_changecom[]m4_dnl            // no m4 "comments"
     8: m4_ifelse(m4_output, sobjlist.h, [m4_dnl
     9: // sobjlist.h
    10: // serf list of arbitrary objects
    11: m4_define(makeName, S[$1])m4_dnl
    12: m4_define(outputCond, [$1])m4_dnl       // select 1st arg
    13: m4_define(SPC, [])m4_dnl
    14: ], [m4_dnl
    15: // objlist.h
    16: // owner list of arbitrary dynamically-allocated objects
    17: m4_define(makeName, [$1])m4_dnl
    18: m4_define(outputCond, [$2])m4_dnl       // select 2nd arg
    19: m4_define(SPC, [ ])m4_dnl               // for balancing lined-up comments
    20: ])m4_dnl
    21: m4_define(includeLatch, makeName(OBJLIST_H))m4_dnl
    22: m4_define(className, makeName(ObjList))m4_dnl
    23: m4_define(iterName, makeName(ObjListIter))m4_dnl
    24: m4_define(mutatorName, makeName(ObjListMutator))m4_dnl
    25: m4_define(iterNameNC, makeName(ObjListIterNC))m4_dnl
    26: m4_define(multiIterName, makeName(ObjListMultiIter))m4_dnl
    27: m4_changequote(, )m4_dnl              // so quotes are not quoted..
    28: m4_changequote([[[, ]]])m4_dnl        // reduce likelihood of confusion
    29: // NOTE: automatically generated from xobjlist.h -- do not edit directly
    30: 
    31: // Author: Scott McPeak, 2000
    32: 
    33: #ifndef includeLatch
    34: #define includeLatch
    35: 
    36: #include "sm_voidlist.h"
    37: 
    38: 
    39: // forward declarations of template classes, so we can befriend them in className
    40: // (not required by Borland C++ 4.5, but GNU wants it...)
    41: template <class T> class iterName;
    42: template <class T> class mutatorName;
    43: template <class T> class iterNameNC;
    44: 
    45: 
    46: outputCond([[[m4_dnl      // sobjlist
    47: // the list is considered to not own any of the items; it's ok to
    48: // insert items multiple times or into multiple lists
    49: ]]], [[[m4_dnl            // objlist
    50: // the list is considered to own all of the items; it is an error to insert
    51: // an item into more than one such list, or to insert an item more than once
    52: // into any such list
    53: ]]])m4_dnl
    54: template <class T>
    55: class className {
    56: private:
    57:   friend class iterName<T>;
    58:   friend class mutatorName<T>;
    59:   friend class iterNameNC<T>;
    60: 
    61: protected:
    62:   VoidList list;                        // list itself
    63: 
    64: outputCond([[[m4_dnl    // sobjlist
    65: public:
    66:   // make shallow copies
    67:   className[[[]]](className const &obj)         : list(obj.list) {}
    68:   className& operator= (className const &src)         { list = src.list; return *this; }
    69: ]]], [[[m4_dnl          // objlist
    70: private:
    71:   // this is an owner list; these are not allowed
    72:   className[[[]]](className const &obj);
    73:   className& operator= (className const &src);
    74: ]]])m4_dnl
    75: 
    76: public:
    77:   className[[[]]]()                            : list() {}
    78:   ~className[[[]]]()                      m4_dnl
    79:      outputCond({}    /* all items removed */, { deleteAll(); })
    80: 
    81:   // The difference function should return <0 if left should come before
    82:   // right, 0 if they are equivalent, and >0 if right should come before
    83:   // left.  For example, if we are sorting numbers into ascending order,
    84:   // then 'diff' would simply be subtraction.
    85:   typedef int (*Diff)(T const *left, T const *right, void *extra);
    86: 
    87:   // selectors
    88:   int count() const                     { return list.count(); }
    89:   bool isEmpty() const                  { return list.isEmpty(); }
    90:   bool isNotEmpty() const               { return list.isNotEmpty(); }
    91:   T *nth(int which)                     { return (T*)list.nth(which); }
    92:   T const *nthC(int which) const        { return (T const*)list.nth(which); }
    93:   T *first()                            { return (T*)list.first(); }
    94:   T const *firstC() const               { return (T const*)list.first(); }
    95:   T *last()                             { return (T*)list.last(); }
    96:   T const *lastC() const                { return (T const*)list.last(); }
    97: 
    98:   // insertion
    99:   void prepend(T *newitem)              { list.prepend((void*)newitem); }
   100:   void append(T *newitem)               { list.append((void*)newitem); }
   101:   void insertAt(T *newitem, int index)  { list.insertAt((void*)newitem, index); }
   102:   void insertSorted(T *newitem, Diff diff, void *extra=NULL)
   103:     { list.insertSorted((void*)newitem, (VoidDiff)diff, extra); }
   104: 
   105:   // removal
   106:   T *removeAt(int index)                { return (T*)list.removeAt(index); }
   107:   T *removeFirst()                      { return (T*)list.removeFirst(); }
   108: outputCond([[[m4_dnl     // sobjlist
   109:   void removeAll()                      { list.removeAll(); }
   110: ]]], [[[m4_dnl           // objlist
   111:   void deleteAt(int index)              { delete (T*)list.removeAt(index); }
   112:   void deleteAll();
   113: ]]])m4_dnl
   114: 
   115:   // list-as-set: selectors
   116:   int indexOf(T const *item) const      { return list.indexOf((void*)item); }
   117:   int indexOfF(void *item) const        { return list.indexOfF((void*)item); }
   118:   bool contains(T const *item) const    { return list.contains((void*)item); }
   119: 
   120:   // list-as-set: mutators
   121:   bool prependUnique(T *newitem)        { return list.prependUnique((void*)newitem); }
   122:   bool appendUnique(T *newitem)         { return list.appendUnique((void*)newitem); }
   123:   void removeItem(T const *item)        { list.removeItem((void*)item); }    // whether the arg should be const is debatable..
   124:   bool removeIfPresent(T const *item)   { return list.removeIfPresent((void*)item); }
   125: 
   126:   // complex modifiers
   127:   void reverse()                                    { list.reverse(); }
   128:   void insertionSort(Diff diff, void *extra=NULL)   { list.insertionSort((VoidDiff)diff, extra); }
   129:   void mergeSort(Diff diff, void *extra=NULL)       { list.mergeSort((VoidDiff)diff, extra); }
   130: 
   131:   // and a related test
   132:   bool isSorted(Diff diff, void *extra=NULL) const  { return list.isSorted((VoidDiff)diff, extra); }
   133: 
   134:   // multiple lists
   135:   void concat(className &tail)                       { list.concat(tail.list); }
   136: outputCond([[[m4_dnl    // sobjlist
   137:   void appendAll(className const &tail)              { list.appendAll(tail.list); }
   138: ]]], [[[m4_dnl          // objlist
   139:   // (we do *not* have appendAll, since these are supposed to be owner lists)
   140: ]]])m4_dnl
   141: 
   142:   // steal
   143:   void stealTailAt(int index, className &tail)       { list.stealTailAt(index, tail.list); }
   144: 
   145:   // equal items in equal positions
   146:   bool equalAsLists(className const &otherList, Diff diff, void *extra=NULL) const
   147:     { return list.equalAsLists(otherList.list, (VoidDiff)diff, extra); }
   148:   int compareAsLists(className const &otherList, Diff diff, void *extra=NULL) const
   149:     { return list.compareAsLists(otherList.list, (VoidDiff)diff, extra); }
   150: 
   151:   // last-as-set: comparisons (NOT efficient)
   152:   bool equalAsSets(className const &otherList, Diff diff, void *extra=NULL) const
   153:     { return list.equalAsSets(otherList.list, (VoidDiff)diff, extra); }
   154:   bool isSubsetOf(className const &otherList, Diff diff, void *extra=NULL) const
   155:     { return list.isSubsetOf(otherList.list, (VoidDiff)diff, extra); }
   156:   bool containsByDiff(T const *item, Diff diff, void *extra=NULL) const
   157:     { return list.containsByDiff((void*)item, (VoidDiff)diff, extra); }
   158: 
   159:   // treating the pointer values themselves as the basis for comparison
   160:   bool equalAsPointerLists(className const &otherList) const
   161:     { return list.equalAsPointerLists(otherList.list); }
   162:   bool equalAsPointerSets(className const &otherList) const
   163:     { return list.equalAsPointerSets(otherList.list); }
   164: 
   165: outputCond([[[m4_dnl    // sobjlist
   166:   // debugging: no invariants beyond VoidList
   167:   void selfCheck() const                { list.selfCheck(); }
   168: 
   169:   // but export the additional checks for cases where they apply anyway
   170:   void checkHeapDataPtrs() const        { list.checkHeapDataPtrs(); }
   171:   void checkUniqueDataPtrs() const      { list.checkUniqueDataPtrs(); }
   172: ]]], [[[m4_dnl          // objlist
   173:   // debugging: two additional invariants
   174:   void selfCheck() const {
   175:     list.selfCheck();
   176:     list.checkHeapDataPtrs();
   177:     list.checkUniqueDataPtrs();
   178:   }
   179: ]]])m4_dnl
   180: };
   181: 
   182: 
   183: outputCond(, [[[m4_dnl      // objlist
   184: template <class T>
   185: void ObjList<T>::deleteAll()
   186: {
   187:   while (!list.isEmpty()) {
   188:     deleteAt(0);
   189:   }
   190: }
   191: 
   192: 
   193: ]]])m4_dnl
   194: // for traversing the list and modifying it (nodes and/or structure)
   195: // NOTE: no list-modification fns should be called on 'list' while this
   196: //       iterator exists, and only one such iterator should exist for
   197: //       any given list
   198: template <class T>
   199: class mutatorName {
   200:   friend class iterName<T>;
   201: 
   202: protected:
   203:   VoidListMutator mut;       // underlying mutator
   204: 
   205: public:
   206:   mutatorName[[[]]](className<T> &lst)     : mut(lst.list) { reset(); }
   207:   ~mutatorName[[[]]]()                    {}
   208: 
   209:   void reset()                          { mut.reset(); }
   210: 
   211:   // iterator copying; safe *only* until one of the mutators modifies
   212:   // the list structure (by inserting or removing), at which time all
   213:   // other iterators might be in limbo
   214:   mutatorName[[[]]](mutatorName const &obj)             : mut(obj.mut) {}
   215:   mutatorName& operator=(mutatorName const &obj)  { mut = obj.mut;  return *this; }
   216:     // requires that 'this' and 'obj' already refer to the same 'list'
   217: 
   218:   // iterator actions
   219:   bool isDone() const                   { return mut.isDone(); }
   220:   void adv()                            { mut.adv(); }
   221:   T *data()                             { return (T*)mut.data(); }
   222:   T *&dataRef()                         { return (T*&)mut.dataRef(); }
   223: 
   224:   // insertion
   225:   void insertBefore(T *item)            { mut.insertBefore((void*)item); }
   226:     // 'item' becomes the new 'current', and the current 'current' is
   227:     // pushed forward (so the next adv() will make it current again)
   228: 
   229:   void insertAfter(T *item)             { mut.insertAfter((void*)item); }
   230:     // 'item' becomes what we reach with the next adv();
   231:     // isDone() must be false
   232: 
   233:   void append(T *item)                  { mut.append((void*)item); }
   234:     // only valid while isDone() is true, it inserts 'item' at the end of
   235:     // the list, and advances such that isDone() remains true; equivalent
   236:     // to { xassert(isDone()); insertBefore(item); adv(); }
   237: 
   238:   // removal
   239:   T *remove()                           { return (T*)mut.remove(); }
   240:     // 'current' is removed from the list and returned, and whatever was
   241:     // next becomes the new 'current'
   242: 
   243: outputCond(, [[[m4_dnl    // sobjlist
   244:   void deleteIt()                       { delete (T*)mut.remove(); }
   245:     // same as remove(), except item is deleted also
   246: 
   247: ]]])m4_dnl
   248:   // debugging
   249:   void selfCheck() const                { mut.selfCheck(); }
   250: };
   251: 
   252: #define makeName(MUTATE_EACH_OBJLIST)(T, list, iter) \
   253:   for(mutatorName< T > iter(list); !iter.isDone(); iter.adv())
   254: 
   255: 
   256: // for traversing the list without modifying it (neither nodes nor structure)
   257: // NOTE: no list-modification fns should be called on 'list' while this
   258: //       iterator exists
   259: template <class T>
   260: class iterName {
   261: protected:
   262:   VoidListIter iter;      // underlying iterator
   263: 
   264: public:
   265:   iterName[[[]]](className<T> const &list) : iter(list.list) {}
   266:   iterName[[[]]](className<T> const &list, int pos) : iter(list.list, pos) {}
   267:   ~iterName[[[]]]()                       {}
   268: 
   269:   void reset(className<T> const &list)   { iter.reset(list.list); }
   270: 
   271:   // iterator copying; generally safe
   272:   iterName[[[]]](iterName const &obj)             : iter(obj.iter) {}
   273:   iterName& operator=(iterName const &obj)  { iter = obj.iter;  return *this; }
   274: 
   275:   // but copying from a mutator is less safe; see above
   276:   iterName[[[]]](mutatorName<T> &obj)             : iter(obj.mut) {}
   277: 
   278:   // iterator actions
   279:   bool isDone() const                   { return iter.isDone(); }
   280:   void adv()                            { iter.adv(); }
   281:   T const *data() const                 { return (T const*)iter.data(); }
   282: };
   283: 
   284: #define makeName(FOREACH_OBJLIST)(T, list, iter) \
   285:   for(iterName< T > iter(list); !iter.isDone(); iter.adv())
   286: 
   287: 
   288: // intermediate to the above two, this allows modification of the
   289: // objects stored on the list, but not the identity or order of
   290: // the objects in the list
   291: template <class T>
   292: class iterNameNC {
   293: protected:
   294:   VoidListIter iter;      // underlying iterator
   295: 
   296: public:
   297:   iterNameNC[[[]]](className<T> &list) : iter(list.list) {}
   298:   iterNameNC[[[]]](className<T> &list, int pos) : iter(list.list, pos) {}
   299:   ~iterNameNC[[[]]]()                     {}
   300: 
   301:   void reset(className<T> &list)         { iter.reset(list.list); }
   302: 
   303:   // iterator copying; generally safe
   304:   iterNameNC[[[]]](iterNameNC const &obj)             : iter(obj.iter) {}
   305:   iterNameNC& operator=(iterNameNC const &obj)  { iter = obj.iter;  return *this; }
   306: 
   307:   // but copying from a mutator is less safe; see above
   308:   iterNameNC[[[]]](mutatorName<T> &obj)               : iter(obj.mut) {}
   309: 
   310:   // iterator actions
   311:   bool isDone() const                   { return iter.isDone(); }
   312:   void adv()                            { iter.adv(); }
   313:   T *data() const                       { return (T*)iter.data(); }
   314: };
   315: 
   316: #define makeName(FOREACH_OBJLIST_NC)(T, list, iter) \
   317:   for(iterNameNC< T > iter(list); !iter.isDone(); iter.adv())
   318: 
   319: 
   320: // iterate over the combined elements of two or more lists
   321: template <class T>
   322: class multiIterName {
   323: private:
   324:   // all the lists
   325:   className<T> **lists;               SPC// serf array of serf list pointers
   326:   int numLists;                      // length of this array
   327: 
   328:   // current element
   329:   int curList;                       // which list we're working on
   330:   iterName<T> iter;              SPC// current element of that list
   331: 
   332:   // invariant:
   333:   //   either curList==numLists, or
   334:   //   iter is not 'done'
   335: 
   336: public:
   337:   multiIterName[[[]]](className<T> **L, int n)
   338:     : lists(L),
   339:       numLists(n),
   340:       curList(0),
   341:       iter(*(lists[0]))
   342:   {
   343:     xassert(n > 0);
   344:     normalize();
   345:   }
   346: 
   347:   // advance the iterator to the next element of the next non-empty list;
   348:   // establishes invariant above
   349:   void normalize();
   350: 
   351:   bool isDone() const {
   352:     return curList == numLists;
   353:   }
   354: 
   355:   T const *data() const {
   356:     return iter.data();
   357:   }
   358: 
   359:   void adv() {
   360:     iter.adv();
   361:     normalize();
   362:   }
   363: };
   364: 
   365: // this was originally inline, but that was causing some strange
   366: // problems (compiler bug?)
   367: template <class T>
   368: void multiIterName<T>::normalize()
   369: {
   370:   while (iter.isDone() && curList < numLists) {
   371:     curList++;
   372:     if (curList < numLists) {
   373:       iter.reset(*(lists[curList]));
   374:     }
   375:   }
   376: }
   377: 
   378: 
   379: #endif // includeLatch
End C section to elk/sm_xobjlist.h[1]
Start cpp section to elk/sm_autofile.cpp[1 /1 ]
     1: #line 7848 "./lpsrc/sm.pak"
     2: // autofile.cc            see license.txt for copyright and terms of use
     3: // code for autofile.h
     4: 
     5: #include "sm_autofile.h"
     6: #include "sm_exc.h"
     7: 
     8: 
     9: FILE *xfopen(char const *fname, char const *mode)
    10: {
    11:   FILE *ret = fopen(fname, mode);
    12:   if (!ret) {
    13:     throw_XOpen(fname);
    14:   }
    15: 
    16:   return ret;
    17: }
    18: 
    19: 
    20: AutoFILE::AutoFILE(char const *fname, char const *mode)
    21:   : AutoFclose(xfopen(fname, mode))
    22: {}
    23: 
    24: AutoFILE::~AutoFILE()
    25: {
    26:   // ~AutoFclose closes the file
    27: }
    28: 
    29: 
    30: // EOF
End cpp section to elk/sm_autofile.cpp[1]
Start cpp section to elk/sm_bflatten.cpp[1 /1 ]
     1: #line 7879 "./lpsrc/sm.pak"
     2: // bflatten.cc            see license.txt for copyright and terms of use
     3: // code for bflatten.h
     4: 
     5: #include "sm_bflatten.h"
     6: #include "sm_exc.h"
     7: #include "sm_syserr.h"
     8: 
     9: 
    10: BFlatten::BFlatten(char const *fname, bool r)
    11:   : readMode(r),
    12:     ownerTable(!r? &BFlatten::getOwnerPtrKeyFn : &BFlatten::getIntNameKeyFn,
    13:                HashTable::lcprngHashFn,
    14:                HashTable::pointerEqualKeyFn),
    15:     nextUniqueName(1)
    16: {
    17:   fp = fopen(fname, readMode? "rb" : "wb");
    18:   if (!fp) {
    19:     throw_XOpen(fname);
    20:   }
    21: }
    22: 
    23: BFlatten::~BFlatten()
    24: {
    25:   fclose(fp);
    26: }
    27: 
    28: 
    29: STATICDEF void const* BFlatten::getOwnerPtrKeyFn(OwnerMapping *data)
    30: {
    31:   return data->ownerPtr;
    32: }
    33: 
    34: STATICDEF void const* BFlatten::getIntNameKeyFn(OwnerMapping *data)
    35: {
    36:   return (void const*)(data->intName);
    37: }
    38: 
    39: 
    40: void BFlatten::xferSimple(void *var, unsigned len)
    41: {
    42:   if (writing()) {
    43:     if (fwrite(var, 1, len, fp) < len) {
    44:       xsyserror("fwrite");
    45:     }
    46:   }
    47:   else {
    48:     if (fread(var, 1, len, fp) < len) {
    49:       xsyserror("fread");
    50:     }
    51:   }
    52: }
    53: 
    54: 
    55: void BFlatten::noteOwner(void *ownerPtr)
    56: {
    57:   // make a new mapping
    58:   OwnerMapping *map = new OwnerMapping;
    59:   map->ownerPtr = ownerPtr;
    60:   map->intName = nextUniqueName++;
    61: 
    62:   // add it to the table
    63:   if (writing()) {
    64:     // index by pointer
    65:     ownerTable.add(ownerPtr, map);
    66:   }
    67:   else {
    68:     // index by int name
    69:     ownerTable.add((void const*)(map->intName), map);
    70:   }
    71: }
    72: 
    73: 
    74: void BFlatten::xferSerf(void *&serfPtr, bool nullable)
    75: {
    76:   if (writing()) {
    77:     xassert(nullable || serfPtr!=NULL);
    78: 
    79:     if (serfPtr == NULL) {
    80:       // encode as 0; the names start with 1
    81:       writeInt(0);
    82:     }
    83:     else {
    84:       // lookup the mapping
    85:       OwnerMapping *map = ownerTable.get(serfPtr);
    86: 
    87:       // we must have already written the owner pointer
    88:       xassert(map != NULL);
    89: 
    90:       // write the int name
    91:       writeInt(map->intName);
    92:     }
    93:   }
    94:   else /*reading*/ {
    95:     // read the int name
    96:     int name = readInt();
    97: 
    98:     if (name == 0) {      // null
    99:       xassert(nullable);
   100:       serfPtr = NULL;
   101:     }
   102:     else {
   103:       // lookup the mapping
   104:       OwnerMapping *map = ownerTable.get((void const*)name);
   105:       formatAssert(map != NULL);
   106: 
   107:       // return the pointer
   108:       serfPtr = map->ownerPtr;
   109:     }
   110:   }
   111: }
   112: 
   113: 
   114: // ------------------------ test code ---------------------
   115: #ifdef TEST_BFLATTEN
   116: 
   117: #include "sm_test.h"
   118: 
   119: void entry()
   120: {
   121:   // make up some data
   122:   int x = 9, y = 22;
   123:   sm_string s("foo bar");
   124:   int *px = &x, *py = &y;
   125: 
   126:   // open a file for writing them
   127:   {
   128:     BFlatten flat("bflat.tmp", false /*reading*/);
   129:     flat.xferInt(x);
   130:     flat.noteOwner(&x);
   131:     s.xfer(flat);
   132:     flat.xferSerf((void*&)px);
   133:     flat.xferInt(y);
   134:     flat.noteOwner(&y);
   135:     flat.xferSerf((void*&)py);
   136:   }
   137: 
   138:   // place to put the data we read
   139:   int x2, y2;
   140:   sm_string s2;
   141:   int *px2, *py2;
   142: 
   143:   // read them back
   144:   {
   145:     BFlatten flat("bflat.tmp", true /*reading*/);
   146:     flat.xferInt(x2);
   147:     flat.noteOwner(&x2);
   148:     s2.xfer(flat);
   149:     flat.xferSerf((void*&)px2);
   150:     flat.xferInt(y2);
   151:     flat.noteOwner(&y2);
   152:     flat.xferSerf((void*&)py2);
   153:   }
   154: 
   155:   // compare
   156:   xassert(x == x2);
   157:   xassert(y == y2);
   158:   xassert(s.equals(s2));
   159:   xassert(px2 == &x2);
   160:   xassert(py2 == &y2);
   161: 
   162:   // delete the temp file
   163:   remove("bflat.tmp");
   164: 
   165:   printf("bflatten works\n");
   166: }
   167: 
   168: 
   169: USUAL_MAIN
   170: 
   171: 
   172: #endif // TEST_BFLATTEN
End cpp section to elk/sm_bflatten.cpp[1]
Start cpp section to elk/sm_bit2d.cpp[1 /1 ]
     1: #line 8052 "./lpsrc/sm.pak"
     2: // bit2d.cc            see license.txt for copyright and terms of use
     3: // code for bit2d.h
     4: 
     5: #include "sm_bit2d.h"
     6: #include "sm_xassert.h"
     7: #include "sm_flatten.h"
     8: 
     9: #include <cstring>     // std::memset, std::memcpy
    10: #include <stdio.h>      // printf
    11: 
    12: 
    13: Bit2d::Bit2d(point const &aSize)
    14:   : owning(true),
    15:     size(aSize)
    16: {
    17:   xassert(size.x > 0 && size.y > 0);
    18:   stride = (size.x+7)/8;
    19:   data = new byte[datasize()];
    20: }
    21: 
    22: 
    23: Bit2d::~Bit2d()
    24: {
    25:   if (owning) {
    26:     delete data;
    27:   }
    28: }
    29: 
    30: 
    31: Bit2d::Bit2d(Bit2d const &obj)
    32: {
    33:   size = obj.size;
    34:   stride = obj.stride;
    35:   data = new byte[datasize()];
    36:   owning = true;
    37:   std::memcpy(data, obj.data, datasize());
    38: }
    39: 
    40: 
    41: Bit2d& Bit2d::operator= (Bit2d const &obj)
    42: {
    43:   if (this != &obj) {
    44:     xassert(size == obj.size);
    45:     std::memcpy(data, obj.data, datasize());
    46:   }
    47:   return *this;
    48: }
    49: 
    50: 
    51: bool Bit2d::operator== (Bit2d const &obj) const
    52: {
    53:   return (size == obj.size) &&
    54:          (0==std::memcmp(data, obj.data, datasize()));
    55: }
    56: 
    57: 
    58: Bit2d::Bit2d(Flatten &)
    59:   : data(NULL),
    60:     owning(true)
    61: {}
    62: 
    63: void Bit2d::xfer(Flatten &flat)
    64: {
    65:   flat.xferInt(size.x);
    66:   flat.xferInt(size.y);
    67:   flat.xferInt(stride);
    68: 
    69:   flat.xferHeapBuffer((void*&)data, datasize());
    70: }
    71: 
    72: 
    73: void Bit2d::setall(int val)
    74: {
    75:   std::memset(data, val? 0xFF : 0, datasize());
    76: }
    77: 
    78: 
    79: int Bit2d::get(point const &p) const
    80: {
    81:   xassert(okpt(p));
    82:   return ( *(byteptrc(p)) >> (p.x&7) ) & 1;
    83: }
    84: 
    85: void Bit2d::set(point const &p)
    86: {
    87:   xassert(okpt(p));
    88:   *(byteptr(p)) |= (byte)  ( 1 << (p.x&7) ) ;
    89: }
    90: 
    91: void Bit2d::reset(point const &p)
    92: {
    93:   xassert(okpt(p));
    94:   *(byteptr(p)) &= (byte)(~( 1 << (p.x&7) ));
    95: }
    96: 
    97: void Bit2d::setto(point const &p, int val)
    98: {
    99:   if (val) { set(p); }
   100:   else { reset(p); }
   101: }
   102: 
   103: int Bit2d::testAndSet(point const &p)
   104: {
   105:   byte *b = byteptr(p);
   106:   int ret = (*b >> (p.x&7)) & 1;
   107:   *b |= (byte)( 1 << (p.x&7) );
   108:   return ret;
   109: }
   110: 
   111: void Bit2d::toggle(point const &p)
   112: {
   113:   xassert(okpt(p));
   114:   *(byteptr(p)) ^= (byte) ( 1 << (p.x&7) );
   115: }
   116: 
   117: 
   118: // count the number of digits required to represent a positive
   119: // integer in base 10
   120: static int digits(int value)
   121: {
   122:   xassert(value > 0);
   123:   int ct=0;
   124:   while (value > 0) {
   125:     ct++;
   126:     value /= 10;
   127:   }
   128:   return ct;
   129: }
   130: 
   131: 
   132: /*
   133:  * Goal is to draw something like this:
   134:  *
   135:  *       1  2  3
   136:  *  1 [  0  0  0  ]
   137:  *  2 [  0  1  1  ]
   138:  *  3 [  0  1  0  ]
   139:  *
   140:  */
   141: void Bit2d::print() const
   142: {
   143:   // compute column widths
   144:   int rowLabelWidth = digits(size.y-1);
   145:   int colLabelWidth = digits(size.x-1);
   146: 
   147:   // column legend
   148:   printf("%*s   ", rowLabelWidth, "");
   149:   loopi(size.x) {
   150:     printf("%*d ", colLabelWidth, i);
   151:   }
   152:   printf("\n");
   153: 
   154:   for (int row=0; row<size.y; row++) {
   155:     printf("%*d [ ", rowLabelWidth, row);
   156:     loopi(size.x) {
   157:       printf("%*s ", colLabelWidth,
   158:                      get(point(i, row))? "1" : ".");    // "." so easier to see patterns
   159:     }
   160:     printf("]\n");
   161:   }
   162: }
   163: 
   164: 
   165: // hack
   166: Bit2d::Bit2d(byte * /*serf*/ d, point const &sz, int str)
   167:   : data(d),
   168:     owning(false),    // since it's a serf ptr
   169:     size(sz),
   170:     stride(str)
   171: {}
   172: 
   173: 
   174: 
   175: // ------------------------ test code ------------------------
   176: #ifdef TEST_BIT2D
   177: 
   178: #include "sm_bflatten.h"
   179: 
   180: int main()
   181: {
   182:   Bit2d bits(point(17,3));
   183:   xassert(bits.okpt(point(16,2)) &&
   184:          !bits.okpt(point(17,3)) &&
   185:          !bits.okpt(point(2,16)));
   186: 
   187:   bits.setall(0);
   188:   xassert(!bits.testAndSet(point(9,1)));
   189:   xassert(bits.testAndSet(point(9,1)));
   190: 
   191:   xassert(!bits.testAndSet(point(2,0)));
   192:   xassert(bits.testAndSet(point(2,0)));
   193: 
   194:   xassert(!bits.testAndSet(point(16,2)));
   195:   xassert(bits.testAndSet(point(16,2)));
   196: 
   197:   bits.toggle(point(3,2));
   198:   xassert(bits.get(point(3,2)));
   199: 
   200:   bits.print();
   201: 
   202:   // test read/write
   203:   Bit2d *another = writeThenRead(bits);
   204:   xassert(*another == bits);
   205:   delete another;
   206: 
   207:   printf("bit2d works\n");
   208: 
   209:   return 0;
   210: }
   211: 
   212: #endif // TEST_BIT2D
   213: 
End cpp section to elk/sm_bit2d.cpp[1]
Start cpp section to elk/sm_bitarray.cpp[1 /1 ]
     1: #line 8266 "./lpsrc/sm.pak"
     2: // bitarray.cc            see license.txt for copyright and terms of use
     3: // code for bitarray.h
     4: 
     5: #include "sm_bitarray.h"
     6: #include "sm_flatten.h"
     7: 
     8: #include <cstring>       // std::memset
     9: 
    10: 
    11: BitArray::BitArray(int n)
    12:   : numBits(n)
    13: {
    14:   bits = new unsigned char[allocdBytes()];
    15:   clearAll();
    16: }
    17: 
    18: 
    19: BitArray::~BitArray()
    20: {
    21:   delete[] bits;
    22: }
    23: 
    24: 
    25: BitArray::BitArray(Flatten&)
    26:   : bits(NULL)
    27: {}
    28: 
    29: void BitArray::xfer(Flatten &flat)
    30: {
    31:   flat.xferInt(numBits);
    32: 
    33:   if (flat.reading()) {
    34:     bits = new unsigned char[allocdBytes()];
    35:   }
    36:   flat.xferSimple(bits, allocdBytes());
    37: }
    38: 
    39: 
    40: void BitArray::clearAll()
    41: {
    42:   std::memset(bits, 0, allocdBytes());
    43: }
    44: 
End cpp section to elk/sm_bitarray.cpp[1]
Start cpp section to elk/sm_boxprint.cpp[1 /1 ]
     1: #line 8311 "./lpsrc/sm.pak"
     2: // boxprint.cc
     3: // code for boxprint.h
     4: 
     5: #include "sm_boxprint.h"
     6: #include "sm_strutil.h"
     7: 
     8: #include <cstring>         // std::strlen
     9: 
    10: 
    11: // ----------------------- BPRender ----------------------
    12: BPRender::BPRender()
    13:   : sb(),         // initially empty
    14:     margin(72),
    15:     curCol(0),
    16:     lineStartText("")
    17: {}
    18: 
    19: BPRender::~BPRender()
    20: {}
    21: 
    22: 
    23: void BPRender::reset()
    24: {
    25:   sb.clear();
    26:   sb << lineStartText;
    27: }
    28: 
    29: 
    30: void BPRender::add(char const *text)
    31: {
    32:   int len = std::strlen(text);
    33:   sb << text;
    34:   curCol += len;
    35: }
    36: 
    37: void BPRender::breakLine(int ind)
    38: {
    39:   sb << "\n" << lineStartText;
    40: 
    41:   for (int i=0; i < ind; i++) {
    42:     sb << ' ';
    43:   }
    44: 
    45:   curCol = ind;
    46: }
    47: 
    48: 
    49: sm_string BPRender::takeAndRender(BoxPrint &bld)
    50: {
    51:   BPBox* /*owner*/ tree = bld.takeTree();
    52:   tree->render(*this);
    53:   sm_string ret(sb);
    54:   sb.clear();
    55:   delete tree;
    56:   return ret;
    57: }
    58: 
    59: 
    60: // ----------------------- BPElement ---------------------
    61: bool BPElement::isBreak() const
    62: {
    63:   return false;
    64: }
    65: 
    66: BPElement::~BPElement()
    67: {}
    68: 
    69: 
    70: // ------------------------- BPText ----------------------
    71: BPText::BPText(char const *t)
    72:   : text(t)
    73: {}
    74: 
    75: BPText::~BPText()
    76: {}
    77: 
    78: 
    79: int BPText::oneLineWidth()
    80: {
    81:   return text.length();
    82: }
    83: 
    84: void BPText::render(BPRender &mgr)
    85: {
    86:   mgr.add(text);
    87: }
    88: 
    89: 
    90: void BPText::debugPrint(std::ostream &os, int /*ind*/) const
    91: {
    92:   os << "text(" << quoted(text) << ")";
    93: }
    94: 
    95: 
    96: // ------------------------ BPBreak ---------------------
    97: BPBreak::BPBreak(bool e, int i)
    98:   : enabled(e),
    99:     indent(i)
   100: {}
   101: 
   102: BPBreak::~BPBreak()
   103: {}
   104: 
   105: int BPBreak::oneLineWidth()
   106: {
   107:   return 1;
   108: }
   109: 
   110: void BPBreak::render(BPRender &mgr)
   111: {
   112:   // if we're being asked to render, then this break must not be taken
   113:   mgr.add(" ");
   114: }
   115: 
   116: bool BPBreak::isBreak() const
   117: {
   118:   return enabled;
   119: }
   120: 
   121: void BPBreak::debugPrint(std::ostream &os, int /*ind*/) const
   122: {
   123:   os << "break(en=" << (int)enabled << ", ind=" << indent << ")";
   124: }
   125: 
   126: 
   127: // ------------------------- BPBox ------------------------
   128: BPBox::BPBox(BPKind k)
   129:   : elts(),      // initially empty
   130:     kind(k)
   131: {
   132:   xassert((unsigned)k < NUM_BPKINDS);
   133: }
   134: 
   135: BPBox::~BPBox()
   136: {}
   137: 
   138: 
   139: int BPBox::oneLineWidth()
   140: {
   141:   int sum = 0;
   142:   FOREACH_ASTLIST_NC(BPElement, elts, iter) {
   143:     sum += iter.data()->oneLineWidth();
   144:   }
   145:   return sum;
   146: }
   147: 
   148: 
   149: // this function is the heart of the rendering engine
   150: void BPBox::render(BPRender &mgr)
   151: {
   152:   int startCol = mgr.getCurCol();
   153: 
   154:   if (kind == BP_vertical ||
   155:       (kind == BP_correlated && oneLineWidth() > mgr.remainder())) {
   156:     // take all of the breaks
   157:     FOREACH_ASTLIST_NC(BPElement, elts, iter) {
   158:       BPElement *elt = iter.data();
   159:       if (elt->isBreak()) {
   160:         startCol += static_cast<BPBreak*>(elt)->indent;
   161:         mgr.breakLine(startCol);
   162:       }
   163:       else {
   164:         elt->render(mgr);
   165:       }
   166:     }
   167:     return;
   168:   }
   169: 
   170:   if (kind == BP_correlated) {
   171:     // if we got here, we're taking none of the breaks
   172:     FOREACH_ASTLIST_NC(BPElement, elts, iter) {
   173:       BPElement *elt = iter.data();
   174:       elt->render(mgr);
   175:     }
   176:     return;
   177:   }
   178: 
   179:   xassert(kind == BP_sequence);
   180: 
   181:   // this cursor points to the next element that has not been rendered
   182:   ASTListIterNC<BPElement> cursor(elts);
   183: 
   184:   // when not NULL, the cursor has just passed a break, but we haven't
   185:   // actually decided whether to take it or not
   186:   BPBreak *pendingBreak = NULL;
   187: 
   188:   while (!cursor.isDone()) {
   189:     // is there room for the elements up to the first break?
   190:     int segmentWidth = pendingBreak? 1 : 0;
   191:     ASTListIterNC<BPElement> lookahead(cursor);
   192:     while (!lookahead.isDone() && !lookahead.data()->isBreak()) {
   193:       segmentWidth += lookahead.data()->oneLineWidth();
   194:       lookahead.adv();
   195:     }
   196: 
   197:     if (pendingBreak && segmentWidth > mgr.remainder()) {
   198:       // take the pending break
   199:       startCol += pendingBreak->indent;
   200:       mgr.breakLine(startCol);
   201:       pendingBreak = NULL;
   202:     }
   203: 
   204:     // the segment will be put here without a preceding break
   205:     else if (pendingBreak) {
   206:       pendingBreak->render(mgr);
   207:       pendingBreak = NULL;
   208:     }
   209: 
   210:     xassert(pendingBreak == NULL);
   211: 
   212:     // render the segment
   213:     while (!cursor.isDone() && !cursor.data()->isBreak()) {
   214:       cursor.data()->render(mgr);
   215:       cursor.adv();
   216:     }
   217: 
   218:     if (!cursor.isDone()) {
   219:       // we stopped on a break
   220:       pendingBreak = static_cast<BPBreak*>(cursor.data());
   221:       cursor.adv();
   222:     }
   223:   }
   224: 
   225:   if (pendingBreak) {
   226:     // ended with a break.. strange, but harmless I suppose
   227:     pendingBreak->render(mgr);
   228:   }
   229: }
   230: 
   231: 
   232: void BPBox::debugPrint(std::ostream &os, int ind) const
   233: {
   234:   static char const * const map[] = {
   235:     "vert",
   236:     "seq",
   237:     "corr"
   238:   };
   239: 
   240:   os << "box(kind=" << map[kind] << ") {\n";
   241:   ind += 2;
   242: 
   243:   FOREACH_ASTLIST(BPElement, elts, iter) {
   244:     for (int i=0; i<ind; i++) {
   245:       os << " ";
   246:     }
   247: 
   248:     iter.data()->debugPrint(os, ind);
   249:     os << "\n";
   250:   }
   251: 
   252:   ind -= 2;
   253:   for (int i=0; i<ind; i++) {
   254:     os << " ";
   255:   }
   256:   os << "}";
   257: }
   258: 
   259: 
   260: // ------------------------ BoxPrint ----------------------
   261: BPKind const BoxPrint::vert = BP_vertical;
   262: BPKind const BoxPrint::seq  = BP_sequence;
   263: BPKind const BoxPrint::hv   = BP_correlated;
   264: BPKind const BoxPrint::end  = NUM_BPKINDS;
   265: 
   266: 
   267: BoxPrint::BoxPrint()
   268:   : boxStack(),
   269:     levelIndent(2)
   270: {
   271:   // initial vert box
   272:   boxStack.push(new BPBox(BP_vertical));
   273: }
   274: 
   275: BoxPrint::~BoxPrint()
   276: {}
   277: 
   278: 
   279: void BoxPrint::append(BPElement *elt)
   280: {
   281:   box()->elts.append(elt);
   282: }
   283: 
   284: 
   285: BoxPrint& BoxPrint::operator<< (int i)
   286: {
   287:   return operator<< (sm_stringc << i);
   288: }
   289: 
   290: BoxPrint& BoxPrint::operator<< (char const *s)
   291: {
   292:   append(new BPText(s));
   293:   return *this;
   294: }
   295: 
   296: 
   297: BoxPrint& BoxPrint::operator<< (BPKind k)
   298: {
   299:   if (k == NUM_BPKINDS) {
   300:     // close current box
   301:     append(boxStack.pop());
   302:   }
   303:   else {
   304:     // open new box
   305:     boxStack.push(new BPBox(k));
   306:   }
   307:   return *this;
   308: }
   309: 
   310: 
   311: BoxPrint& BoxPrint::operator<< (Cmd c)
   312: {
   313:   if (c == br || c == sp) {
   314:     append(new BPBreak(c==br /*enabled*/, 0 /*indent*/));
   315:   }
   316:   else {
   317:     append(new BPBreak(true /*enabled*/, c==ind? levelIndent : -levelIndent));
   318:   }
   319:   return *this;
   320: }
   321: 
   322: 
   323: BoxPrint& BoxPrint::operator<< (IBreak b)
   324: {
   325:   append(new BPBreak(true /*enabled*/, b.indent /*indent*/));
   326:   return *this;
   327: }
   328: 
   329: 
   330: BoxPrint& BoxPrint::operator<< (Op o)
   331: {
   332:   return *this << sp << o.text << br;
   333: }
   334: 
   335: 
   336: BPBox* /*owner*/ BoxPrint::takeTree()
   337: {
   338:   // all boxes must be closed
   339:   xassert(boxStack.length() == 1);
   340: 
   341:   BPBox *ret = boxStack.pop();
   342: 
   343:   // initialize the box stack again, in case the user wants
   344:   // to build another tree
   345:   boxStack.push(new BPBox(BP_vertical));
   346: 
   347:   return ret;
   348: }
   349: 
   350: 
   351: void BoxPrint::debugPrint(std::ostream &os) const
   352: {
   353:   for (int i=0; i < boxStack.length(); i++) {
   354:     os << "----- frame -----\n";
   355:     boxStack[i]->debugPrint(os, 0 /*ind*/);
   356:     os << "\n";
   357:   }
   358: }
   359: 
   360: void BoxPrint::debugPrintCout() const
   361: {
   362:   debugPrint(std::cout);
   363: }
   364: 
   365: 
   366: // ------------------------ test code ----------------------
   367: #ifdef TEST_BOXPRINT
   368: 
   369: #include <stdlib.h>       // atoi
   370: #include "sm_ckheap.h"
   371: 
   372: void doit(int argc, char *argv[])
   373: {
   374:   BoxPrint bp;
   375: 
   376:   bp << "int foo()" << bp.br
   377:      << "{" << bp.ind;
   378: 
   379:   bp << "printf(" << bp.seq
   380:         << "\"hello there %d!\\n\"," << bp.br
   381:         << "123456789"
   382:      << bp.end << ");" << bp.br;
   383: 
   384:   bp << "bar(" << bp.seq
   385:         << "1" << bp.op("+")
   386:         << "2" << bp.op("+")
   387:         << "3" << bp.op("+")
   388:         << "4" << bp.op("+")
   389:         << "5" << bp.op("+")
   390:         << "6" << bp.op("+")
   391:         << "7" << bp.op("+")
   392:         << "8" << bp.op("+")
   393:         << "9" << bp.op("+")
   394:         << "10"
   395:      << bp.end << ");" << bp.br;
   396: 
   397:   bp << "baz(" << bp.seq
   398:         << "\"a really long line that has no optional breaks at all\""
   399:      << bp.end << ");" << bp.br;
   400: 
   401:   bp << "zoo(" << bp.seq
   402:         << "\"one break is here, but it is very\"," << bp.br
   403:         << "\"far from the start\""
   404:      << bp.end << ");" << bp.br;
   405: 
   406:   bp << "assert(" << bp.seq
   407:         << bp.seq << "x" << bp.op("=") << "y" << bp.end << bp.op("&&")
   408:         << bp.seq << "z" << bp.op("=") << "w" << bp.end << bp.op("&&")
   409:         << "(" << bp.seq
   410:            << bp.seq << "moron" << bp.op("!=") << "fool" << bp.end << bp.op("||")
   411:            << "taxes->theRich"
   412:         << bp.end << ")"
   413:      << bp.end << ")" << bp.br;
   414: 
   415:   bp << bp.hv
   416:         << "forall(" << bp.seq
   417:            << "x," << bp.br << "y," << bp.br << "z"
   418:         << bp.end << "). if {" << bp.ind
   419:         << bp.seq << "x" << bp.op("==") << "yooey_more" << bp.end << ";" << bp.br
   420:         << bp.seq << "yowza" << bp.op("!=") << "fooey" << bp.end << ";"
   421:         << bp.und << "} /*==>*/ {" << bp.ind
   422:         << bp.seq << "z(x,y,z)" << bp.op("==") << "3" << bp.end << ";" << bp.br
   423:         << "ay_caramba" << ";"
   424:         << bp.und << "};"
   425:      << bp.end;
   426: 
   427:   bp << bp.und << "}" << bp.br;
   428: 
   429:   BPBox *tree = bp.takeTree();
   430: 
   431:   BPRender ren;
   432:   ren.margin = 30;
   433:   if (argc >= 2) {
   434:     ren.margin = atoi(argv[1]);
   435:   }
   436:   std::cout << "margin: " << ren.margin << "\n";
   437: 
   438:   tree->render(ren);
   439:   delete tree;
   440: 
   441:   std::cout << "         1    1    2    2    3    3    4    4    5    5    6    6    7\n";
   442:   std::cout << "1---5----0----5----0----5----0----5----0----5----0----5----0----5----0\n";
   443:   std::cout << ren.takeString();
   444: }
   445: 
   446: int main(int argc, char *argv[])
   447: {
   448:   doit(argc, argv);
   449:   //malloc_stats();
   450:   return 0;
   451: }
   452: 
   453: #endif // TEST_BOXPRINT
   454: 
End cpp section to elk/sm_boxprint.cpp[1]
Start cpp section to elk/sm_flatten.cpp[1 /1 ]
     1: #line 8766 "./lpsrc/sm.pak"
     2: // flatten.cc            see license.txt for copyright and terms of use
     3: // code for flatten.h
     4: 
     5: // basically, this file provides some reasonable defaults
     6: // assuming we are reading/writing binary files
     7: 
     8: #include "sm_flatten.h"
     9: #include "sm_exc.h"
    10: #include <cstring>      // std::strlen
    11: 
    12: Flatten::Flatten()
    13: {}
    14: 
    15: Flatten::~Flatten()
    16: {}
    17: 
    18: 
    19: void Flatten::xferChar(char &c)
    20: {
    21:   xferSimple(&c, sizeof(c));
    22: }
    23: 
    24: void Flatten::xferInt(int &i)
    25: {
    26:   xferSimple(&i, sizeof(i));
    27: }
    28: 
    29: void Flatten::xferLong(long &l)
    30: {
    31:   xferSimple(&l, sizeof(l));
    32: }
    33: 
    34: void Flatten::xferBool(bool &b)
    35: {
    36:   xferSimple(&b, sizeof(b));
    37: }
    38: 
    39: 
    40: void Flatten::xferHeapBuffer(void *&buf, int len)
    41: {
    42:   if (reading()) {
    43:     buf = new unsigned char[len];
    44:   }
    45:   xferSimple(buf, len);
    46: }
    47: 
    48: 
    49: void Flatten::xferCharString(char *&str)
    50: {
    51:   if (writing()) {
    52:     if (!str) {
    53:       writeInt(-1);     // representation of NULL
    54:       return;
    55:     }
    56: 
    57:     int len = std::strlen(str);
    58:     writeInt(len);
    59: 
    60:     // write the null terminator too, as a simple
    61:     // sanity check when reading
    62:     xferSimple(str, len+1);
    63:   }
    64:   else {
    65:     int len = readInt();
    66:     if (len == -1) {
    67:       str = NULL;
    68:       return;
    69:     }
    70: 
    71:     str = new char[len+1];
    72:     xferSimple(str, len+1);
    73:     formatAssert(str[len] == '\0');
    74:   }
    75: }
    76: 
    77: 
    78: void Flatten::checkpoint(int code)
    79: {
    80:   if (writing()) {
    81:     writeInt(code);
    82:   }
    83:   else {
    84:     int c = readInt();
    85:     formatAssert(c == code);
    86:   }
    87: }
    88: 
    89: 
    90: void Flatten::writeInt(int i)
    91: {
    92:   xassert(writing());
    93:   xferInt(i);
    94: }
    95: 
    96: int Flatten::readInt()
    97: {
    98:   xassert(reading());
    99:   int i;
   100:   xferInt(i);
   101:   return i;
   102: }
   103: 
End cpp section to elk/sm_flatten.cpp[1]
Start cpp section to elk/sm_gprintf.cpp[1 /1 ]
     1: #line 8870 "./lpsrc/sm.pak"
     2: /* gprintf.c */
     3: /* originally from: http://www.efgh.com/software/gprintf.htm */
     4: /* this file is in the public domain */
     5: 
     6: /* modified by Scott McPeak, April 2003:
     7:  *   - use va_list instead of 'const int*' for the
     8:  *     pointer-to-argument type (for portability)
     9:  *   - implement conservative estimates for unknown format
    10:  *     chars, particularly 'f' (CONSERVATIVE_ESTIMATE flag)
    11:  *   - add a few test vectors
    12:  */
    13: 
    14: /* NOTE: There are quite a few differences among the *printf
    15:  * implementations running around in the various libcs.  The
    16:  * implementation in this module doesn't know about all of those
    17:  * variations and extensions.  So, if you're using this to estimate
    18:  * the # of chars your libc's printf will use, be sure to compare
    19:  * libc's printf's actual return value, to make sure something doesn't
    20:  * slip through the cracks. */
    21: 
    22: /* Code for general_printf() */
    23: /* Change extension to .c before compiling */
    24: 
    25: #include "sm_gprintf.h"
    26: #include <assert.h>      /* assert */
    27: 
    28: /* when this is true, unknown fields are filled with Xs, in an attempt
    29:  * to print at least as many characters as libc's sprintf */
    30: #define CONSERVATIVE_ESTIMATE 1
    31: 
    32: #define BITS_PER_BYTE           8
    33: 
    34: struct parameters
    35: {
    36:   int number_of_output_chars;
    37:   short minimum_field_width;
    38:   char options;
    39:     #define MINUS_SIGN    1
    40:     #define RIGHT_JUSTIFY 2
    41:     #define ZERO_PAD      4
    42:     #define CAPITAL_HEX   8
    43:   short edited_sm_string_length;
    44:   short leading_zeros;
    45:   int (*output_function)(void *, int);
    46:   void *output_pointer;
    47: };
    48: 
    49: static void output_and_count(struct parameters *p, int c)
    50: {
    51:   if (p->number_of_output_chars >= 0)
    52:   {
    53:     int n = (*p->output_function)(p->output_pointer, c);
    54:     if (n>=0) p->number_of_output_chars++;
    55:     else p->number_of_output_chars = n;
    56:   }
    57: }
    58: 
    59: static void output_field(struct parameters *p, char const *s)
    60: {
    61:   short justification_length =
    62:     p->minimum_field_width - p->leading_zeros - p->edited_sm_string_length;
    63:   if (p->options & MINUS_SIGN)
    64:   {
    65:     if (p->options & ZERO_PAD)
    66:       output_and_count(p, '-');
    67:     justification_length--;
    68:   }
    69:   if (p->options & RIGHT_JUSTIFY)
    70:     while (--justification_length >= 0)
    71:       output_and_count(p, p->options & ZERO_PAD ? '0' : ' ');
    72:   if (p->options & MINUS_SIGN && !(p->options & ZERO_PAD))
    73:     output_and_count(p, '-');
    74:   while (--p->leading_zeros >= 0)
    75:     output_and_count(p, '0');
    76:   while (--p->edited_sm_string_length >= 0)
    77:     output_and_count(p, *s++);
    78:   while (--justification_length >= 0)
    79:     output_and_count(p, ' ');
    80: }
    81: 
    82: 
    83: int general_vprintf(Gprintf_output_function output_function,
    84:                     void *output_pointer,
    85:                     const char *control_sm_string,
    86:                     va_list argument_pointer)
    87: {
    88:   struct parameters p;
    89:   char control_char;
    90:   p.number_of_output_chars = 0;
    91:   p.output_function = output_function;
    92:   p.output_pointer = output_pointer;
    93:   control_char = *control_sm_string++;
    94:   while (control_char != '\0')
    95:   {
    96:     if (control_char == '%')
    97:     {
    98:       short precision = -1;
    99:       short long_argument = 0;
   100:       short base = 0;
   101:       control_char = *control_sm_string++;
   102:       p.minimum_field_width = 0;
   103:       p.leading_zeros = 0;
   104:       p.options = RIGHT_JUSTIFY;
   105:       if (control_char == '-')
   106:       {
   107:         p.options = 0;
   108:         control_char = *control_sm_string++;
   109:       }
   110:       if (control_char == '0')
   111:       {
   112:         p.options |= ZERO_PAD;
   113:         control_char = *control_sm_string++;
   114:       }
   115:       if (control_char == '*')
   116:       {
   117:         p.minimum_field_width = va_arg(argument_pointer, int);
   118:         control_char = *control_sm_string++;
   119:       }
   120:       else
   121:       {
   122:         while ('0' <= control_char && control_char <= '9')
   123:         {
   124:           p.minimum_field_width =
   125:             p.minimum_field_width * 10 + control_char - '0';
   126:           control_char = *control_sm_string++;
   127:         }
   128:       }
   129:       if (control_char == '.')
   130:       {
   131:         control_char = *control_sm_string++;
   132:         if (control_char == '*')
   133:         {
   134:           precision = va_arg(argument_pointer, int);
   135:           control_char = *control_sm_string++;
   136:         }
   137:         else
   138:         {
   139:           precision = 0;
   140:           while ('0' <= control_char && control_char <= '9')
   141:           {
   142:             precision = precision * 10 + control_char - '0';
   143:             control_char = *control_sm_string++;
   144:           }
   145:         }
   146:       }
   147:       if (control_char == 'l')
   148:       {
   149:         long_argument = 1;
   150:         control_char = *control_sm_string++;
   151:       }
   152:       if (control_char == 'd')
   153:         base = 10;
   154:       else if (control_char == 'x')
   155:         base = 16;
   156:       else if (control_char == 'X')
   157:       {
   158:         base = 16;
   159:         p.options |= CAPITAL_HEX;
   160:       }
   161:       else if (control_char == 'u')
   162:         base = 10;
   163:       else if (control_char == 'o')
   164:         base = 8;
   165:       else if (control_char == 'b')
   166:         base = 2;
   167:       else if (control_char == 'c')
   168:       {
   169:         base = -1;
   170:         p.options &= ~ZERO_PAD;
   171:       }
   172:       else if (control_char == 's')
   173:       {
   174:         base = -2;
   175:         p.options &= ~ZERO_PAD;
   176:       }
   177:       if (base == 0)  /* invalid conversion type */
   178:       {
   179:         if (control_char != '\0')
   180:         {
   181:           #if !CONSERVATIVE_ESTIMATE
   182:             /* sm: this was the original code; it just prints the
   183:              * format character itself */
   184:             output_and_count(&p, control_char);
   185: 
   186:           #else
   187:             /* since my goal is actually to compute a conservative
   188:              * upper bound on the # of chars output by sprintf, I want
   189:              * to fill unknown fields with Xs */
   190:             static char const * const XXX =
   191:               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";  /* 50 Xs */
   192:             assert(precision <= 30);     /* otherwise I need more Xs */
   193: 
   194:             /* I'm assuming that printing floating-point is the worst case.
   195:              * I further assume non-fractional parts (integer part,
   196:              * exponent, decimal, sign) won't exceed 20 chars.  Finally,
   197:              * up to 30 characters of decimal part are supported (this
   198:              * is checked with the assertion above). */
   199:             if (precision == -1) {
   200:               p.edited_sm_string_length = 20 + 6;    /* 6 is default precision for 'f' */
   201:             }
   202:             else {
   203:               p.edited_sm_string_length = 20 + precision;
   204:             }
   205:             output_field(&p, XXX);
   206:           #endif
   207: 
   208:           control_char = *control_sm_string++;
   209:         }
   210:       }
   211:       else
   212:       {
   213:         if (base == -1)  /* conversion type c */
   214:         {
   215:           /* 'char' is passed as 'int' through '...' */
   216:           char c = (char)va_arg(argument_pointer, int);
   217:           p.edited_sm_string_length = 1;
   218:           output_field(&p, &c);
   219:         }
   220:         else if (base == -2)  /* conversion type s */
   221:         {
   222:           char *sm_string;
   223:           p.edited_sm_string_length = 0;
   224:           sm_string = va_arg(argument_pointer, char*);
   225:           while (sm_string[p.edited_sm_string_length] != 0)
   226:             p.edited_sm_string_length++;
   227:           if (precision >= 0 && p.edited_sm_string_length > precision)
   228:             p.edited_sm_string_length = precision;
   229:           output_field(&p, sm_string);
   230:         }
   231:         else  /* conversion type d, b, o or x */
   232:         {
   233:           unsigned long x;
   234:           char buffer[BITS_PER_BYTE * sizeof(unsigned long) + 1];
   235:           p.edited_sm_string_length = 0;
   236:           if (long_argument)
   237:           {
   238:             x = va_arg(argument_pointer, unsigned long);
   239:           }
   240:           else if (control_char == 'd')
   241:             x = va_arg(argument_pointer, long);
   242:           else
   243:             x = va_arg(argument_pointer, unsigned);
   244:           if (control_char == 'd' && (long) x < 0)
   245:           {
   246:             p.options |= MINUS_SIGN;
   247:             x = - (long) x;
   248:           }
   249:           do
   250:           {
   251:             int c;
   252:             c = x % base + '0';
   253:             if (c > '9')
   254:             {
   255:               if (p.options & CAPITAL_HEX)
   256:                 c += 'A'-'9'-1;
   257:               else
   258:                 c += 'a'-'9'-1;
   259:             }
   260:             buffer[sizeof(buffer) - 1 - p.edited_sm_string_length++] = c;
   261:           }
   262:           while ((x/=base) != 0);
   263:           if (precision >= 0 && precision > p.edited_sm_string_length)
   264:             p.leading_zeros = precision - p.edited_sm_string_length;
   265:           output_field(&p, buffer + sizeof(buffer) - p.edited_sm_string_length);
   266:         }
   267:         control_char = *control_sm_string++;
   268:       }
   269:     }
   270:     else
   271:     {
   272:       output_and_count(&p, control_char);
   273:       control_char = *control_sm_string++;
   274:     }
   275:   }
   276:   return p.number_of_output_chars;
   277: }
   278: 
   279: 
   280: int general_printf(Gprintf_output_function output,
   281:                    void *extra, const char *format, ...)
   282: {
   283:   va_list args;
   284:   int ret;
   285: 
   286:   va_start(args, format);
   287:   ret = general_vprintf(output, extra, format, args);
   288:   va_end(args);
   289: 
   290:   return ret;
   291: }
   292: 
   293: 
   294: /* ------------------ test code --------------------- */
   295: #ifdef TEST_GPRINTF
   296: 
   297: #include <stdio.h>     /* fputc, printf, vsprintf */
   298: #include <cstring>    /* strcmp, std::strlen */
   299: #include <stdlib.h>    /* exit */
   300: 
   301: 
   302: int sm_string_output(void *extra, int ch)
   303: {
   304:   /* the 'extra' argument is a pointer to a pointer to the
   305:    * next character to write */
   306:   char **s = (char**)extra;
   307: 
   308:   **s = ch;     /* write */
   309:   (*s)++;       /* advance */
   310: 
   311:   return 0;
   312: }
   313: 
   314: int general_vsprintf(char *dest, char const *format, va_list args)
   315: {
   316:   char *s = dest;
   317:   int ret;
   318: 
   319:   ret = general_vprintf(sm_string_output, &s, format, args);
   320:   *s = 0;
   321: 
   322:   return ret;
   323: }
   324: 
   325: 
   326: char output1[1024];    /* for libc */
   327: char output2[1024];    /* for this module */
   328: 
   329: 
   330: void expect_vector_len(int expect_len, char const *expect_output,
   331:                        char const *format, va_list args)
   332: {
   333:   int len;
   334:   static int vectors = 0;
   335: 
   336:   /* keep track of how many vectors we've tried, to make it
   337:    * a little easier to correlate failures with the inputs
   338:    * in this file */
   339:   vectors++;
   340: 
   341:   /* run the generalized vsprintf */
   342:   len = general_vsprintf(output2, format, args);
   343: 
   344:   /* compare */
   345:   if (len!=expect_len ||
   346:       0!=strcmp(expect_output, output2)) {
   347:     printf("outputs differ for vector %d!\n", vectors);
   348:     printf("  format: %s\n", format);
   349:     printf("  expect: %s (%d)\n", expect_output, expect_len);
   350:     printf("      me: %s (%d)\n", output2, len);
   351:     exit(2);
   352:   }
   353: }
   354: 
   355: 
   356: void expect_vector(char const *expect_output,
   357:                    char const *format, ...)
   358: {
   359:   va_list args;
   360:   va_start(args, format);
   361:   expect_vector_len(std::strlen(expect_output), expect_output, format, args);
   362:   va_end(args);
   363: }
   364: 
   365: 
   366: void vector(char const *format, ...)
   367: {
   368:   va_list args;
   369:   int len;
   370: 
   371:   /* run the real vsprintf */
   372:   va_start(args, format);
   373:   len = vsprintf(output1, format, args);
   374:   va_end(args);
   375: 
   376:   /* test against the generalized vsprintf */
   377:   va_start(args, format);
   378:   expect_vector_len(len, output1, format, args);
   379:   va_end(args);
   380: }
   381: 
   382: 
   383: int main()
   384: {
   385:   printf("testing gprintf...\n");
   386: 
   387:   /* test against libc */
   388:   vector("simple");
   389:   vector("a %s more", "little");
   390:   vector("some %4d more %s complicated %c stuff",
   391:          33, "yikes", 'f');
   392: 
   393:   /* test unknown format chars */
   394:   expect_vector("XXXXXXXXXXXXXXXXXXXXXXXXXX", "%f", 3.4);
   395:   expect_vector("XXXXXXXXXXXXXXXXXXXXXXX", "%.3f", 3.4);
   396:   expect_vector("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "%.10f", 3.4);
   397:   expect_vector("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "%.30f", 3.4);
   398: 
   399:   /* fails assertion, as it should */
   400:   /* expect_vector("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "%.31f", 3.4); */
   401: 
   402:   /* TODO: add more tests */
   403: 
   404:   printf("gprintf works\n");
   405:   return 0;
   406: }
   407: 
   408: #endif /* TEST_GPRINTF */
End cpp section to elk/sm_gprintf.cpp[1]
Start cpp section to elk/sm_growbuf.cpp[1 /1 ]
     1: #line 9279 "./lpsrc/sm.pak"
     2: // growbuf.cc            see license.txt for copyright and terms of use
     3: // code for growbuf.h
     4: 
     5: #include "sm_growbuf.h"
     6: 
     7: #include <cstring>       // std::memcpy
     8: 
     9: void GrowBuffer::append(byte const *str, int len)
    10: {
    11:   // test length
    12:   int newLen = getDataLen() + len;
    13:   if (newLen > getAllocated()) {
    14:     // must grow
    15:     int newAlloc = max(getAllocated(), 16);
    16:     while (newLen > newAlloc) {
    17:       newAlloc *= 2;      // would like an overflow test here..
    18:     }
    19: 
    20:     setAllocated(newAlloc);
    21:   }
    22: 
    23:   // do copy
    24:   std::memcpy(getData()+getDataLen(), str, len);
    25:   setDataLen(newLen);
    26: }
    27: 
    28: 
    29: // ----------------- test code ----------------
    30: #ifdef TEST_GROWBUF
    31: #include "sm_test.h"
    32: 
    33: void entry()
    34: {
    35:   byte const str[] = "crazy like a mad cow!";
    36:   int len = sizeof(str);
    37: 
    38:   GrowBuffer buf;
    39:   loopi(10) {
    40:     buf.append(str, len);
    41:   }
    42:   loopi(10) {
    43:     if (0!=std::memcmp(str, buf.getData()+len*i, len)) {
    44:       xfailure("buffer contents are wrong");
    45:     }
    46:   }
    47:   std::cout << "growbuf ok\n";
    48: }
    49: 
    50: USUAL_MAIN
    51: 
    52: #endif // TEST_GROWBUF
End cpp section to elk/sm_growbuf.cpp[1]
Start cpp section to elk/sm_hashline.cpp[1 /1 ]
     1: #line 9332 "./lpsrc/sm.pak"
     2: // hashline.cc
     3: // code for hashline.h
     4: 
     5: #include "sm_hashline.h"
     6: 
     7: #include <cstring>        // std::memcpy
     8: 
     9: 
    10: HashLineMap::HashLineMap(char const *pf)
    11:   : ppFname(pf),
    12:     filenames(),     // empty
    13:     directives(),    // empty
    14:     prev_ppLine(-1)  // user shouldn't have negative line numbers
    15: {}
    16: 
    17: 
    18: HashLineMap::~HashLineMap()
    19: {}
    20: 
    21: 
    22: void HashLineMap::addHashLine(int ppLine, int origLine, char const *origFname)
    23: {
    24:   // check that entries are being added in sorted order
    25:   xassert(ppLine > prev_ppLine);
    26:   prev_ppLine = ppLine;
    27: 
    28:   // map 'origFname' to a canonical reference
    29:   sm_string *canon = filenames.queryif(origFname);
    30:   if (!canon) {
    31:     // add a new one
    32:     canon = new sm_string(origFname);
    33:     filenames.add(origFname, canon);
    34:   }
    35:   origFname = canon->pcharc();
    36: 
    37:   // add the entry to the array
    38:   directives.push(HashLine(ppLine, origLine, origFname));
    39: }
    40: 
    41: 
    42: void HashLineMap::doneAdding()
    43: {
    44:   // make a new array of exactly the right size
    45:   ArrayStack<HashLine> tmp(directives.length());
    46: 
    47:   // copy all the entries into the new array
    48:   std::memcpy(tmp.getDangerousWritableArray(), directives.getArray(),
    49:          directives.length() * sizeof(HashLine));
    50:   tmp.setLength(directives.length());
    51: 
    52:   // swap the internal contents of the two arrays, so 'directives'
    53:   // becomes the consolidated one
    54:   tmp.swapWith(directives);
    55: 
    56:   // now tmp's internal storage will be automatically deleted
    57: }
    58: 
    59: 
    60: // for queries exactly on #line directives we return the specified
    61: // origLine minus 1, but I don't specify any behavior in that case
    62: // so it's not a problem
    63: void HashLineMap::map(int ppLine, int &origLine, char const *&origFname) const
    64: {
    65:   // check for a ppLine that precedes any in the array
    66:   if (directives.isEmpty() ||
    67:       ppLine < directives[0].ppLine) {
    68:     // it simply refers to the pp file
    69:     origLine = ppLine;
    70:     origFname = ppFname.pcharc();
    71:     return;
    72:   }
    73: 
    74:   // perform binary search on the 'directives' array
    75:   int low = 0;                        // index of lowest candidate
    76:   int high = directives.length()-1;   // index of highest candidate
    77: 
    78:   while (low < high) {
    79:     // check the midpoint (round up to ensure progress when low+1 == high)
    80:     int mid = (low+high+1)/2;
    81:     if (directives[mid].ppLine > ppLine) {
    82:       // too high
    83:       high = mid-1;
    84:     }
    85:     else {
    86:       // too low or just right
    87:       low = mid;
    88:     }
    89:   }
    90:   xassert(low == high);
    91:   HashLine const &hl = directives[low];
    92: 
    93:   // the original line is the origLine in the array entry, plus the
    94:   // offset between the ppLine passed in and the ppLine in the array,
    95:   // minus 1 because the #line directive itself occupies one pp line
    96:   origLine = hl.origLine + (ppLine - hl.ppLine - 1);
    97: 
    98:   origFname = hl.origFname;
    99: }
   100: 
   101: 
   102: int HashLineMap::mapLine(int ppLine) const
   103: {
   104:   int origLine;
   105:   char const *origFname;
   106:   map(ppLine, origLine, origFname);
   107:   return origLine;
   108: }
   109: 
   110: char const *HashLineMap::mapFile(int ppLine) const
   111: {
   112:   int origLine;
   113:   char const *origFname;
   114:   map(ppLine, origLine, origFname);
   115:   return origFname;
   116: }
   117: 
   118: 
   119: // --------------------- test code ---------------------
   120: #ifdef TEST_HASHLINE
   121: 
   122: #include <stdio.h>     // printf
   123: #include <stdlib.h>    // exit
   124: 
   125: void query(HashLineMap &hl, int ppLine,
   126:            int expectOrigLine, char const *expectOrigFname)
   127: {
   128:   int origLine;
   129:   char const *origFname;
   130:   hl.map(ppLine, origLine, origFname);
   131: 
   132:   if (origLine != expectOrigLine ||
   133:       0!=strcmp(origFname, expectOrigFname)) {
   134:     printf("map(%d) yielded %s:%d, but I expected %s:%d\n",
   135:            ppLine, origFname, origLine,
   136:            expectOrigFname, expectOrigLine);
   137:     exit(2);
   138:   }
   139: }
   140: 
   141: 
   142: int main()
   143: {
   144:   // insert #line directives:
   145:   //    foo.i
   146:   //    +----------
   147:   //   1|// nothing; it's in the pp file
   148:   //   2|#line 1 foo.cc
   149:   //   3|
   150:   //   4|
   151:   //   5|#line 1 foo.h
   152:   //   ..
   153:   //  76|#line 5 foo.cc
   154:   //   ..
   155:   // 100|#line 101 foo.i
   156: 
   157:   HashLineMap hl("foo.i");
   158:   hl.addHashLine(2, 1, "foo.cc");
   159:   hl.addHashLine(5, 1, "foo.h");
   160:   hl.addHashLine(76, 5, "foo.cc");
   161:   hl.addHashLine(100, 101, "foo.i");
   162:   hl.doneAdding();
   163: 
   164:   // make queries, and check for expected results
   165:   query(hl, 1, 1, "foo.i");
   166: 
   167:   query(hl, 3, 1, "foo.cc");
   168:   query(hl, 4, 2, "foo.cc");
   169: 
   170:   query(hl, 6, 1, "foo.h");
   171:   query(hl, 7, 2, "foo.h");
   172:   // ...
   173:   query(hl, 75, 70, "foo.h");
   174: 
   175:   query(hl, 77, 5, "foo.cc");
   176:   query(hl, 78, 6, "foo.cc");
   177:   // ...
   178:   query(hl, 99, 27, "foo.cc");
   179: 
   180:   query(hl, 101, 101, "foo.i");
   181:   query(hl, 102, 102, "foo.i");
   182:   // ...
   183: 
   184:   printf("unique filenames: %d\n", hl.numUniqueFilenames());
   185:   printf("hashline seems to work\n");
   186: 
   187:   return 0;
   188: }
   189: 
   190: #endif // TEST_HASHLINE
End cpp section to elk/sm_hashline.cpp[1]
Start cpp section to elk/sm_hashtbl.cpp[1 /1 ]
     1: #line 9523 "./lpsrc/sm.pak"
     2: // hashtbl.cc            see license.txt for copyright and terms of use
     3: // code for hashtbl.h
     4: 
     5: #include "sm_hashtbl.h"
     6: #include "sm_xassert.h"
     7: 
     8: #include <cstring>      // std::memset
     9: 
    10: 
    11: unsigned HashTable::hashFunction(void const *key) const
    12: {
    13:   return coreHashFn(key) % (unsigned)tableSize;
    14: }
    15: 
    16: 
    17: HashTable::HashTable(GetKeyFn gk, HashFn hf, EqualKeyFn ek, int initSize)
    18:   : getKey(gk),
    19:     coreHashFn(hf),
    20:     equalKeys(ek)
    21: {
    22:   makeTable(initSize);
    23: }
    24: 
    25: HashTable::~HashTable()
    26: {
    27:   delete[] hashTable;
    28: }
    29: 
    30: 
    31: void HashTable::makeTable(int size)
    32: {
    33:   hashTable = new void*[size];
    34:   tableSize = size;
    35:   std::memset(hashTable, 0, sizeof(void*) * tableSize);
    36:   numEntries = 0;
    37: }
    38: 
    39: 
    40: int HashTable::getEntry(void const *key) const
    41: {
    42:   int index = hashFunction(key);
    43:   int originalIndex = index;
    44:   for(;;) {
    45:     if (hashTable[index] == NULL) {
    46:       // unmapped
    47:       return index;
    48:     }
    49:     if (equalKeys(key, getKey(hashTable[index]))) {
    50:       // mapped here
    51:       return index;
    52:     }
    53: 
    54:     // this entry is mapped, but not with this key, i.e.
    55:     // we have a collision -- so just go to the next entry,
    56:     // wrapping as necessary
    57:     index = nextIndex(index);
    58: 
    59:     // detect infinite looping
    60:     xassert(index != originalIndex);
    61:   }
    62: }
    63: 
    64: 
    65: void *HashTable::get(void const *key) const
    66: {
    67:   return hashTable[getEntry(key)];
    68: }
    69: 
    70: 
    71: void HashTable::resizeTable(int newSize)
    72: {
    73:   // save old stuff
    74:   void **oldTable = hashTable;
    75:   int oldSize = tableSize;
    76:   int oldEntries = numEntries;
    77: 
    78:   // make the new table
    79:   makeTable(newSize);
    80: 
    81:   // move entries to the new table
    82:   for (int i=0; i<oldSize; i++) {
    83:     if (oldTable[i] != NULL) {
    84:       add(getKey(oldTable[i]), oldTable[i]);
    85:       oldEntries--;
    86:     }
    87:   }
    88:   xassert(oldEntries == 0);
    89: 
    90:   // deallocate the old table
    91:   delete[] oldTable;
    92: }
    93: 
    94: 
    95: void HashTable::add(void const *key, void *value)
    96: {
    97:   if (numEntries+1 > tableSize*2/3) {
    98:     // we're over the usage threshold; increase table size
    99:     resizeTable(tableSize * 2 + 1);
   100:   }
   101: 
   102:   int index = getEntry(key);
   103:   xassert(hashTable[index] == NULL);    // must not be a mapping yet
   104: 
   105:   hashTable[index] = value;
   106:   numEntries++;
   107: }
   108: 
   109: 
   110: void *HashTable::remove(void const *key)
   111: {
   112:   if (enableShrink                &&
   113:       numEntries-1 < tableSize/5  &&
   114:       tableSize > defaultSize) {
   115:     // we're below threshold; reduce table size
   116:     resizeTable(tableSize / 2);
   117:   }
   118: 
   119:   int index = getEntry(key);
   120:   xassert(hashTable[index] != NULL);    // must be a mapping to remove
   121: 
   122:   // remove this entry
   123:   void *retval = hashTable[index];
   124:   hashTable[index] = NULL;
   125:   numEntries--;
   126: 
   127:   // now, if we ever inserted something and it collided with this one,
   128:   // leaving things like this would prevent us from finding that other
   129:   // mapping because the search stops as soon as a NULL entry is
   130:   // discovered; so we must examine all entries that could have
   131:   // collided, and re-insert them
   132:   int originalIndex = index;
   133:   for(;;) {
   134:     index = nextIndex(index);
   135:     xassert(index != originalIndex);    // prevent infinite loops
   136: 
   137:     if (hashTable[index] == NULL) {
   138:       // we've reached the end of the list of possible colliders
   139:       break;
   140:     }
   141: 
   142:     // remove this one
   143:     void *data = hashTable[index];
   144:     hashTable[index] = NULL;
   145:     numEntries--;
   146: 
   147:     // add it back
   148:     add(getKey(data), data);
   149:   }
   150: 
   151:   return retval;
   152: }
   153: 
   154: 
   155: void HashTable::empty(int initSize)
   156: {
   157:   delete[] hashTable;
   158:   makeTable(initSize);
   159: }
   160: 
   161: 
   162: void HashTable::selfCheck() const
   163: {
   164:   int ct=0;
   165:   for (int i=0; i<tableSize; i++) {
   166:     if (hashTable[i] != NULL) {
   167:       checkEntry(i);
   168:       ct++;
   169:     }
   170:   }
   171: 
   172:   xassert(ct == numEntries);
   173: }
   174: 
   175: void HashTable::checkEntry(int entry) const
   176: {
   177:   int index = getEntry(getKey(hashTable[entry]));
   178:   int originalIndex = index;
   179:   for(;;) {
   180:     if (index == entry) {
   181:       // the entry lives where it will be found, so that's good
   182:       return;
   183:     }
   184:     if (hashTable[index] == NULL) {
   185:       // the search for this entry would stop before finding it,
   186:       // so that's bad!
   187:       xfailure("checkEntry: entry in wrong slot");
   188:     }
   189: 
   190:     // collision; keep looking
   191:     index = nextIndex(index);
   192:     xassert(index != originalIndex);
   193:   }
   194: }
   195: 
   196: 
   197: // ------------------ HashTableIter --------------------
   198: HashTableIter::HashTableIter(HashTable &t)
   199:   : table(t)
   200: {
   201:   index = 0;
   202:   moveToSth();
   203: }
   204: 
   205: void HashTableIter::adv()
   206: {
   207:   xassert(!isDone());
   208: 
   209:   // move off the current item
   210:   index++;
   211: 
   212:   // keep moving until we find something
   213:   moveToSth();
   214: }
   215: 
   216: void HashTableIter::moveToSth()
   217: {
   218:   while (index < table.tableSize &&
   219:          table.hashTable[index] == NULL) {
   220:     index++;
   221:   }
   222: 
   223:   if (index == table.tableSize) {
   224:     index = -1;    // mark as done
   225:   }
   226: }
   227: 
   228: 
   229: void *HashTableIter::data() const
   230: {
   231:   xassert(!isDone());
   232:   return table.hashTable[index];
   233: }
   234: 
   235: 
   236: STATICDEF void const *HashTable::identityKeyFn(void *data)
   237: {
   238:   return data;
   239: }
   240: 
   241: unsigned lcprngTwoSteps(SM_RAWADDRESS v)
   242: {
   243:   // this is the core of the LC PRNG in one of the many libcs
   244:   // running around the net
   245:   v = (v * 1103515245) + 12345;
   246: 
   247:   // do it again for good measure
   248:   v = (v * 1103515245) + 12345;
   249: 
   250:   return v;
   251: }
   252: 
   253: STATICDEF unsigned HashTable::lcprngHashFn(void const *key)
   254: {
   255:   return lcprngTwoSteps((SM_RAWADDRESS)key);
   256: }
   257: 
   258: STATICDEF bool HashTable::
   259:   pointerEqualKeyFn(void const *key1, void const *key2)
   260: {
   261:   return key1 == key2;
   262: }
   263: 
End cpp section to elk/sm_hashtbl.cpp[1]
Start cpp section to elk/sm_mysig.cpp[1 /1 ]
     1: #line 9787 "./lpsrc/sm.pak"
     2: // mysig.cc            see license.txt for copyright and terms of use
     3: // code for mysig.h
     4: 
     5: #include "sm_mysig.h"
     6: 
     7: //#include <cstring>     // strsignal
     8: #include <stdlib.h>     // exit
     9: #include <unistd.h>     // sleep
    10: #include <stdio.h>      // printf
    11: 
    12: // needed on Solaris; is __sun__ a good way to detect that?
    13: #ifdef __sun__
    14:   #include <siginfo.h>
    15: #endif
    16: 
    17: #ifndef __CYGWIN__      // everything here is for *not* cygwin
    18: 
    19: void setHandler(int signum, SignalHandler handler)
    20: {
    21:   struct sigaction sa;
    22:   std::memset(&sa, 0, sizeof(sa));       // clear any "extra" fields
    23:   sa.sa_handler = handler;          // handler function
    24:   sigemptyset(&sa.sa_mask);         // don't block other signals
    25:   sa.sa_flags = SA_RESTART;         // automatically restart syscalls
    26: 
    27:   // install the handler
    28:   if (0 > sigaction(signum, &sa, NULL)) {
    29:     perror("sigaction");
    30:     exit(2);
    31:   }
    32: }
    33: 
    34: 
    35: // simple handler that just prints and re-raises
    36: void printHandler(int signum)
    37: {
    38:   fprintf(stderr, "printHandler: I caught signal %d\n", signum);
    39:   psignal(signum, "psignal message");
    40: 
    41:   //fprintf(stderr, "I'm just going to wait a while...\n");
    42:   //sleep(60);
    43: 
    44:   // block the signal -- doesn't work for internally-generated
    45:   // signals (i.e. raise)
    46:   //sigset_t mask;
    47:   //sigemptyset(&mask);
    48:   //sigaddset(&mask, SIGINT);
    49: 
    50:   // reset the signal handler to its default handler
    51:   setHandler(signum, SIG_DFL);
    52: 
    53:   // re-raise, which doesn't come back to this handler because
    54:   // the signal is blocked while we're in here -- wrong; it
    55:   // is blocked from external signals, but not from signals
    56:   // generated internally...
    57:   fprintf(stderr, "re-raising...\n");
    58:   raise(signum);
    59: }
    60: 
    61: 
    62: jmp_buf sane_state;
    63: 
    64: // handler to do a longjmp
    65: void jmpHandler(int signum)
    66: {
    67:   //fprintf(stderr, "jmpHandler: I caught signal %d\n", signum);
    68:   psignal(signum, "jmpHandler: caught signal");
    69: 
    70:   // reset the signal handler to its default handler
    71:   setHandler(signum, SIG_DFL);
    72: 
    73:   // do it
    74:   //fprintf(stderr, "calling longjmp...\n");
    75:   longjmp(sane_state, 1);
    76: }
    77: 
    78: 
    79: void printAddrHandler(int signum, siginfo_t *info, void *)
    80: {
    81:   fprintf(stderr, "faulting address: %p\n", (info->si_addr));
    82: 
    83:   // reset handler and re-raise
    84:   setHandler(signum, SIG_DFL);
    85:   raise(signum);
    86: }
    87: 
    88: 
    89: // unfortunately, linux 2.4.18 seems to have some bugs w.r.t.
    90: // sigalstack... anytime MYSZ is as small as 4096, the test program
    91: // hangs repeatedly segfaulting once I get the first.. (but note that
    92: // MINSIGSTKSZ is 2048, so I should be well beyond the acknowledged
    93: // minimum); and with 8192 it works only some of the time, depending on
    94: // how things get laid out.  so I'm going to disable the alt stack
    95: // altogether, and rely on noticing that no "faulting address" is
    96: // printed if I get a stack overflow...
    97: 
    98: //#define MYSZ 4096
    99: //char mysigstack[MYSZ];
   100: 
   101: void printSegfaultAddrs()
   102: {
   103:   // allocate the alternate signal stack; I do this because I want
   104:   // the handler to work even for SEGVs caused by stack-overflow
   105:   //if (!mysigstack) {
   106:   //  mysigstack = (char*)malloc(MINSIGSTKSZ);    // "minimum signal stack size"
   107:   //}
   108: 
   109:   #if 0
   110:   // tell the library about it
   111:   struct sigaltstack sas;
   112:   sas.ss_sp = mysigstack;
   113:   sas.ss_size = MYSZ;
   114:   sas.ss_flags = SS_ONSTACK;
   115: 
   116:   if (0 > sigaltstack(&sas, NULL)) {
   117:     perror("sigaltstack");
   118:     exit(2);
   119:   }
   120:   #endif // 0
   121: 
   122: 
   123:   // NOTE: I have no idea how portable this stuff is, especially the
   124:   // 'sigaltstack' call.  It's only here as a debugging aid.  Feel
   125:   // free to #ifdef-out the entire section if necessary, but tell me
   126:   // (smcpeak@acm.org) about it so I can add detection logic.
   127: 
   128: 
   129:   // construct the handler information struct
   130:   struct sigaction sa;
   131:   std::memset(&sa, 0, sizeof(sa));
   132:   sa.sa_sigaction = printAddrHandler;
   133:   sigemptyset(&sa.sa_mask);         // don't block other signals
   134:   sa.sa_flags = SA_SIGINFO; // | SA_STACK;
   135: 
   136:   // install the handler
   137:   if (0 > sigaction(SIGSEGV, &sa, NULL)) {
   138:     perror("sigaction");
   139:     exit(2);
   140:   }
   141: }
   142: 
   143: 
   144: // ------------------ test code ------------------
   145: #ifdef TEST_MYSIG
   146: 
   147: static void infiniteRecursion()
   148: {
   149:   char buf[1024];
   150:   buf[0] = 4;
   151:   buf[1] = buf[0];     // silence an icc warning
   152:   buf[1023] = 6;
   153:   infiniteRecursion();
   154: }
   155: 
   156: int main(int argc, char **argv)
   157: {
   158:   if (argc >= 2) {
   159:     // segfault at a given addr
   160:     printSegfaultAddrs();
   161: 
   162:     if (0==strcmp(argv[1], "inf")) {
   163:       // die by stack overflow.. interesting, I can't catch it..
   164:       printf("going into infinite recursion...\n");
   165:       infiniteRecursion();
   166:     }
   167: 
   168:     int addr = strtoul(argv[1], NULL /*endp*/, 0 /*radix*/);
   169:     printf("about to access 0x%08X ...\n", addr);
   170:     *((int*)addr) = 0;
   171:     return 0;     // won't be reached for most values of 'addr'
   172:   }
   173: 
   174:   if (setjmp(sane_state) == 0) {   // normal flow
   175:     setHandler(SIGINT, printHandler);
   176:     setHandler(SIGTERM, printHandler);
   177:     setHandler(SIGUSR1, jmpHandler);
   178:     setHandler(SIGSEGV, jmpHandler);
   179:     setHandler(SIGBUS, jmpHandler);   // osx gives SIBGUS instead of SIGSEGV
   180: 
   181:     //printf("I'm pid %d waiting to be killed...\n", getpid());
   182:     //sleep(10);
   183:     printf("about to deliberately cause a segfault ...\n");
   184:     *((int*)0) = 0;    // segfault!
   185: 
   186:     printf("didn't segfault??\n");
   187:     return 2;
   188:   }
   189: 
   190:   else {         // from longjmp
   191:     printf("came back from a longjmp!\n");
   192:     printf("\nmysig works\n");
   193:     return 0;
   194:   }
   195: }
   196: 
   197: #endif // TEST_MYSIG
   198: 
   199: 
   200: #else   // cygwin -- just stubs so it compiles
   201: void setHandler(int, SignalHandler) {}
   202: void printHandler(int) {}
   203: jmp_buf sane_state;
   204: void jmpHandler(int) {}
   205: 
   206: #ifdef TEST_MYSIG
   207: int main()
   208: {
   209:   printf("mysig on cygwin: nop\n");
   210:   return 0;
   211: }
   212: #endif // TEST_MYSIG
   213: 
   214: #endif
End cpp section to elk/sm_mysig.cpp[1]
Start cpp section to elk/sm_point.cpp[1 /1 ]
     1: #line 10002 "./lpsrc/sm.pak"
     2: // point.cc            see license.txt for copyright and terms of use
     3: // code for point.h
     4: 
     5: #include "sm_point.h"
     6: #include "sm_str.h"
     7: 
     8: sm_stringBuilder& operator<< (sm_stringBuilder &sb, point const &pt)
     9: {
    10:   return sb << "(" << pt.x << ", " << pt.y << ")";
    11: }
    12: 
    13: sm_stringBuilder& operator<< (sm_stringBuilder &sb, fpoint const &pt)
    14: {
    15:   return sb << "(" << pt.x << ", " << pt.y << ")";
    16: }
End cpp section to elk/sm_point.cpp[1]
Start cpp section to elk/sm_pprint.cpp[1 /1 ]
     1: #line 10019 "./lpsrc/sm.pak"
     2: // pprint.cc
     3: // code for pprint.h
     4: 
     5: #include "sm_pprint.h"
     6: #include "sm_breaker.h"
     7: #include <stdio.h>       // sprintf
     8: #include <cstring>      // std::memcpy
     9: 
    10: 
    11: // ---------------------- PPrintOut ----------------------
    12: void PPrintStringOut::write(char const *text)
    13: {
    14:   sb << text;
    15: }
    16: 
    17: void PPrintOstreamOut::write(char const *text)
    18: {
    19:   os << text;
    20: }
    21: 
    22: 
    23: // ------------------------ PPrint ----------------------
    24: bool PPrint::warnWhenUnbalanced = true;
    25: 
    26: PPrint::PPrint(PPrintOut &o)
    27:   : line(),
    28:     lineIndent(0),
    29:     margin(72),
    30:     altIndent(2),
    31:     startText(NULL),
    32:     out(o)
    33: {}
    34: 
    35: PPrint::~PPrint()
    36: {
    37:   if (line.length() > 0) {
    38:     // hit a breakpoint if we're in the debugger, since this
    39:     // is unexpected
    40:     breaker();
    41: 
    42:     // add a final newline to get all the output out
    43:     print("\n");
    44:   }
    45: }
    46: 
    47: 
    48: struct BreakInfo {
    49:   int p;        // index in the 'line' array of the '\r'
    50:   int pCol;     // column of emitted text where the newline would go
    51:   int pInd;     // indent for the next line
    52: 
    53:   BreakInfo(int pp, int pc, int pi)
    54:     : p(pp), pCol(pc), pInd(pi) {}
    55:   BreakInfo() {}      // for use in arrays
    56: 
    57:   // when choosing breaks, we maximize this sum: the # of chars
    58:   // that will be on this line, and the # of chars available to
    59:   // fill on the next line
    60:   int sum(/*int margin*/) const {
    61:     // except, since all the margins play the same role and will
    62:     // all cancel, we don't need to consider it
    63:     return pCol +                 // this line
    64:            (/*margin*/ - pInd);   // next line
    65:   }
    66: 
    67:   // decision between two breaks
    68:   bool betterThan(BreakInfo const &obj /*, int margin*/) {
    69:     if (sum(/*margin*/) > obj.sum(/*margin*/))
    70:       { return true; }
    71: 
    72:     // tiebreaker: prefer more space on the next line, since that's
    73:     // likely to become the space available on the line after that,
    74:     // etc.
    75:     if (sum(/*margin*/) == obj.sum(/*margin*/) &&
    76:         pInd < obj.pInd)
    77:       { return true; }
    78: 
    79:     return false;
    80:   }
    81: };
    82: 
    83: 
    84: void PPrint::Setter::indent(int amt)
    85: {
    86:   for (int i=0; i<amt; i++) {
    87:     curLine << ' ';
    88:   }
    89: }
    90: 
    91: 
    92: void PPrint::Setter::set()
    93: {
    94:   // initialize the indentation stack with the line-start indentation
    95:   indentGroups.push(pprint.lineIndent);
    96: 
    97:   // loop over the actual emitted lines
    98:   while (lineIndex < pprint.line.length()) {
    99:     // indent the line by the amount at the top of the stack
   100:     curLineInd = indentGroups.top();
   101:     indent(curLineInd);
   102: 
   103:     // find all of the line breaks that would make the current line end
   104:     // before the 'margin' column
   105:     ArrayStack<BreakInfo> breaks;      // TODO: move up to avoid allocation
   106: 
   107:     // this will scan forward to find optional line breaks
   108:     int p = lineIndex;
   109: 
   110:     // column at which the line would break, if we broke at 'p'
   111:     int pCol = curLine.length();
   112: 
   113:     // the topmost entry of this stack says how far we'd indent
   114:     // at the beginning of the next line, if we broke at 'p'
   115:     ArrayStack<int> pInd;              // TODO: move up to avoid allocation
   116: 
   117:     // initialize 'pInd' with 'indentGroups', because what happens next
   118:     // is we speculatively emit, as if doing it for real and consequently
   119:     // updating 'indentGroups'
   120:     {
   121:       pInd.ensureAtLeast(indentGroups.length());
   122:       for (int i=0; i < indentGroups.length(); i++) {
   123:         pInd[i] = indentGroups[i];
   124:       }
   125:       pInd.setLength(indentGroups.length());
   126:     }
   127: 
   128:     while (p < pprint.line.length()-1 && pCol < pprint.margin) {
   129:       switch (pprint.line[p]) {
   130:         case '\r':   // optional line break
   131:           breaks.push(BreakInfo(p, pCol, pInd.top()));
   132:           pCol++;    // if *not* taken, it will be a space
   133:           break;
   134: 
   135:         case '\b':   // begin break group
   136:           pInd.push(pCol);
   137:           break;
   138: 
   139:         case '\a':   // alternate begin group
   140:           pInd.push(curLineInd + pprint.altIndent);
   141:           break;
   142: 
   143:         case '\f':   // break group end
   144:           pInd.pop();
   145:           break;
   146: 
   147:         default:     // printing character
   148:           // increases output column
   149:           pCol++;
   150:           break;
   151:       }
   152: 
   153:       p++;
   154:     }
   155: 
   156:     if (pCol < pprint.margin) {
   157:       // we got to the end of 'line' before hitting margin; emit the
   158:       // remainder as-is
   159:       emitTo(p+1 /*include newline*/);
   160:       flush();
   161:       return;
   162:     }
   163: 
   164:     if (breaks.isEmpty()) {
   165:       // no line breaks happen soon enough, so just find the first
   166:       // break and take it
   167:       while (p < pprint.line.length()-1) {
   168:         if (pprint.line[p] == '\r') {
   169:           emitTo(p /*not including '\r'*/);
   170:           lineIndex++;     // skip '\r'
   171:           curLine << '\n';
   172:           flush();
   173:           break;
   174:         }
   175: 
   176:         p++;
   177:       }
   178: 
   179:       if (p == pprint.line.length()-1) {
   180:         // no optional line breaks at all; emit remainder
   181:         emitTo(p+1 /*include newline*/);
   182:         flush();
   183:         return;
   184:       }
   185:     }
   186: 
   187:     else {
   188:       // choose the best break from among those in 'breaks'
   189:       int best = 0;
   190:       for (int i=1; i < breaks.length(); i++) {
   191:         if (breaks[i].betterThan(breaks[best] /*, pprint.margin*/)) {
   192:           best = i;
   193:         }
   194:       }
   195: 
   196:       // break the line
   197:       emitTo(breaks[best].p /*not including '\r'*/);
   198:       lineIndex++;                  // skip '\r'
   199:       curLine << '\n';
   200:       flush();
   201:     }
   202:   }
   203: }
   204: 
   205: PPrint::Setter::~Setter()
   206: {
   207:   if (indentGroups.length() != 1) {
   208:     // unbalanced groups
   209:     breaker();
   210:     if (warnWhenUnbalanced) {
   211:       std::cout << "warning: unbalanced indentation grouping in pprint input\n";
   212:     }
   213:   }
   214: }
   215: 
   216: 
   217: void PPrint::Setter::emitTo(int p)
   218: {
   219:   while (lineIndex < p) {
   220:     char ch = pprint.line[lineIndex];
   221:     switch (ch) {
   222:       case '\r':   // optional line break
   223:         // not taken, it's a space
   224:         curLine << ' ';
   225:         break;
   226: 
   227:       case '\b':   // begin break group
   228:         indentGroups.push(curLine.length());
   229:         break;
   230: 
   231:       case '\a':   // alternate begin group
   232:         indentGroups.push(curLineInd + pprint.altIndent);
   233:         break;
   234: 
   235:       case '\f':   // break group end
   236:         indentGroups.pop();
   237:         break;
   238: 
   239:       default:     // printing character
   240:         curLine << ch;
   241:         break;
   242:     }
   243: 
   244:     lineIndex++;
   245:   }
   246: }
   247: 
   248: 
   249: void PPrint::Setter::flush()
   250: {
   251:   if (pprint.startText) {
   252:     pprint.out.write(pprint.startText);
   253:   }
   254:   pprint.out.write(curLine);
   255:   curLine.clear();
   256: }
   257: 
   258: 
   259: void PPrint::set()
   260: {
   261:   // on entry, 'line' contains exactly one newline, at the end
   262:   xassert(line[line.length()-1] == '\n');
   263: 
   264:   Setter s(*this);
   265:   s.set();
   266: 
   267:   // clear the line, ready for the next one
   268:   line.setLength(0);
   269: }
   270: 
   271: 
   272: void append(ArrayStack<char> &line, char const *src, int len)
   273: {
   274:   line.ensureAtLeast(line.length() + len);
   275:   std::memcpy(line.getArrayNC()+line.length(),     // dest
   276:          src, len);                           // src, len
   277:   line.setLength(line.length() + len);
   278: }
   279: 
   280: void PPrint::print(char const *text)
   281: {
   282:   // any newlines?
   283:   char const *p = text;
   284:   while (*p != 0) {
   285:     if (*p == '\n') {
   286:       // transfer everything up to the newline into the setting buffer
   287:       int copylen = p-text+1;
   288:       append(line, text, copylen);
   289: 
   290:       // advance 'text' so the next batch will begin after what we
   291:       // just transferred
   292:       text += copylen;
   293: 
   294:       // set the now-complete line
   295:       set();
   296:     }
   297: 
   298:     p++;
   299:   }
   300: 
   301:   // copy the remainder into the line buffer
   302:   append(line, text, p-text);
   303: }
   304: 
   305: 
   306: PPrint& PPrint::operator<< (int i)
   307: {
   308:   char tmp[40];
   309:   sprintf(tmp, "%d", i);
   310:   print(tmp);
   311:   return *this;
   312: }
   313: 
   314: PPrint& PPrint::operator<< (char const *s)
   315: {
   316:   print(s);
   317:   return *this;
   318: }
   319: 
   320: 
   321: PPrintToString::~PPrintToString()
   322: {}
   323: 
   324: 
   325: // --------------------- test code -----------------------
   326: #ifdef TEST_PPRINT
   327: 
   328: PPrintToString pp;
   329: 
   330: int main()
   331: {
   332:   pp.margin = 30;
   333:   pp.startText = "; ";
   334: 
   335:   std::cout << "         1    1    2    2    3\n";
   336:   std::cout << "1---5----0----5----0----5----0\n";
   337: 
   338:   pp << "int foo()\n"
   339:         "{\n"
   340:         ;
   341:   pp.ind(+2);
   342:   pp << "printf(\b\"hello there %d!\\n\",\r123456789\f);\n";
   343:   pp << "bar(\b1 +\r2 +\r3 +\r4 +\r5 +\r6 +\r7 +\r8 +\r9 +\r10\f);\n";
   344:   pp << "baz(\b\"a really long line that has no optional breaks at all\"\f);\n";
   345:   pp << "zoo(\b\"one break is here, but it is very\",\r\"far from the start\"\f);\n";
   346:   pp << "assert(\bx ==\ry &&\rz ==\rw &&\r"
   347:                "(\bmoron !=\rfool ||\rtaxes->theRich\f)\f);\n";
   348:   pp << "\aforall(x, y, z). if {\r"
   349:           "x == yooey_more;\r"
   350:           "yowza != fooey;\f\r"
   351:         "} {\a\r"
   352:           "z(x,y,z)==3;\r"
   353:           "ay_caramba;\f\r"
   354:         "}\n";
   355:   pp.ind(-2);
   356:   pp << "}\n";
   357: 
   358:   std::cout << pp.sb;
   359: 
   360:   return 0;
   361: }
   362: 
   363: 
   364: #endif // TEST_PPRINT
End cpp section to elk/sm_pprint.cpp[1]
Start cpp section to elk/sm_regexp.cpp[1 /1 ]
     1: #line 10384 "./lpsrc/sm.pak"
     2: // smregexp.cc
     3: // code for smregexp.h
     4: 
     5: #include "sm_regexp.h"
     6: #include "sm_str.h"
     7: #include "sm_exc.h"
     8: 
     9: #include <stddef.h>       // size_t
    10: 
    11: // for now, I implement everything using the libc POSIX regex
    12: // facilities
    13: //
    14: // linux (etc.) has proper declarations in regex.h, but FreeBSD (and
    15: // other BSDs?) has regex.h contents that do not compile under C++,
    16: // and apparently gnuregex.h is the substitute that does
    17: #ifndef __FreeBSD__
    18:   #include <regex.h>
    19: #else
    20:   #include <gnuregex.h>
    21: #endif
    22: 
    23: 
    24: // get an error sm_string
    25: static sm_string regexpErrorString(regex_t const *pat, int code)
    26: {
    27:   // find out how long the error sm_string is; this size
    28:   // includes the final NUL byte
    29:   int size = regerror(code, pat, NULL, 0);
    30: 
    31:   // get the sm_string
    32:   sm_string ret(size);
    33:   regerror(code, pat, ret.pchar(), size);
    34: 
    35:   return ret;
    36: }
    37: 
    38: // throw an exception
    39: static void regexpError(regex_t const *pat, int code) NORETURN;
    40: static void regexpError(regex_t const *pat, int code)
    41: {
    42:   xbase(regexpErrorString(pat, code));
    43: }
    44: 
    45: 
    46: // -------------------- Regexp --------------------------
    47: // interpretation of 'impl' field
    48: #define PAT ((regex_t*&)impl)
    49: 
    50: Regexp::Regexp(char const *exp, CFlags flags)
    51: {
    52:   PAT = new regex_t;
    53: 
    54:   int f = REG_EXTENDED;    // "extended" language
    55: 
    56:   // if the values I chose line up perfectly with the values used by
    57:   // libc, then I don't have to interpret them (hopefully the
    58:   // optimizer will discover that the 'if' test is constant
    59:   // (gcc-2.95.3's optimizer does); I can't do it with the
    60:   // preprocessor because it can't see the enumerator values)
    61:   if (REG_ICASE==ICASE && REG_NOSUB==NOSUB) {
    62:     f |= (int)flags;
    63:   }
    64:   else {
    65:     // interpret my flags
    66:     if (flags & ICASE) f |= REG_ICASE;
    67:     if (flags & NOSUB) f |= REG_NOSUB;
    68:   }
    69: 
    70:   int code = regcomp(PAT, exp, f);
    71:   if (code) {
    72:     // deallocate the pattern buffer before throwing the exception
    73:     sm_string msg = regexpErrorString(PAT, code);
    74:     delete PAT;
    75:     xbase(msg);
    76:   }
    77: }
    78: 
    79: Regexp::~Regexp()
    80: {
    81:   regfree(PAT);
    82:   delete PAT;
    83: }
    84: 
    85: 
    86: void Regexp::err(int code)
    87: {
    88:   regexpError(PAT, code);
    89: }
    90: 
    91: 
    92: bool Regexp::match(char const *str, EFlags flags)
    93: {
    94:   int f = 0;
    95: 
    96:   // same thing as above
    97:   if (REG_NOTBOL==NOTBOL && REG_NOTEOL==NOTEOL) {
    98:     f = (int)flags;
    99:   }
   100:   else {
   101:     if (flags & NOTBOL) f |= REG_NOTBOL;
   102:     if (flags & NOTEOL) f |= REG_NOTEOL;
   103:   }
   104: 
   105:   int code = regexec(PAT, str, 0, NULL, f);
   106:   if (code == 0) {
   107:     return true;
   108:   }
   109:   else if (code == REG_NOMATCH) {
   110:     return false;
   111:   }
   112:   else {
   113:     err(code);
   114:   }
   115: }
   116: 
   117: 
   118: #undef PAT
   119: 
   120: 
   121: // --------------- convenience functions ---------------
   122: bool regexpMatch(char const *str, char const *exp)
   123: {
   124:   Regexp pat(exp, Regexp::NOSUB);
   125:   return pat.match(str);
   126: }
   127: 
   128: 
   129: // ----------------- test code --------------------
   130: #ifdef TEST_SMREGEXP
   131: 
   132: #include <stdlib.h>    // exit
   133: #include <stdio.h>     // printf
   134: 
   135: 
   136: void matchVector(char const *str, char const *exp, bool expect)
   137: {
   138:   bool actual = regexpMatch(str, exp);
   139:   if (actual != expect) {
   140:     printf("regexp failure\n");
   141:     printf("  str: %s\n", str);
   142:     printf("  exp: %s\n", exp);
   143:     printf("  expect: %s\n", (expect? "true" : "false"));
   144:     printf("  actual: %s\n", (actual? "true" : "false"));
   145:     exit(2);
   146:   }
   147: }
   148: 
   149: 
   150: int main()
   151: {
   152:   matchVector("abc", "a", true);
   153:   matchVector("abc", "b", true);
   154:   matchVector("abc", "c", true);
   155:   matchVector("abc", "d", false);
   156: 
   157:   matchVector("abc", "^a", true);
   158:   matchVector("abc", "^b", false);
   159:   matchVector("abc", "b$", false);
   160:   matchVector("abc", "c$", true);
   161:   matchVector("abc", "^d", false);
   162: 
   163:   printf("regexp works\n");
   164:   return 0;
   165: }
   166: 
   167: 
   168: #endif // TEST_SMREGEXP
End cpp section to elk/sm_regexp.cpp[1]
Start cpp section to elk/sm_srcloc.cpp[1 /1 ]
     1: #line 10553 "./lpsrc/sm.pak"
     2: // srcloc.cc            see license.txt for copyright and terms of use
     3: // code for srcloc.h
     4: 
     5: #include "sm_srcloc.h"
     6: #include "sm_autofile.h"
     7: #include "sm_array.h"
     8: #include "sm_syserr.h"
     9: #include "sm_trace.h"
    10: #include "sm_hashline.h"
    11: 
    12: #include <stdio.h>      // fprintf
    13: #include <cstring>     // std::memcpy
    14: 
    15: 
    16: // this parameter controls the frequency of Markers in
    17: // the marker index; lower period makes the index
    18: // faster but use more space
    19: enum { MARKER_PERIOD = 100 };    // 100 is about a 10% overhead
    20: 
    21: 
    22: // ------------------------- File -----------------------
    23: void addLineLength(ArrayStack<unsigned char> &lengths, int len)
    24: {
    25:   while (len >= 255) {
    26:     // add a long-line marker, which represents 254 chars of input
    27:     lengths.push(255);
    28:     len -= 254;
    29:   }
    30: 
    31:   // add the short count at the end
    32:   lengths.push((unsigned char)len);
    33: }
    34: 
    35: 
    36: SourceLocManager::File::File(char const *n, SourceLoc aStartLoc)
    37:   : name(n),
    38:     startLoc(aStartLoc),     // assigned by SourceLocManager
    39:     hashLines(NULL),
    40: 
    41:     // valid marker/col for the first char in the file
    42:     marker(0, 1, 0),
    43:     markerCol(1)
    44: {
    45:   AutoFILE fp(name, "r");
    46: 
    47:   // the buffering that FILE would do would be wasted, so
    48:   // make it unbuffered (if this causes a problem on some
    49:   // system it can be commented-out)
    50:   setbuf(fp, NULL);
    51: 
    52:   // These are growable versions of the indexes.  They will be
    53:   // discarded after the file has been read.  They keep track of their
    54:   // own 'next index' values.  I chose to use a growable array instead
    55:   // of making two passes over the file to reduce i/o.
    56:   ArrayStack<unsigned char> lineLengths;
    57:   ArrayStack<Marker> index;
    58: 
    59:   // put a marker at the start for uniformity
    60:   index.push(Marker(0, 1, 0));
    61: 
    62:   // how many lines to go before I insert the next marker
    63:   int indexDelay = MARKER_PERIOD;
    64: 
    65:   // where I am in the file
    66:   int charOffset = 0;
    67:   int lineNum = 1;
    68:   int lineLen = 0;       // length of current line, so far
    69: 
    70:   // read the file, computing information about line lengths
    71:   enum { BUFLEN=8192 };
    72:   char buf[BUFLEN];
    73:   for (;;) {
    74:     // read a buffer of data
    75:     int len = fread(buf, 1, BUFLEN, fp);
    76:     if (len < 0) {
    77:       xsyserror("fread", name);
    78:     }
    79:     if (len==0) {
    80:       break;
    81:     }
    82: 
    83:     // the code that follows can be seen as abstracting the data
    84:     // contained in buf[start] through buf[end-1] and adding that
    85:     // information to the summary variables above
    86:     char const *start = buf;      // beginning of unaccounted-for chars
    87:     char const *p = buf;          // scan pointer
    88:     char const *end = buf+len;    // end of unaccounted-for chars
    89: 
    90:     // loop over the lines in 'buf'
    91:     while (start < end) {
    92:       // scan to the next newline
    93:       while (p<end && *p!='\n') {
    94:         p++;
    95:       }
    96:       if (p==end) {
    97:         break;
    98:       }
    99:       xassert(*p == '\n');
   100: 
   101:       // account for [start,p)
   102:       charOffset += p-start;
   103:       lineLen += p-start;
   104:       start = p;
   105: 
   106:       // account for the newline at '*p'
   107:       addLineLength(lineLengths, lineLen);
   108:       charOffset++;
   109:       lineNum++;
   110:       lineLen = 0;
   111: 
   112:       p++;
   113:       start++;
   114: 
   115:       if (--indexDelay == 0) {
   116:         // insert a marker to remember this location
   117:         index.push(Marker(charOffset, lineNum, lineLengths.length()));
   118:         indexDelay = MARKER_PERIOD;
   119:       }
   120:     }
   121: 
   122:     // move [start,p) into 'lineLen'
   123:     charOffset += p-start;
   124:     lineLen += p-start;
   125:     start = p;
   126:     xassert(start == end);
   127:   }
   128: 
   129:   // handle the last line; in the usual case, where a newline is
   130:   // the last character, the final line will have 0 length, but
   131:   // we encode that anyway since it helps the decode phase below
   132:   addLineLength(lineLengths, lineLen);
   133:   charOffset += lineLen;
   134: 
   135:   // move computed information into 'this'
   136:   this->numChars = charOffset;
   137:   this->numLines = lineNum-1;
   138:   if (numLines == 0) {
   139:     // a file with no newlines
   140:     this->avgCharsPerLine = numChars;
   141:   }
   142:   else {
   143:     this->avgCharsPerLine = numChars / numLines;
   144:   }
   145: 
   146:   this->lineLengthsSize = lineLengths.length();
   147:   this->lineLengths = new unsigned char[lineLengthsSize];
   148:   std::memcpy(this->lineLengths, lineLengths.getArray(),
   149:          lineLengthsSize * sizeof(this->lineLengths[0]));
   150: 
   151:   this->indexSize = index.length();
   152:   this->index = new Marker[indexSize];
   153:   std::memcpy(this->index, index.getArray(),
   154:          indexSize * sizeof(this->index[0]));
   155: 
   156:   // 'fp' closed by the AutoFILE
   157: }
   158: 
   159: 
   160: SourceLocManager::File::~File()
   161: {
   162:   if (hashLines) {
   163:     delete hashLines;
   164:   }
   165:   delete[] lineLengths;
   166: }
   167: 
   168: 
   169: void SourceLocManager::File::resetMarker()
   170: {
   171:   marker.charOffset = 0;
   172:   marker.lineOffset = 1;
   173:   marker.arrayOffset = 0;
   174:   markerCol = 1;
   175: }
   176: 
   177: 
   178: // it's conceivable gcc is smart enough to recognize
   179: // the induction variable, if I inline this..
   180: inline void SourceLocManager::File::advanceMarker()
   181: {
   182:   int len = (int)lineLengths[marker.arrayOffset];
   183:   if (len < 255) {
   184:     // normal length line
   185:     marker.charOffset += len+1;     // +1 for newline
   186:     marker.lineOffset++;
   187:     marker.arrayOffset++;
   188:     markerCol = 1;
   189:   }
   190:   else {
   191:     // fragment of a long line, representing 254 characters
   192:     marker.charOffset += 254;
   193:     marker.arrayOffset++;
   194:     markerCol += 254;
   195:   }
   196: }
   197: 
   198: 
   199: int SourceLocManager::File::lineToChar(int lineNum)
   200: {
   201:   if (lineNum == numLines+1) {
   202:     // end of file location
   203:     return numChars;
   204:   }
   205: 
   206:   xassert(1 <= lineNum && lineNum <= numLines);
   207: 
   208:   // check to see if the marker is already close
   209:   if (marker.lineOffset <= lineNum &&
   210:                            lineNum < marker.lineOffset + MARKER_PERIOD) {
   211:     // use the marker as-is
   212:   }
   213:   else {
   214:     // do a binary search on the index to find the marker whose
   215:     // lineOffset is closest to 'lineNum' without going over
   216:     int low = 0;              // lowest index under consideration
   217:     int high = indexSize-1;   // highest index under consideration
   218:     while (low < high) {
   219:       // check the midpoint (round up to ensure progress when low+1 == high)
   220:       int mid = (low+high+1)/2;
   221:       if (index[mid].lineOffset > lineNum) {
   222:         // too high
   223:         high = mid-1;
   224:       }
   225:       else {
   226:         // too low or just right
   227:         low = mid;
   228:       }
   229:     }
   230: 
   231:     // copy this index marker into our primary marker
   232:     marker = index[low];
   233:     markerCol = 1;            // all index markers implicitly have column 1
   234:   }
   235: 
   236:   xassert(marker.lineOffset <= lineNum);
   237: 
   238:   // move the marker down the array until it arrives at
   239:   // the desired line
   240:   while (marker.lineOffset < lineNum) {
   241:     advanceMarker();
   242:   }
   243: 
   244:   // make sure we never go beyond the end of the array
   245:   xassert(marker.arrayOffset < lineLengthsSize);
   246: 
   247:   // if we didn't move the marker, we might not be in column 1
   248:   return marker.charOffset - (markerCol-1);
   249: }
   250: 
   251: 
   252: int SourceLocManager::File::lineColToChar(int lineNum, int col)
   253: {
   254:   // use the above function first
   255:   int offset = lineToChar(lineNum);
   256: 
   257:   // now, we use an property established by the previous function:
   258:   // the marker points at the line of interest, possibly offset from
   259:   // the line start by 'markerCol-1' places
   260:   if (col <= markerCol) {
   261:     // the column we want is not even beyond the marker's column,
   262:     // so it's surely not beyond the end of the line, so do the
   263:     // obvious thing
   264:     return offset + (col-1);
   265:   }
   266: 
   267:   // we're at least as far as the marker; move the offset up to
   268:   // this far, and subtract from 'col' so it now represents the
   269:   // number of chars yet to traverse on this line
   270:   offset = marker.charOffset;
   271:   col -= markerCol;       // 'col' is now 0-based
   272: 
   273:   // march to the end of the line, looking for either the end or a
   274:   // line length component which goes beyond 'col'; I don't move the
   275:   // marker itself out of concern for preserving the locality of
   276:   // future accesses
   277:   int index = marker.arrayOffset;
   278:   for (;;) {
   279:     int len = (int)lineLengths[index];
   280:     if (col <= len) {
   281:       // 'col' doesn't go beyond this component, we're done
   282:       // (even if len==255 it still works)
   283:       return offset + col;
   284:     }
   285:     if (len < 255) {
   286:       // the line ends here, truncate and we're done
   287:       SourceLocManager::shortLineCount++;
   288:       return offset + len;
   289:     }
   290: 
   291:     // the line continues
   292:     xassertdb(len == 255);
   293: 
   294:     col -= 254;
   295:     offset += 254;
   296:     xassertdb(col > 0);
   297: 
   298:     index++;
   299:     xassert(index < lineLengthsSize);
   300:   }
   301: }
   302: 
   303: 
   304: void SourceLocManager::File::charToLineCol(int offset, int &line, int &col)
   305: {
   306:   if (offset == numChars) {
   307:     // end of file location
   308:     line = numLines+1;
   309:     col = 1;
   310:     return;
   311:   }
   312: 
   313:   xassert(0 <= offset && offset < numChars);
   314: 
   315:   // check if the marker is close enough
   316:   if (marker.charOffset <= offset &&
   317:                            offset < marker.charOffset + MARKER_PERIOD*avgCharsPerLine) {
   318:     // use as-is
   319:   }
   320:   else {
   321:     // binary search, like above
   322:     int low = 0;
   323:     int high = indexSize-1;
   324:     while (low < high) {
   325:       // check midpoint
   326:       int mid = (low+high+1)/2;
   327:       if (index[mid].charOffset > offset) {
   328:         high = mid-1;
   329:       }
   330:       else {
   331:         low = mid;
   332:       }
   333:     }
   334: 
   335:     // copy this marker
   336:     marker = index[low];
   337:     markerCol = 1;
   338:   }
   339: 
   340:   xassert(marker.charOffset <= offset);
   341: 
   342:   // move the marker until it's within one spot of moving
   343:   // beyond the offset
   344:   while (marker.charOffset + lineLengths[marker.arrayOffset] < offset) {
   345:     advanceMarker();
   346:   }
   347: 
   348:   // make sure we never go beyond the end of the array
   349:   xassert(marker.arrayOffset < lineLengthsSize);
   350: 
   351:   // read off line/col
   352:   line = marker.lineOffset;
   353:   col = markerCol + (offset - marker.charOffset);
   354: }
   355: 
   356: 
   357: void SourceLocManager::File::addHashLine
   358:   (int ppLine, int origLine, char const *origFname)
   359: {
   360:   if (!hashLines) {
   361:     hashLines = new HashLineMap(name);
   362:   }
   363:   hashLines->addHashLine(ppLine, origLine, origFname);
   364: }
   365: 
   366: void SourceLocManager::File::doneAdding()
   367: {
   368:   if (hashLines) {
   369:     hashLines->doneAdding();
   370:   }
   371:   else {
   372:     // nothing to consolidate, the NULL pointer is valid and
   373:     // will cause the map to be ignored, so do nothing
   374:   }
   375: }
   376: 
   377: 
   378: // ----------------------- StaticLoc -------------------
   379: SourceLocManager::StaticLoc::~StaticLoc()
   380: {}
   381: 
   382: 
   383: // ----------------------- SourceLocManager -------------------
   384: int SourceLocManager::shortLineCount = 0;
   385: 
   386: SourceLocManager *sourceLocManager = NULL;
   387: 
   388: 
   389: SourceLocManager::SourceLocManager()
   390:   : files(),
   391:     recent(NULL),
   392:     statics(),
   393:     nextLoc(toLoc(1)),
   394:     nextStaticLoc(toLoc(0)),
   395:     maxStaticLocs(100),
   396:     useHashLines(true)
   397: {
   398:   if (!sourceLocManager) {
   399:     sourceLocManager = this;
   400:   }
   401: 
   402:   // slightly clever: treat SL_UNKNOWN as a static
   403:   SourceLoc u = encodeStatic(StaticLoc("<noloc>", 0,1,1));
   404:   xassert(u == SL_UNKNOWN);
   405:   PRETEND_USED(u);     // silence warning in case xasserts are turned off
   406: 
   407:   // similarly for SL_INIT
   408:   u = encodeStatic(StaticLoc("<init>", 0,1,1));
   409:   xassert(u == SL_INIT);
   410:   PRETEND_USED(u);
   411: }
   412: 
   413: SourceLocManager::~SourceLocManager()
   414: {
   415:   if (sourceLocManager == this) {
   416:     sourceLocManager = NULL;
   417:   }
   418: }
   419: 
   420: 
   421: // find it, or return NULL
   422: SourceLocManager::File *SourceLocManager::findFile(char const *name)
   423: {
   424:   if (!this) {
   425:     // it's quite common to forget to do this, and this function is
   426:     // almost always the one which segfaults in that case, so I'll
   427:     // make the error message a bit nicer to save a trip through
   428:     // the debugger
   429:     xfailure("you have to create a SourceLocManager in your main() function");
   430:   }
   431: 
   432:   if (recent && recent->name.equals(name)) {
   433:     return recent;
   434:   }
   435: 
   436:   FOREACH_OBJLIST_NC(File, files, iter) {
   437:     if (iter.data()->name.equals(name)) {
   438:       return recent = iter.data();
   439:     }
   440:   }
   441: 
   442:   return NULL;
   443: }
   444: 
   445: // find it or make it
   446: SourceLocManager::File *SourceLocManager::getFile(char const *name)
   447: {
   448:   File *f = findFile(name);
   449:   if (!f) {
   450:     // read the file
   451:     f = new File(name, nextLoc);
   452:     files.append(f);
   453: 
   454:     // bump 'nextLoc' according to how long that file was,
   455:     // plus 1 so it can own the position equal to its length
   456:     nextLoc = toLoc(f->startLoc + f->numChars + 1);
   457:   }
   458: 
   459:   return recent = f;
   460: }
   461: 
   462: 
   463: SourceLoc SourceLocManager::encodeOffset(
   464:   char const *filename, int charOffset)
   465: {
   466:   xassert(charOffset >= 0);
   467: 
   468:   File *f = getFile(filename);
   469:   return toLoc(f->startLoc + charOffset);
   470: }
   471: 
   472: 
   473: SourceLoc SourceLocManager::encodeLineCol(
   474:   char const *filename, int line, int col)
   475: {
   476:   xassert(line >= 1);
   477:   xassert(col >= 1);
   478: 
   479:   File *f = getFile(filename);
   480: 
   481:   // map from a line number to a char offset
   482:   #if 1  // new
   483:     int charOffset = f->lineColToChar(line, col);
   484:     return toLoc(toInt(f->startLoc) + charOffset);
   485:   #else  // old
   486:     int charOffset = f->lineToChar(line);
   487:     return toLoc(toInt(f->startLoc) + charOffset + (col-1));
   488:   #endif
   489: }
   490: 
   491: 
   492: SourceLoc SourceLocManager::encodeStatic(StaticLoc const &obj)
   493: {
   494:   if (-toInt(nextStaticLoc) == maxStaticLocs) {
   495:     // Each distinct static location should correspond to a single
   496:     // place in the source code.  If one place in the source is creating
   497:     // a given static location over and over, that's bad because it
   498:     // quickly leads to poor performance when storing and decoding them.
   499:     // Instead, make one and re-use it.
   500:     //
   501:     // If this message is being printed because the program is just
   502:     // really big and has lots of distinct static locations, then you
   503:     // can increase maxStaticLocs manually.
   504:     fprintf(stderr,
   505:       "Warning: You've created %d static locations, which is symptomatic\n"
   506:       "of a bug.  See %s, line %d.\n",
   507:       maxStaticLocs, __FILE__, __LINE__);
   508:   }
   509: 
   510:   // save this location
   511:   statics.append(new StaticLoc(obj));
   512: 
   513:   // return current index, yield next
   514:   SourceLoc ret = nextStaticLoc;
   515:   nextStaticLoc = toLoc(toInt(ret) - 1);
   516:   return ret;
   517: }
   518: 
   519: 
   520: SourceLocManager::File *SourceLocManager::findFileWithLoc(SourceLoc loc)
   521: {
   522:   // check cache
   523:   if (recent && recent->hasLoc(loc)) {
   524:     return recent;
   525:   }
   526: 
   527:   // iterative walk
   528:   FOREACH_OBJLIST_NC(File, files, iter) {
   529:     if (iter.data()->hasLoc(loc)) {
   530:       return recent = iter.data();
   531:     }
   532:   }
   533: 
   534:   // the user gave me a value that I never made!
   535:   xfailure("invalid source location");
   536:   return NULL;    // silence warning
   537: }
   538: 
   539: 
   540: SourceLocManager::StaticLoc const *SourceLocManager::getStatic(SourceLoc loc)
   541: {
   542:   int index = -toInt(loc);
   543:   return statics.nthC(index);
   544: }
   545: 
   546: 
   547: void SourceLocManager::decodeOffset(
   548:   SourceLoc loc, char const *&filename, int &charOffset)
   549: {
   550:   // check for static
   551:   if (isStatic(loc)) {
   552:     StaticLoc const *s = getStatic(loc);
   553:     filename = s->name.pcharc();
   554:     charOffset = s->offset;
   555:     return;
   556:   }
   557: 
   558:   File *f = findFileWithLoc(loc);
   559:   filename = f->name.pcharc();
   560:   charOffset = toInt(loc) - toInt(f->startLoc);
   561: 
   562:   if (useHashLines && f->hashLines) {
   563:     // we can't pass charOffsets directly through the #line map, so we
   564:     // must first map to line/col and then back to charOffset after
   565:     // going through the map
   566: 
   567:     // map to a pp line/col
   568:     int ppLine, ppCol;
   569:     f->charToLineCol(charOffset, ppLine, ppCol);
   570: 
   571:     // map to original line/file
   572:     int origLine;
   573:     char const *origFname;
   574:     f->hashLines->map(ppLine, origLine, origFname);
   575: 
   576:     // get a File for the original file; this opens that file
   577:     // and scans it for line boundaries
   578:     File *orig = getFile(origFname);
   579: 
   580:     // use that map to get an offset, truncating columns that are
   581:     // beyond the true line ending (happens due to macro expansion)
   582:     charOffset = orig->lineColToChar(origLine, ppCol);
   583: 
   584:     // filename is whatever #line said
   585:     filename = origFname;
   586:   }
   587: }
   588: 
   589: 
   590: void SourceLocManager::decodeLineCol(
   591:   SourceLoc loc, char const *&filename, int &line, int &col)
   592: {
   593:   if (!this) {
   594:     // didn't initialize a loc manager.. but maybe we can survive?
   595:     if (loc == SL_UNKNOWN) {
   596:       filename = "<noloc>";
   597:       line = 1;
   598:       col = 1;
   599:       return;
   600:     }
   601:     else {
   602:       xfailure("you have to create a SourceLocManager in your main() function");
   603:     }
   604:   }
   605: 
   606:   // check for static
   607:   if (isStatic(loc)) {
   608:     StaticLoc const *s = getStatic(loc);
   609:     filename = s->name.pcharc();
   610:     line = s->line;
   611:     col = s->col;
   612:     return;
   613:   }
   614: 
   615:   File *f = findFileWithLoc(loc);
   616:   filename = f->name.pcharc();
   617:   int charOffset = toInt(loc) - toInt(f->startLoc);
   618: 
   619:   f->charToLineCol(charOffset, line, col);
   620: 
   621:   if (useHashLines && f->hashLines) {
   622:     // use the #line map to determine a new file/line pair; simply
   623:     // assume that the column information is still correct, though of
   624:     // course in C, due to macro expansion, it isn't always
   625:     f->hashLines->map(line, line, filename);
   626:   }
   627: }
   628: 
   629: 
   630: char const *SourceLocManager::getFile(SourceLoc loc)
   631: {
   632:   char const *name;
   633:   int ofs;
   634:   decodeOffset(loc, name, ofs);
   635:   return name;
   636: }
   637: 
   638: 
   639: int SourceLocManager::getOffset(SourceLoc loc)
   640: {
   641:   char const *name;
   642:   int ofs;
   643:   decodeOffset(loc, name, ofs);
   644:   return ofs;
   645: }
   646: 
   647: 
   648: int SourceLocManager::getLine(SourceLoc loc)
   649: {
   650:   char const *name;
   651:   int line, col;
   652:   decodeLineCol(loc, name, line, col);
   653:   return line;
   654: }
   655: 
   656: 
   657: int SourceLocManager::getCol(SourceLoc loc)
   658: {
   659:   char const *name;
   660:   int line, col;
   661:   decodeLineCol(loc, name, line, col);
   662:   return col;
   663: }
   664: 
   665: 
   666: sm_string SourceLocManager::getString(SourceLoc loc)
   667: {
   668:   char const *name;
   669:   int line, col;
   670:   decodeLineCol(loc, name, line, col);
   671: 
   672:   return sm_stringc << name << ":" << line << ":" << col;
   673: }
   674: 
   675: sm_string SourceLocManager::getLCString(SourceLoc loc)
   676: {
   677:   char const *name;
   678:   int line, col;
   679:   decodeLineCol(loc, name, line, col);
   680: 
   681:   return sm_stringc << line << ":" << col;
   682: }
   683: 
   684: 
   685: sm_string locToStr(SourceLoc sl)
   686: {
   687:   return sourceLocManager->getString(sl);
   688: }
   689: 
   690: 
   691: // -------------------------- test code ----------------------
   692: #ifdef TEST_SRCLOC
   693: 
   694: #include "sm_test.h"
   695: #include "sm_strtokp.h"
   696: 
   697: #include <stdlib.h>      // rand, exit, system
   698: 
   699: SourceLocManager mgr;
   700: int longestLen=0;
   701: 
   702: // given a location, decode it into line/col and then re-encode,
   703: // and check that the new encoding matches the old
   704: void testRoundTrip(SourceLoc loc)
   705: {
   706:   char const *fname;
   707:   int line, col;
   708:   mgr.decodeLineCol(loc, fname, line, col);
   709: 
   710:   if (col > longestLen) {
   711:     longestLen = col;
   712:   }
   713: 
   714:   SourceLoc loc2 = mgr.encodeLineCol(fname, line, col);
   715: 
   716:   xassert(loc == loc2);
   717: }
   718: 
   719: 
   720: // these are some long lines to test their handling
   721: // 109: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   722: // 209: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   723: // 309: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   724: // 509: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   725: // 609: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   726: // 1210: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   727: // 2410: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   728: // 4810: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   729: // 9610: 1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------1---------2---------3---------4---------5---------6---------7---------8---------9---------10--------
   730: 
   731: 
   732: 
   733: // location in SourceLoc and line/col
   734: class BiLoc {
   735: public:
   736:   int line, col;
   737:   SourceLoc loc;
   738: };
   739: 
   740: 
   741: // given a file, compute SourceLocs throughout it and verify
   742: // that round-trip encoding works
   743: void testFile(char const *fname)
   744: {
   745:   // find the file's length
   746:   int len;
   747:   {
   748:     AutoFILE fp(fname, "r");
   749: 
   750:     fseek(fp, 0, SEEK_END);
   751:     len = (int)ftell(fp);
   752:   }
   753: 
   754:   // get locations for the start and end
   755:   SourceLoc start = mgr.encodeOffset(fname, 0);
   756:   SourceLoc end = mgr.encodeOffset(fname, len-1);
   757: 
   758:   // check expectations for start
   759:   xassert(mgr.getLine(start) == 1);
   760:   xassert(mgr.getCol(start) == 1);
   761: 
   762:   // test them
   763:   testRoundTrip(start);
   764:   testRoundTrip(end);
   765: 
   766:   // temporary
   767:   //testRoundTrip((SourceLoc)11649);
   768: 
   769:   BiLoc *bi = new BiLoc[len+1];
   770:   char const *dummy;
   771: 
   772:   // test all positions, forward sequential; also build the
   773:   // map for the random test; note that 'len' is considered
   774:   // a valid source location even though it corresponds to
   775:   // the char just beyond the end
   776:   int i;
   777:   for (i=0; i<=len; i++) {
   778:     SourceLoc loc = mgr.encodeOffset(fname, i);
   779:     testRoundTrip(loc);
   780: 
   781:     bi[i].loc = loc;
   782:     mgr.decodeLineCol(loc, dummy, bi[i].line, bi[i].col);
   783:   }
   784: 
   785:   // backward sequential
   786:   for (i=len; i>0; i--) {
   787:     SourceLoc loc = mgr.encodeOffset(fname, i);
   788:     testRoundTrip(loc);
   789:   }
   790: 
   791:   // random access, both mapping directions
   792:   for (i=0; i<=len; i++) {
   793:     int j = rand()%(len+1);
   794:     int dir = rand()%2;
   795: 
   796:     if (dir==0) {
   797:       // test loc -> line/col map
   798:       int line, col;
   799:       mgr.decodeLineCol(bi[j].loc, dummy, line, col);
   800:       xassert(line == bi[j].line);
   801:       xassert(col == bi[j].col);
   802:     }
   803:     else {
   804:       // test line/col -> loc map
   805:       SourceLoc loc = mgr.encodeLineCol(fname, bi[j].line, bi[j].col);
   806:       xassert(loc == bi[j].loc);
   807:     }
   808:   }
   809: 
   810:   delete[] bi;
   811: }
   812: 
   813: 
   814: // decode with given expectation, complain if it doesn't match
   815: void expect(SourceLoc loc, char const *expFname, int expLine, int expCol)
   816: {
   817:   char const *fname;
   818:   int line, col;
   819:   mgr.decodeLineCol(loc, fname, line, col);
   820: 
   821:   if (0!=strcmp(fname, expFname) ||
   822:       line != expLine ||
   823:       col != expCol) {
   824:     printf("expected %s:%d:%d, but got %s:%d:%d\n",
   825:            expFname, expLine, expCol,
   826:            fname, line, col);
   827:     exit(2);
   828:   }
   829: }
   830: 
   831: 
   832: // this is a macro that will expand to more text than the call site,
   833: // to test column truncation
   834: #define EXPANDER int blah_de_blah_de_frickin_blah;
   835: EXPANDER
   836: 
   837: 
   838: // should this be exported?
   839: sm_string locString(char const *fname, int line, int col)
   840: {
   841:   return sm_stringc << fname << ":" << line << ":" << col;
   842: }
   843: 
   844: 
   845: void testHashMap()
   846: {
   847:   // run the preprocessor
   848:   system("cpp -DTEST_SRCLOC srcloc.cc >srcloc.tmp");
   849: 
   850:   SourceLocManager::File *pp = mgr.getInternalFile("srcloc.tmp");
   851:   SourceLocManager::File *orig = mgr.getInternalFile("srcloc.cc");
   852: 
   853:   // read srcloc.tmp and install the hash maps
   854:   int expanderLine=0;
   855:   {
   856:     AutoFILE fp("srcloc.tmp", "r");
   857: 
   858:     enum { SZ=256 };
   859:     char buf[SZ];
   860:     int ppLine=0;
   861:     while (fgets(buf, SZ, fp)) {
   862:       if (buf[std::strlen(buf)-1] == '\n') {
   863:         ppLine++;
   864:       }
   865: 
   866:       if (0==std::memcmp(buf, "int blah_de_blah", 16)) {
   867:         expanderLine = ppLine;
   868:       }
   869: 
   870:       if (buf[0]!='#') continue;
   871: 
   872:       // break into tokens at whitespace (this isn't exactly
   873:       // right, because the file names can have quoted spaces,
   874:       // but it will do for testing purposes)
   875:       StrtokParse tok(buf, " \n");
   876:       if (tok < 3) continue;
   877: 
   878:       int origLine = atoi(tok[1]);
   879:       char const *tok2 = tok[2];
   880:       sm_string origFname = sm_string(tok2+1, std::strlen(tok2)-2);  // remove quotes
   881:       pp->addHashLine(ppLine, origLine, origFname);
   882:     }
   883:     pp->doneAdding();
   884:   }
   885: 
   886:   // the 2nd line in the pp source should correspond to the
   887:   // first line in the orig src
   888:   // update: this doesn't work with all preprocessors, and I'm
   889:   // confident in the implementation now, so I'll turn this off
   890:   //SourceLoc lineTwo = mgr.encodeLineCol("srcloc.tmp", 2, 1);
   891:   //expect(lineTwo, "srcloc.cc", 1,1);
   892: 
   893:   // print decodes of first several lines (including those that
   894:   // are technically undefined because they occur on #line lines)
   895:   int ppLine;
   896:   for (ppLine = 1; ppLine < 10; ppLine++) {
   897:     SourceLoc loc = mgr.encodeLineCol("srcloc.tmp", ppLine, 1);
   898:     std::cout << "ppLine " << ppLine << ": " << toString(loc) << std::endl;
   899:   }
   900: 
   901:   // similar for last few lines
   902:   for (ppLine = pp->numLines - 10; ppLine <= pp->numLines; ppLine++) {
   903:     SourceLoc loc = mgr.encodeLineCol("srcloc.tmp", ppLine, 1);
   904:     std::cout << "ppLine " << ppLine << ": " << toString(loc) << std::endl;
   905:   }
   906: 
   907:   // see how the expander line behaves
   908:   if (!expanderLine) {
   909:     std::cout << "didn't find expander line!\n";
   910:     exit(2);
   911:   }
   912:   else {
   913:     SourceLoc loc = mgr.encodeLineCol("srcloc.tmp", expanderLine, 1);
   914:     std::cout << "expander column 1: " << toString(loc) << std::endl;
   915: 
   916:     // in the pp file, I can advance the expander horizontally a long ways;
   917:     // this should truncate to column 9
   918:     loc = advCol(loc, 20);
   919: 
   920:     char const *fname;
   921:     int offset;
   922:     mgr.decodeOffset(loc, fname, offset);
   923:     std::cout << "expander column 21: " << fname << ", offset " << offset << std::endl;
   924:     xassert(0==strcmp(fname, "srcloc.cc"));
   925: 
   926:     // map that to line/col, which should show the truncation
   927:     int line, col;
   928:     orig->charToLineCol(offset, line, col);
   929:     std::cout << "expander column 21: " << locString(fname, line, col) << std::endl;
   930:     if (col != 9) {
   931:       std::cout << "expected column 9!\n";
   932:       exit(2);
   933:     }
   934:   }
   935: }
   936: 
   937: 
   938: void entry(int argc, char ** /*argv*/)
   939: {
   940:   traceAddSys("progress");
   941:   traceProgress() << "begin" << std::endl;
   942: 
   943:   if (argc >= 2) {
   944:     // set maxStaticLocs low to test the warning
   945:     mgr.maxStaticLocs = 1;
   946:   }
   947: 
   948:   // test my source code
   949:   testFile("srcloc.cc");
   950:   testFile("srcloc.h");
   951: 
   952:   // do it again, so at least one won't be the just-added file;
   953:   // in fact do it many times so I can see results in a profiler
   954:   for (int i=0; i<1; i++) {
   955:     testFile("srcloc.cc");
   956:     testFile("srcloc.h");
   957:   }
   958: 
   959:   traceProgress() << "end" << std::endl;
   960: 
   961:   // protect against degeneracy by printing the length of
   962:   // the longest line
   963:   std::cout << "\n";
   964:   std::cout << "long line len: " << longestLen << std::endl;
   965: 
   966:   // test the statics
   967:   std::cout << "invalid: " << toString(SL_UNKNOWN) << std::endl;
   968:   std::cout << "here: " << toString(HERE_SOURCELOC) << std::endl;
   969: 
   970:   std::cout << "\n";
   971:   testHashMap();
   972: 
   973:   std::cout << "srcloc is ok\n";
   974: }
   975: 
   976: ARGS_MAIN
   977: 
   978: 
   979: #endif // TEST_SRCLOC
End cpp section to elk/sm_srcloc.cpp[1]
Start cpp section to elk/sm_strdict.cpp[1 /1 ]
     1: #line 11533 "./lpsrc/sm.pak"
     2: // strdict.cc            see license.txt for copyright and terms of use
     3: // code for strdict.h
     4: 
     5: #include "sm_strdict.h"
     6: #include <cstring>         // strcmp
     7: 
     8: 
     9: #define FOREACH_NODE(itervar) \
    10:   for(Node *itervar = top; itervar != NULL; itervar = itervar->next)
    11: 
    12: #define FOREACH_ITER(dict, itervar) \
    13:   for(Iter itervar = (dict).getIter(); !itervar.isDone(); itervar.next())
    14: 
    15: #define FOREACH_ITERC(dict, itervar) \
    16:   for(IterC itervar = (dict).getIterC(); !itervar.isDone(); itervar.next())
    17: 
    18: #define MUTABLE_SORT(obj) (const_cast<StringDict&>(obj)).sort()
    19: 
    20: 
    21: StringDict::StringDict()
    22:   : top(NULL)
    23: {}
    24: 
    25: 
    26: StringDict::StringDict(StringDict const &obj)
    27:   : top(NULL)
    28: {
    29:   *this = obj;
    30: }
    31: 
    32: 
    33: StringDict::~StringDict()
    34: {
    35:   SELFCHECK();
    36:   empty();
    37: }
    38: 
    39: 
    40: StringDict& StringDict::operator= (StringDict const &obj)
    41: {
    42:   if (this == &obj) {
    43:     return *this;
    44:   }
    45: 
    46:   empty();
    47: 
    48:   Node *end = top;
    49:   FOREACH_ITERC(obj, src) {
    50:     Node *newnode = new Node(src.key(), src.value());
    51:     if (!end) {
    52:       // first element of list
    53:       end = top = newnode;
    54:     }
    55:     else {
    56:       // adding to end of nonempty list
    57:       end = end->next = newnode;
    58:     }
    59:   }
    60: 
    61:   SELFCHECK();
    62:   return *this;
    63: }
    64: 
    65: 
    66: bool StringDict::operator== (StringDict const &obj) const
    67: {
    68:   // sort both lists
    69:   MUTABLE_SORT(*this);
    70:   MUTABLE_SORT(obj);
    71: 
    72:   IterC ths(*this), other(obj);
    73:   while (!ths.isDone() && !other.isDone()) {
    74:     if (0!=strcmp(ths.key(), other.key()) ||
    75:         0!=strcmp(ths.value(), other.value())) {
    76:       return false;
    77:     }
    78:     ths.next();
    79:     other.next();
    80:   }
    81: 
    82:   if (!ths.isDone() || !other.isDone()) {
    83:     // one finished first, so they can't be equal
    84:     return false;
    85:   }
    86: 
    87:   return true;
    88: }
    89: 
    90: 
    91: bool StringDict::isEmpty() const
    92: {
    93:   return top == NULL;
    94: }
    95: 
    96: 
    97: int StringDict::size() const
    98: {
    99:   int ret=0;
   100:   FOREACH_ITERC(*this, entry) {
   101:     ret++;
   102:   }
   103:   return ret;
   104: }
   105: 
   106: 
   107: bool StringDict::query(char const *key, sm_string &value) const
   108: {
   109:   FOREACH_ITERC(*this, entry) {
   110:     if (0==strcmp(entry.key(), key)) {
   111:       value = entry.value();
   112:       return true;
   113:     }
   114:   }
   115: 
   116:   return false;
   117: }
   118: 
   119: 
   120: sm_string StringDict::queryf(char const *key) const
   121: {
   122:   sm_string ret;
   123:   bool ok = query(key, ret);
   124:   xassert(ok);
   125:   return ret;
   126: }
   127: 
   128: 
   129: bool StringDict::isMapped(char const *key) const
   130: {
   131:   sm_string dummy;
   132:   return query(key, dummy);
   133: }
   134: 
   135: 
   136: void StringDict::add(char const *key, char const *value)
   137: {
   138:   xassert(!isMapped(key));
   139: 
   140:   // just prepend; we'll sort later (when an iterator is retrieved)
   141:   top = new Node(key, value, top);
   142: 
   143:   SELFCHECK();
   144: }
   145: 
   146: 
   147: void StringDict::modify(char const *key, char const *newValue)
   148: {
   149:   Iter entry = find(key);
   150:   xassert(!entry.isDone());
   151: 
   152:   entry.value() = newValue;
   153: 
   154:   SELFCHECK();
   155: }
   156: 
   157: 
   158: StringDict::Iter StringDict::find(char const *key)
   159: {
   160:   FOREACH_ITER(*this, entry) {
   161:     if (0==strcmp(entry.key(), key)) {
   162:       return entry;
   163:     }
   164:   }
   165:   return Iter(NULL);
   166: }
   167: 
   168: 
   169: void StringDict::remove(char const *key)
   170: {
   171:   xassert(top);
   172: 
   173:   // check for removal of top element
   174:   if (0==strcmp(top->key, key)) {
   175:     Node *temp = top;
   176:     top = top->next;
   177:     delete temp;
   178:   }
   179: 
   180:   // find node to remove in tail of list
   181:   else {
   182:     Node *p = top;
   183:     while (p->next && 0!=strcmp(p->next->key, key)) {
   184:       p = p->next;
   185:     }
   186: 
   187:     if (!p->next) {
   188:       // reached the end of the list without finding the key
   189:       xfailure("failed to find key");
   190:     }
   191: 
   192:     // remove p->next from the list
   193:     Node *temp = p->next;
   194:     p->next = p->next->next;
   195:     delete temp;
   196:   }
   197: 
   198:   SELFCHECK();
   199: }
   200: 
   201: 
   202: void StringDict::empty()
   203: {
   204:   while (top) {
   205:     Node *temp = top;
   206:     top = top->next;
   207:     delete temp;
   208:   }
   209: 
   210:   SELFCHECK();
   211: }
   212: 
   213: 
   214: StringDict::Iter StringDict::getIter()
   215: {
   216:   sort();        // must return items in sorted order
   217:   return Iter(top);
   218: }
   219: 
   220: 
   221: StringDict::IterC StringDict::getIterC() const
   222: {
   223:   //sort();
   224:   const_cast<StringDict*>(this)->sort();    // mutable
   225:   return IterC(top);
   226: }
   227: 
   228: 
   229: // use simple insertion sort for now
   230: /*mutable*/ void StringDict::sort()
   231: {
   232:   if (!top) {
   233:     return;
   234:   }
   235: 
   236:   // invariant: sequence of nodes from 'top' to 'walker', inclusive,
   237:   //            is always sorted
   238:   Node *walker = top;
   239:   while (walker->next != NULL) {
   240:     // see if walker->next is out of order
   241:     if (0 <= strcmp(walker->key, walker->next->key)) {
   242:       // it's in order
   243:       walker = walker->next;
   244:       continue;
   245:     }
   246: 
   247:     // remove walker->next from where it is (note that this has
   248:     // the effect of advancing walker, so below here we won't
   249:     // have another movement of walker)
   250:     Node *mover = walker->next;
   251:     walker->next = walker->next->next;
   252:     mover->next = NULL;       // (redundant because of (**) lines)
   253: 
   254:     // insert at head?
   255:     if (0 < strcmp(mover->key, top->key)) {
   256:       mover->next = top;            // (**)
   257:       top = mover;
   258:       continue;
   259:     }
   260: 
   261:     // must find correct place to insert mover (will find the place
   262:     // where we can insert mover just before searcher->next)
   263:     Node *searcher = top;
   264:     while (0 < strcmp(searcher->next->key, mover->key)) {
   265:       searcher = searcher->next;
   266:       xassert(searcher != walker);
   267:         // otherwise how could mover have been out of order to begin with?
   268:     }
   269: 
   270:     // insert mover before searcher->next
   271:     mover->next = searcher->next;   // (**)
   272:     searcher->next = mover;
   273:   }
   274: 
   275:   SELFCHECK();
   276: 
   277:   #ifndef NDEBUG
   278:     verifySorted();
   279:   #endif
   280: }
   281: 
   282: 
   283: void StringDict::verifySorted() const
   284: {
   285:   if (!top) {
   286:     return;
   287:   }
   288: 
   289:   Node *p = top;
   290:   while (p->next) {
   291:     xassert(0 <= strcmp(p->key, p->next->key));
   292:     p = p->next;
   293:   }
   294: }
   295: 
   296: 
   297: // verify that the list is well structured
   298: void StringDict::selfCheck() const
   299: {
   300:   Node *fast = top, *slow = top;
   301:   while (fast && fast->next) {
   302:     fast = fast->next->next;
   303:     slow = slow->next;
   304: 
   305:     xassert(fast != slow);
   306:       // if these become equal, the list is circular
   307:   }
   308: }
   309: 
   310: 
   311: void StringDict::insertOstream(std::ostream &os) const
   312: {
   313:   FOREACH_ITERC(*this, entry) {
   314:     os << entry.key() << " = " << entry.value() << std::endl;
   315:   }
   316: }
   317: 
   318: 
   319: sm_string StringDict::toString() const
   320: {
   321:   sm_stringBuilder sb;
   322:   sb << "{";
   323:   int count=0;
   324:   FOREACH_ITERC(*this, entry) {
   325:     if (count++ > 0) {
   326:       sb << ",";
   327:     }
   328:     sb << " " << entry.key() << "=\"" << entry.value() << "\"";
   329:   }
   330:   sb << " }";
   331:   return sb;
   332: }
   333: 
   334: 
   335: // -------------------- test code ------------------------
   336: #ifdef TEST_STRDICT
   337: 
   338: #include "sm_test.h"
   339: #include <stdlib.h>    // rand
   340: 
   341: #define myrandom(n) (rand()%(n))
   342: 
   343: char randChar()
   344: {
   345:   return (char)(myrandom(127-32+1)+32);
   346: }
   347: 
   348: sm_string randString(int len)
   349: {
   350:   sm_stringBuilder str;
   351:   loopj(len) {
   352:     str << randChar();
   353:   }
   354:   return str;
   355: }
   356: 
   357: sm_string randStringRandLen(int maxlen)
   358: {
   359:   return randString(myrandom(maxlen)+1);
   360: }
   361: 
   362: sm_string randKey(StringDict const &dict)
   363: {
   364:   int size = dict.size();
   365:   xassert(size > 0);
   366: 
   367:   int nth = myrandom(size);
   368:   StringDict::IterC entry(dict);
   369:   for (; nth > 0; entry.next(), nth--)
   370:     {}
   371: 
   372:   return entry.key();
   373: }
   374: 
   375: 
   376: void entry()
   377: {
   378:   StringDict dict;
   379:   int size=0, collisions=0;
   380: 
   381:   int iters = 1000;
   382:   loopi(iters) {
   383:     switch (myrandom(6)) {
   384:       case 0: {
   385:         // insert a random element
   386:         sm_string key = randStringRandLen(10);
   387:         sm_string value = randStringRandLen(30);
   388: 
   389:         if (!dict.isMapped(key)) {
   390:           dict.add(key, value);
   391:           size++;
   392:         }
   393:         else {
   394:           collisions++;
   395:         }
   396:         break;
   397:       }
   398: 
   399:       case 1: {
   400:         // remove a random element
   401:         if (dict.isEmpty()) {
   402:           break;
   403:         }
   404: 
   405:         sm_string key = randKey(dict);
   406:         dict.remove(key);
   407:         size--;
   408:         break;
   409:       }
   410: 
   411:       case 2: {
   412:         // check a random element that should not be there
   413:         sm_string key = randStringRandLen(10);
   414:         if (dict.isMapped(key)) {
   415:           collisions++;
   416:         }
   417:         break;
   418:       }
   419: 
   420:       case 3: {
   421:         // verify that computed length is right
   422:         xassert(size == dict.size());
   423:         break;
   424:       }
   425: 
   426:       case 4: {
   427:         // test == and =
   428:         StringDict dict2(dict);
   429:         xassert(dict2 == dict);
   430:         xassert(dict2.size() == dict.size());
   431: 
   432:         // modify it, then verify inequality
   433:         if (!dict2.isEmpty()) {
   434:           sm_string key = randKey(dict2);
   435:           sm_string value = dict2.queryf(key);
   436: 
   437:           if (myrandom(2) == 0) {
   438:             dict2.remove(key);
   439:           }
   440:           else {
   441:             dict2.modify(key, value & "x");
   442:           }
   443:           xassert(dict2 != dict);
   444:         }
   445: 
   446:         break;
   447:       }
   448: 
   449:       case 5: {
   450:         // random modification
   451:         if (!dict.isEmpty()) {
   452:           sm_string key = randKey(dict);
   453:           dict.modify(key, randStringRandLen(30));
   454:         }
   455:         break;
   456:       }
   457: 
   458:       default:
   459:         xfailure("huh?");
   460:         break;
   461:     }
   462:   }
   463: 
   464:   std::cout << "final size: " << size
   465:        << "\ncollisions: " << collisions
   466:        << "\n";
   467: 
   468:   std::cout << "all tests passed\n";
   469: }
   470: 
   471: USUAL_MAIN
   472: 
   473: #endif // TEST_STRDICT
End cpp section to elk/sm_strdict.cpp[1]
Start cpp section to elk/sm_strhash.cpp[1 /1 ]
     1: #line 12007 "./lpsrc/sm.pak"
     2: // strhash.cc            see license.txt for copyright and terms of use
     3: // code for strhash.h
     4: 
     5: #include "sm_strhash.h"
     6: #include "sm_xassert.h"
     7: 
     8: #include <cstring>      // strcmp
     9: 
    10: // notes on sm_string hash functions ****************
    11: 
    12: // We need to test the various hash functions versus each other for
    13: // randomness.  Scott suggests simply hashing the same data and
    14: // modding it down as a hashtable would and seeing which gives more
    15: // collisions.
    16: 
    17: // Note that both hash functions could be improved if they were aware
    18: // of how many of their low order bits were really going to be
    19: // preserved and then taking the high order bits that would otherwise
    20: // be discarded and xoring or adding them to the low order bits.
    21: 
    22: // Hash function 2:
    23: 
    24: // 1) Does not work on architectures other than 32 bit.
    25: 
    26: // 2) Will not work well or at all if the sm_strings do not start on
    27: // 32-bit aligned boundaries.
    28: 
    29: // 3) Could be made to work faster if the sm_strings start AND end on
    30: // 32-bit aligned boundaries and are padded out with NUL-s.  Actually,
    31: // if we do this trick, then the code becomes portable with no
    32: // #ifdef-s !  All you do is cast the array to an array of ints and
    33: // then test for termination by masking off all but the last 8 bits.
    34: // Everything else is just operations on ints.  You might want to pick
    35: // 64-bit primes, but they will work in 32-bit mode as long as the
    36: // compiler just truncates their high bits off.
    37: 
    38: // ****************
    39: 
    40: 
    41: StringHash::StringHash(GetKeyFn gk)
    42:   : HashTable((HashTable::GetKeyFn)gk,
    43:               (HashTable::HashFn)coreHash,
    44:               (HashTable::EqualKeyFn)keyCompare)
    45: {}
    46: 
    47: StringHash::~StringHash()
    48: {}
    49: 
    50: 
    51: STATICDEF unsigned StringHash::coreHash(char const *key)
    52: {
    53:   // dsw: not sure if this is the best place for it, but an assertion
    54:   // failure is better than a segfault
    55:   //
    56:   // sm: I don't agree; segfaults arising from NULL derefs are
    57:   // quite fristd::endly (deref'ing random address is another story).
    58:   // Anyway, this is fine, but I'd like it to go away in NDEBUG
    59:   // mode so I'm changing it to use 'xassertdb'.
    60:   xassertdb(key);
    61: 
    62:   // some more references:
    63: 
    64:   // http://www.cs.yorku.ca/~oz/hash.html
    65:   //
    66:   // Describes three well-known hashes: djb2, sdbm, and K&R ed. 1.
    67: 
    68:   // http://burtleburtle.net/bob/hash/doobs.html
    69:   //
    70:   // Describes a particular hash function (called simply "My Hash")
    71:   // and provides justifications for preferring it to several others,
    72:   // including MD4.
    73: 
    74:   // http://www.isthe.com/chongo/tech/comp/fnv/
    75:   //
    76:   // Glen Fowler, Landon Curt Noll, and Phong Vo's hash function.
    77: 
    78:   // http://mail.python.org/pipermail/python-dev/2004-April/044235.html
    79:   //
    80:   // Start of a discussion thread about changing the sm_string hash
    81:   // in Python.
    82: 
    83: 
    84: 
    85: 
    86: 
    87: 
    88:   #if 0
    89:   // I pulled this out of my ass.. it's supposed to mimic
    90:   // a linear congruential random number generator
    91:   unsigned val = 0x42e9d115;    // arbitrary
    92:   while (*key != 0) {
    93:     val *= (unsigned)(*key);
    94:     val += 1;
    95:     key++;
    96:   }
    97:   return val;
    98:   #endif // 0
    99: 
   100:   // pick a default STRHASH_ALG
   101:   #ifndef STRHASH_ALG
   102:     #define STRHASH_ALG 1
   103:   #endif // STRHASH_ALG
   104: 
   105: 
   106:   #if STRHASH_ALG == 1
   107:   #ifdef SAY_STRHASH_ALG
   108:     #warning hash function 1: Nelson
   109:   #endif // SAY_STRHASH_ALG
   110:   // this one is supposed to be better
   111:   /* An excellent sm_string hashing function.
   112:      Adapted from glib's g_str_hash().
   113:      Investigation by Karl Nelson <kenelson@ece.ucdavis.edu>.
   114:      Do a web search for "g_str_hash X31_HASH" if you want to know more. */
   115:   /* update: this is the same function as that described in Kernighan and Pike,
   116:      "The Practice of Programming", section 2.9 */
   117:   unsigned h = 0;
   118:   for (; *key != '\0'; key += 1) {
   119:     // original X31_HASH
   120:     h = ( h << 5 ) - h + *key;       // h*31 + *key
   121: 
   122:     // dsw: this one is better because it does the multiply last;
   123:     // otherwise the last byte has no hope of modifying the high order
   124:     // bits
   125:     //
   126:     // sm: I'm not convinced it's better.  For short sm_strings, say less
   127:     // than 6 characters, the arithmetic won't overflow a 32-bit
   128:     // register.  In that case, by multiplying last, the hash value is
   129:     // always a multiple of 31 and hence will suffer many more
   130:     // collisions.  I would like more justification in the form of
   131:     // experimental measurements before making a change.
   132:     //h += *key;
   133:     //h = ( h << 5 ) - h;         // h *= 31
   134:   }
   135:   return h;
   136: 
   137: 
   138:   #elif STRHASH_ALG == 2
   139:   #ifdef SAY_STRHASH_ALG
   140:     #warning hash function 2: word-rotate/final-mix
   141:   #endif // SAY_STRHASH_ALG
   142: 
   143:   // FIX:
   144:   #warning word-rotate/final-mix hash function only works on 32-bit architectures
   145:   #warning word-rotate/final-mix hash function still needs to be tested for randomness vs nelson
   146: 
   147:   // Word-Rotate / Final-Mix hash function by Daniel Wilkerson; A
   148:   // slighly faster and likely more random hash function; Invented in
   149:   // collaboration with Simon Goldsmith.
   150:   //
   151:   // Supposedly gcc will sometimes recognize this and generate a
   152:   // single rotate instruction 'ROR'.  Thanks to Matt Harren for this.
   153:   // http://groups.google.com/groups?q=rorl+x86&start=10&hl=en&lr=&ie=UTF-8&oe=UTF-8&
   154:   // selm=359954C9.3B354F0%40cartsys.com&rnum=11
   155:   // http://www.privacy.nb.ca/cryptography/archives/coderpunks/new/1998-10/0096.html
   156:   #define ROTATE(n, b) (n >> b) | (n << (32 - b))
   157: 
   158:   // Note that UINT_MAX        = 4294967295U;
   159:   // source of primes: http://www.utm.edu/research/primes/lists/small/small.html
   160:   static unsigned const primeA = 1500450271U;
   161:   static unsigned const primeB = 2860486313U;
   162:   // source of primes: http://www.utm.edu/research/primes/lists/2small/0bit.html
   163:   static unsigned const primeC = (1U<<31) - 99U;
   164:   static unsigned const primeD = (1U<<30) - 35U;
   165: 
   166:   static int count = 0;
   167:   ++count;
   168: 
   169:   unsigned h = primeA;
   170: 
   171:   // Stride a word at a time.  Note that this works best (or works at
   172:   // all) if the sm_string is 32-bit aligned.  This initial 'if' block is
   173:   // to prevent an extra unneeded rotate.
   174:   //
   175:   // FIX: this would be even faster if all sm_strings were NUL-padded in
   176:   // length to a multiple of 4; we could then omit all but the last
   177:   // 'if' and the ragged end after the loop (it doesn't matter if you
   178:   // tile a few extra NULs into your value).
   179:   if (!key[0]) goto end0;
   180:   if (!key[1]) goto end1;
   181:   if (!key[2]) goto end2;
   182:   if (!key[3]) goto end3;
   183:   // No rotate here.
   184:   h += *( (unsigned *) key );
   185:   key += 4;
   186:   while (1) {
   187:     // invariant: when we get here, we are ready to rotate
   188:     if (!key[0]) {h = ROTATE(h, 5); goto end0;}
   189:     if (!key[1]) {h = ROTATE(h, 5); goto end1;}
   190:     if (!key[2]) {h = ROTATE(h, 5); goto end2;}
   191:     if (!key[3]) {h = ROTATE(h, 5); goto end3;}
   192:     h = ROTATE(h, 5);
   193:     // FIX: if the start of the sm_string is not 4-byte aligned then this
   194:     // will be slower on x86 and I think even illegal on MIPS and
   195:     // perhaps others.  To be portable we should ensure this.
   196:     h += *( (unsigned *) key ); // on my machine plus is faster than xor
   197:     key += 4;
   198:   }
   199:   xfailure("shouldn't get here");
   200: 
   201:   // deal with the ragged end
   202:   // invariant: when we get here we are ready to add
   203: end3:
   204:   h += *key; h = ROTATE(h, 5); key += 1;
   205: end2:
   206:   h += *key; h = ROTATE(h, 5); key += 1;
   207: end1:
   208:   h += *key;                    // No rotate nor increment here.
   209:   #ifndef NDEBUG
   210:     key += 1;                   // this is only needed for the assertion below
   211:   #endif
   212: end0:
   213:   xassertdb(*key=='\0');
   214: 
   215:   // I will say for now that the property of hash functions that we
   216:   // want is that a change in any input bit has a 50% chance of
   217:   // inverting any output bit.
   218:   //
   219:   // At this point, compare this hash function to the Nelson hash
   220:   // function above.  In Nelson, everytime data is added, the
   221:   // accumulator, 'h', is "stirred" with a multiplication.  Since data
   222:   // is being added at the low byte and since multiplication
   223:   // propagates dependencies towards the high bytes and since after
   224:   // ever add there is at least one multiply, every byte has a chance
   225:   // to affect the value of every bit above the first byte.
   226:   //
   227:   // However, in this hash function we have saved time by simply
   228:   // "tiling" the data across the accumulator and at this point it
   229:   // hasn't been "stirred" at all.  Since most hashvalues are used by
   230:   // modding off some high bits, those bits have never had a chance to
   231:   // affect the final value, so some stirring is needed.  How much
   232:   // "stirring" do we need?
   233:   //
   234:   // Consider the 32-bit word in two halves, H and L.
   235:   //
   236:   // 1) With a single multiply, any bit of L "has a chance" to affect
   237:   // any bit in H.  The reverse is not true.
   238:   h *= primeB;
   239: 
   240:   // 2) We therefore swap H and L and multiply again.  Now, any bit in
   241:   // H has had a chance to affect any bit in L.  We are not done
   242:   // though, since the high order bits in H have not had a chance to
   243:   // affect the low order bits of H (yes H).  Please note however,
   244:   // that since L affected H and H affected L, the hight order bits of
   245:   // L *have* had a chance to affect the low order bits of L.
   246:   h = ROTATE(h, 16);
   247:   h *= primeC;
   248: 
   249:   // 3) Therefore we swap H and L and multiply once again.  Now the
   250:   // high order bits of H have had a chance to affect L (in 2) which
   251:   // now can affect H again.  Any bit now has "had a chance" to affect
   252:   // any other bit.
   253:   h = ROTATE(h, 16);
   254:   h *= primeD;
   255: 
   256:   return h;
   257: 
   258:   #undef ROTATE
   259: 
   260: 
   261:   #else
   262:     #error You must pick a hash function
   263:   #endif // STRHASH_ALG multi-switch
   264: }
   265: 
   266: 
   267: STATICDEF bool StringHash::keyCompare(char const *key1, char const *key2)
   268: {
   269:   return 0==strcmp(key1, key2);
   270: }
   271: 
   272: 
   273: // ---------------------- test code --------------------
   274: #ifdef TEST_STRHASH
   275: 
   276: #include <iostream>    // std::cout
   277: #include <stdlib.h>      // rand
   278: #include <iostream>      // std::istream
   279: #include <fstream>       // filebuf
   280: #include "sm_trace.h"
   281: #include "sm_crc.h"
   282: #include "sm_nonport.h"
   283: #include "sm_array.h"
   284: #include "sm_str.h"
   285: 
   286: // pair a GrowArray with its size
   287: struct StringArray {
   288:   int tableSize;
   289:   GrowArray<char*> table;
   290:   bool appendable;
   291: 
   292:   StringArray(int tableSize0)
   293:     : tableSize(tableSize0)
   294:     , table(tableSize)
   295:     , appendable(tableSize == 0)
   296:   {}
   297:   void append(char *str) {
   298:     xassert(appendable);
   299:     table.ensureIndexDoubler(tableSize);
   300:     table[tableSize] = str;
   301:     ++tableSize;
   302:   }
   303: };
   304: 
   305: // data to hash
   306: StringArray *dataArray = NULL;
   307: 
   308: 
   309: char const *id(void *p)
   310: {
   311:   return (char const*)p;
   312: }
   313: 
   314: char *randomString()
   315: {
   316:   char *ret = new char[11];
   317:   loopi(10) {
   318:     ret[i] = (rand()%26)+'a';
   319:   }
   320:   ret[10]=0;
   321:   return ret;
   322: }
   323: 
   324: // fill a table with random sm_strings
   325: void makeRandomData(int numRandStrs) {
   326:   dataArray = new StringArray(numRandStrs);
   327:   {loopi(dataArray->tableSize) {
   328:     dataArray->table[i] = randomString();
   329:   }}
   330: }
   331: 
   332: 
   333: // file the data array with whitespace-delimited sm_strings from a file
   334: void readDataFromFile(char *inFileName) {
   335:   dataArray = new StringArray(0);
   336:   char *delim = " \t\n\r\v\f";
   337:   std::filebuf fb;
   338:   fb.open (inFileName, ios::in);
   339:   std::istream in(&fb);
   340:   while(true) {
   341:     sm_stringBuilder s;
   342:     s.readdelim(in, delim);
   343: //      std::cout << ":" << s->pcharc() << ":" << std::endl;
   344:     if (in.eof()) break;
   345: //      // don't insert 0 length sm_strings
   346: //      if (s->length() == 0) continue;
   347:     dataArray->append(strdup(s.pcharc()));
   348:   }
   349: }
   350: 
   351: void writeData(std::ostream &out) {
   352:   std::cout << "write data" << std::endl;
   353:   for(int i=0; i<dataArray->tableSize; ++i) {
   354:     out << dataArray->table[i] << std::endl;
   355:   }
   356: }
   357: 
   358: // dsw: what is the point of this?
   359: // dealloc the test sm_strings
   360: //  void deleteData() {
   361: //    {loopi(dataArray->tableSize) {
   362: //      delete[] dataArray->table[i];
   363: //    }}
   364: //  //    delete[] dataArray->table;
   365: //  }
   366: 
   367: void correctnessTest() {
   368:   traceProgress() << "start of strhash correctness testing\n";
   369: 
   370:   // insert them all into a hash table
   371:   StringHash hash(id);
   372:   {loopi(dataArray->tableSize) {
   373:     hash.add(dataArray->table[i], dataArray->table[i]);
   374:     hash.selfCheck();
   375:   }}
   376:   hash.selfCheck();
   377:   xassert(hash.getNumEntries() == dataArray->tableSize);
   378: 
   379:   // verify that they are all mapped properly
   380:   {loopi(dataArray->tableSize) {
   381:     xassert(hash.get(dataArray->table[i]) == dataArray->table[i]);
   382:   }}
   383:   hash.selfCheck();
   384: 
   385:   // remove every other one
   386:   {loopi(dataArray->tableSize) {
   387:     if (i%2 == 0) {
   388:       hash.remove(dataArray->table[i]);
   389:       hash.selfCheck();
   390:     }
   391:   }}
   392:   hash.selfCheck();
   393:   xassert(hash.getNumEntries() == dataArray->tableSize / 2);
   394: 
   395:   // verify it
   396:   {loopi(dataArray->tableSize) {
   397:     if (i%2 == 0) {
   398:       xassert(hash.get(dataArray->table[i]) == NULL);
   399:     }
   400:     else {
   401:       xassert(hash.get(dataArray->table[i]) == dataArray->table[i]);
   402:     }
   403:   }}
   404:   hash.selfCheck();
   405: 
   406:   // remove the rest
   407:   {loopi(dataArray->tableSize) {
   408:     if (i%2 == 1) {
   409:       hash.remove(dataArray->table[i]);
   410:       hash.selfCheck();
   411:     }
   412:   }}
   413:   hash.selfCheck();
   414:   xassert(hash.getNumEntries() == 0);
   415: 
   416:   traceProgress() << "end of strhash correctness testing\n";
   417: }
   418: 
   419: void performanceTest(int numPerfRuns) {
   420:   // test performance of the hash function
   421:   traceProgress() << "start of strhash performance testing\n";
   422: 
   423:   long startTime = getMilliseconds();
   424:   loopj(numPerfRuns) {
   425:     loopi(dataArray->tableSize) {
   426:       StringHash::coreHash(dataArray->table[i]);
   427:       //crc32((unsigned char*)dataArray->table[i], std::strlen(dataArray->table[i]));
   428:       //crc32((unsigned char*)dataArray->table[i], 10);
   429:     }
   430:   }
   431:   long stopTime = getMilliseconds();
   432:   long duration = stopTime - startTime;
   433:   std::cout << "milliseconds to hash: " << duration << std::endl;
   434: 
   435:   traceProgress() << "end of strhash performance testing\n";
   436: }
   437: 
   438: // command-line state
   439: int numRandStrs = 0;
   440: char *inFileName = NULL;
   441: bool dump = false;
   442: bool testCor = true;
   443: bool testPerf = true;
   444: int numPerfRuns = 10000;
   445: 
   446: void usage() {
   447:   std::cout << "Test the sm_string hashing module strhash.cc\n"
   448:        << "  --help / -h     : print this message\n"
   449:        << "  --[no-]testCor  : run the correctness tests\n"
   450:        << "                    will fail if data has duplicate sm_strings (?!)\n"
   451:        << "  --[no-]testPerf : run the performance tests\n"
   452:        << "  --numPerfRuns N : loop over data N times during performance run\n"
   453:        << "  --file FILE     : use the whitespace-delimited sm_string contents of FILE\n"
   454:        << "  --random N      : use N internally generated random sm_strings of length 10;\n"
   455:        << "                    N should be even\n"
   456:        << "  --dump          : dump out the data after generating/reading it\n"
   457:        << "The default is '--random 300 --testCor --testPerf --numPerfRuns 10000'."
   458:        << std::endl;
   459: }
   460: 
   461: void initFromFlags(int &argc, char**&argv) {
   462:   --argc; ++argv;
   463:   for(;
   464:       *argv;
   465:       --argc, ++argv) {
   466:     if (strcmp(*argv, "--help")==0 || strcmp(*argv, "-h")==0) {
   467:       usage();
   468:       exit(0);
   469:     } else if (strcmp(*argv, "--testCor")==0) {
   470:       testCor = true;
   471:     } else if (strcmp(*argv, "--no-testCor")==0) {
   472:       testCor = false;
   473:     } else if (strcmp(*argv, "--testPerf")==0) {
   474:       testPerf = true;
   475:     } else if (strcmp(*argv, "--no-testPerf")==0) {
   476:       testPerf = false;
   477:     } else if (strcmp(*argv, "--random")==0) {
   478:       if (inFileName) {
   479:         std::cout << "do not use --random and --file together" << std::endl;
   480:         usage();
   481:         exit(1);
   482:       }
   483:       --argc; ++argv;
   484:       if (!*argv) {
   485:         std::cout << "supply an argument to --random" << std::endl;
   486:         usage();
   487:         exit(1);
   488:       }
   489:       numRandStrs = atoi(*argv);
   490:       if (!(numRandStrs > 0)) {
   491:         std::cout << "argument to --random must be > 0" << std::endl;
   492:         usage();
   493:         exit(1);
   494:       }
   495:     } else if (strcmp(*argv, "--file")==0) {
   496:       if (numRandStrs) {
   497:         std::cout << "do not use --random and --file together" << std::endl;
   498:         usage();
   499:         exit(1);
   500:       }
   501:       --argc; ++argv;
   502:       if (!*argv) {
   503:         std::cout << "supply an argument to --file" << std::endl;
   504:         usage();
   505:         exit(1);
   506:       }
   507:       inFileName = strdup(*argv);
   508:       xassert(inFileName);
   509:     } else if (strcmp(*argv, "--numPerfRuns")==0) {
   510:       --argc; ++argv;
   511:       if (!*argv) {
   512:         std::cout << "supply an argument to --numPerfRuns" << std::endl;
   513:         usage();
   514:         exit(1);
   515:       }
   516:       numPerfRuns = atoi(*argv);
   517:       if (!(numPerfRuns > 0)) {
   518:         std::cout << "argument to --numPerfRuns must be > 0" << std::endl;
   519:         usage();
   520:         exit(1);
   521:       }
   522:     } else if (strcmp(*argv, "--dump")==0) {
   523:       dump = true;
   524:     } else {
   525:       std::cout << "unrecognized flag " << *argv << std::endl;
   526:       usage();
   527:       exit(1);
   528:     }
   529:   }
   530: }
   531: 
   532: int main(int argc, char **argv)
   533: {
   534:   traceAddSys("progress");
   535: 
   536:   #if STRHASH_ALG == 1
   537:     std::cout << "hash function 1: Nelson" << std::endl;
   538:   #elif STRHASH_ALG == 2
   539:     std::cout << "hash function 2: word-rotate/final-mix" << std::endl;
   540:   #else
   541:     #error You must pick a hash function
   542:   #endif // STRHASH_ALG multi-switch
   543: 
   544:   // read command line flags
   545:   initFromFlags(argc, argv);
   546: 
   547:   // read data
   548:   if ((!inFileName) && (!numRandStrs)) {
   549:     numRandStrs = 300;          // default
   550:   }
   551:   if (numRandStrs % 2 != 0) {
   552:     std::cout << "use an even-number argument for --random" << std::endl;
   553:     usage();
   554:     exit(1);
   555:   }
   556:   if (numRandStrs) {
   557:     makeRandomData(numRandStrs);
   558:   } else if (inFileName) {
   559:     if (testCor) {
   560:       std::cout << "Warning: The correctness test fails if sm_strings are duplicated "
   561:         "and you are reading data from a file." << std::endl;
   562:     }
   563:     readDataFromFile(inFileName);
   564:   } else {
   565:     xfailure("goink?");
   566:   }
   567: 
   568:   // dump data
   569:   if (dump) {
   570:     writeData(std::cout);
   571:   }
   572: 
   573:   // test
   574:   if (testCor) {
   575:     correctnessTest();
   576:   }
   577:   if (testPerf) {
   578:     performanceTest(numPerfRuns);
   579:   }
   580: 
   581:   // delete data
   582: //    deleteData();
   583: 
   584:   std::cout << "strhash tests finished\n";
   585:   return 0;
   586: }
   587: 
   588: #endif // TEST_STRHASH
End cpp section to elk/sm_strhash.cpp[1]
Start cpp section to elk/sm_stringset.cpp[1 /1 ]
     1: #line 12596 "./lpsrc/sm.pak"
     2: // sm_stringset.cc            see license.txt for copyright and terms of use
     3: // code for sm_stringset.h
     4: 
     5: #include "sm_stringset.h"
     6: 
     7: StringSet::~StringSet()
     8: {}
     9: 
    10: void StringSet::add(char const *elt)
    11: {
    12:   if (!contains(elt)) {
    13:     elts.add(elt, NULL);
    14:   }
    15: }
    16: 
    17: void StringSet::remove(char const *elt)
    18: {
    19:   if (contains(elt)) {
    20:     elts.remove(elt);
    21:   }
    22: }
    23: 
End cpp section to elk/sm_stringset.cpp[1]
Start cpp section to elk/sm_strutil.cpp[1 /1 ]
     1: #line 12620 "./lpsrc/sm.pak"
     2: // strutil.cc            see license.txt for copyright and terms of use
     3: // code for strutil.h
     4: 
     5: #include "sm_strutil.h"
     6: #include "sm_exc.h"
     7: #include "sm_autofile.h"
     8: 
     9: #include <ctype.h>       // isspace
    10: #include <cstring>      // strstr, std::memcmp
    11: #include <stdio.h>       // sprintf
    12: #include <stdlib.h>      // strtoul
    13: #include <time.h>        // time, asctime, localtime
    14: 
    15: 
    16: // replace all instances of oldstr in src with newstr, return result
    17: sm_string replace(char const *src, char const *oldstr, char const *newstr)
    18: {
    19:   sm_stringBuilder ret("");
    20: 
    21:   while (*src) {
    22:     char const *next = strstr(src, oldstr);
    23:     if (!next) {
    24:       ret &= sm_string(src);
    25:       break;
    26:     }
    27: 
    28:     // make a subsm_string out of the characters between src and next
    29:     sm_string upto(src, next-src);
    30: 
    31:     // add it to running sm_string
    32:     ret &= upto;
    33: 
    34:     // add the replace-with sm_string
    35:     ret &= sm_string(newstr);
    36: 
    37:     // move src to beyond replaced subsm_string
    38:     src += (next-src) + std::strlen(oldstr);
    39:   }
    40: 
    41:   return ret;
    42: }
    43: 
    44: 
    45: sm_string expandRanges(char const *chars)
    46: {
    47:   sm_stringBuilder ret;
    48: 
    49:   while (*chars) {
    50:     if (chars[1] == '-' && chars[2] != 0) {
    51:       // range specification
    52:       if (chars[0] > chars[2]) {
    53:         xformat("range specification with wrong collation order");
    54:       }
    55: 
    56:       for (char c = chars[0]; c <= chars[2]; c++) {
    57:         ret << c;
    58:       }
    59:       chars += 3;
    60:     }
    61:     else {
    62:       // simple character specification
    63:       ret << chars[0];
    64:       chars++;
    65:     }
    66:   }
    67: 
    68:   return ret;
    69: }
    70: 
    71: 
    72: sm_string translate(char const *src, char const *srcchars, char const *destchars)
    73: {
    74:   // first, expand range notation in the specification sequences
    75:   sm_string srcSpec = expandRanges(srcchars);
    76:   sm_string destSpec = expandRanges(destchars);
    77: 
    78:   // build a translation map
    79:   char map[256];
    80:   int i;
    81:   for (i=0; i<256; i++) {
    82:     map[i] = i;
    83:   }
    84: 
    85:   // excess characters from either sm_string are ignored ("SysV" behavior)
    86:   for (i=0; i < srcSpec.length() && i < destSpec.length(); i++) {
    87:     map[(unsigned char)( srcSpec[i] )] = destSpec[i];
    88:   }
    89: 
    90:   // run through 'src', applying 'map'
    91:   sm_string ret(std::strlen(src));
    92:   char *dest = ret.pchar();
    93:   while (*src) {
    94:     *dest = map[(unsigned char)*src];
    95:     dest++;
    96:     src++;
    97:   }
    98:   *dest = 0;    // final nul terminator
    99: 
   100:   return ret;
   101: }
   102: 
   103: 
   104: // why is this necessary?
   105: sm_string sm_stringToupper(char const *src)
   106:   { return translate(src, "a-z", "A-Z"); }
   107: 
   108: 
   109: sm_string trimWhitespace(char const *str)
   110: {
   111:   // trim leading whitespace
   112:   while (isspace(*str)) {
   113:     str++;
   114:   }
   115: 
   116:   // trim trailing whitespace
   117:   char const *end = str + std::strlen(str);
   118:   while (end > str &&
   119:          isspace(end[-1])) {
   120:     end--;
   121:   }
   122: 
   123:   // return it
   124:   return sm_string(str, end-str);
   125: }
   126: 
   127: 
   128: // table of escape codes
   129: static struct Escape {
   130:   char actual;      // actual character in sm_string
   131:   char escape;      // char that follows backslash to produce 'actual'
   132: } const escapes[] = {
   133:   { '\0', '0' },  // nul
   134:   { '\a', 'a' },  // bell
   135:   { '\b', 'b' },  // backspace
   136:   { '\f', 'f' },  // form feed
   137:   { '\n', 'n' },  // newline
   138:   { '\r', 'r' },  // carriage return
   139:   { '\t', 't' },  // tab
   140:   { '\v', 'v' },  // vertical tab
   141:   { '\\', '\\'},  // backslash
   142:   { '"',  '"' },  // double-quote
   143:   { '\'', '\''},  // single-quote
   144: };
   145: 
   146: 
   147: sm_string encodeWithEscapes(char const *p, int len)
   148: {
   149:   sm_stringBuilder sb;
   150: 
   151:   for (; len>0; len--, p++) {
   152:     // look for an escape code
   153:     unsigned i;
   154:     for (i=0; i<TABLESIZE(escapes); i++) {
   155:       if (escapes[i].actual == *p) {
   156:         sb << '\\' << escapes[i].escape;
   157:         break;
   158:       }
   159:     }
   160:     if (i<TABLESIZE(escapes)) {
   161:       continue;   // found it and printed it
   162:     }
   163: 
   164:     // try itself
   165:     if (isprint(*p)) {
   166:       sb << *p;
   167:       continue;
   168:     }
   169: 
   170:     // use the most general notation
   171:     char tmp[5];
   172:     sprintf(tmp, "\\x%02X", (unsigned char)(*p));
   173:     sb << tmp;
   174:   }
   175: 
   176:   return sb;
   177: }
   178: 
   179: 
   180: sm_string encodeWithEscapes(char const *p)
   181: {
   182:   return encodeWithEscapes(p, std::strlen(p));
   183: }
   184: 
   185: 
   186: sm_string quoted(char const *src)
   187: {
   188:   return sm_stringc << "\""
   189:                  << encodeWithEscapes(src, std::strlen(src))
   190:                  << "\"";
   191: }
   192: 
   193: 
   194: void decodeEscapes(sm_string &dest, int &destLen, char const *src,
   195:                    char delim, bool allowNewlines)
   196: {
   197:   // place to collect the sm_string characters
   198:   sm_stringBuilder sb;
   199:   destLen = 0;
   200: 
   201:   while (*src != '\0') {
   202:     if (*src == '\n' && !allowNewlines) {
   203:       xformat("unescaped newline (unterminated sm_string)");
   204:     }
   205:     if (*src == delim) {
   206:       xformat(sm_stringc << "unescaped delimiter (" << delim << ")");
   207:     }
   208: 
   209:     if (*src != '\\') {
   210:       // easy case
   211:       sb << *src;
   212:       destLen++;
   213:       src++;
   214:       continue;
   215:     }
   216: 
   217:     // advance past backslash
   218:     src++;
   219: 
   220:     // see if it's a simple one-char backslash code;
   221:     // start at 1 so we do *not* use the '\0' code since
   222:     // that's actually a special case of \0123', and
   223:     // interferes with the latter
   224:     int i;
   225:     for (i=1; i<TABLESIZE(escapes); i++) {
   226:       if (escapes[i].escape == *src) {
   227:         sb << escapes[i].actual;
   228:         destLen++;
   229:         src++;
   230:         break;
   231:       }
   232:     }
   233:     if (i < TABLESIZE(escapes)) {
   234:       continue;
   235:     }
   236: 
   237:     if (*src == '\0') {
   238:       xformat("backslash at end of sm_string");
   239:     }
   240: 
   241:     if (*src == '\n') {
   242:       // escaped newline; advance to first non-whitespace
   243:       src++;
   244:       while (*src==' ' || *src=='\t') {
   245:         src++;
   246:       }
   247:       continue;
   248:     }
   249: 
   250:     if (*src == 'x' || isdigit(*src)) {
   251:       // hexadecimal or octal char (it's unclear to me exactly how to
   252:       // parse these since it's supposedly legal to have e.g. "\x1234"
   253:       // mean a one-char sm_string.. whatever)
   254:       bool hex = (*src == 'x');
   255:       if (hex) {
   256:         src++;
   257: 
   258:         // strtoul is willing to skip leading whitespace
   259:         if (isspace(*src)) {
   260:           xformat("whitespace following hex (\\x) escape");
   261:         }
   262:       }
   263: 
   264:       char const *endptr;
   265:       unsigned long val = strtoul(src, (char**)&endptr, hex? 16 : 8);
   266:       if (src == endptr) {
   267:         // this can't happen with the octal escapes because
   268:         // there is always at least one valid digit
   269:         xformat("invalid hex (\\x) escape");
   270:       }
   271: 
   272:       sb << (unsigned char)val;    // possible truncation..
   273:       destLen++;
   274:       src = endptr;
   275:       continue;
   276:     }
   277: 
   278:     // everything not explicitly covered will be considered
   279:     // an error (for now), even though the C++ spec says
   280:     // it should be treated as if the backslash were not there
   281:     //
   282:     // 7/29/04: now making backslash the identity transformation in
   283:     // this case
   284:     //
   285:     // copy character as if it had not been backslashed
   286:     sb << *src;
   287:     destLen++;
   288:     src++;
   289:   }
   290: 
   291:   // copy to 'dest'
   292:   dest.setlength(destLen);       // this sets the NUL
   293:   if (destLen > 0) {
   294:     std::memcpy(dest.pchar(), sb.pchar(), destLen);
   295:   }
   296: }
   297: 
   298: 
   299: sm_string parseQuotedString(char const *text)
   300: {
   301:   if (!( text[0] == '"' &&
   302:          text[std::strlen(text)-1] == '"' )) {
   303:     xformat(sm_stringc << "quoted sm_string is missing quotes: " << text);
   304:   }
   305: 
   306:   // strip the quotes
   307:   sm_string noQuotes = sm_string(text+1, std::strlen(text)-2);
   308: 
   309:   // decode escapes
   310:   sm_string ret;
   311:   int dummyLen;
   312:   decodeEscapes(ret, dummyLen, noQuotes, '"');
   313:   return ret;
   314: }
   315: 
   316: 
   317: sm_string localTimeString()
   318: {
   319:   time_t t = time(NULL);
   320:   char const *p = asctime(localtime(&t));
   321:   return sm_string(p, std::strlen(p) - 1);     // strip final newline
   322: }
   323: 
   324: // rewritten by JMS to handle /, \ and : as separators
   325: sm_string sm_basename(char const *src)
   326: {
   327:   int n = std::strlen(src);
   328:   while(n>0 && (src[n-1] == '/' || src[n-1] == '\\' || src[n-1] == ':'))--n;
   329:   int m = n;
   330:   while(m>0 && src[m-1] != '/' && src[m-1] != '\\' && src[m-1] != ':')--m;
   331:   if(n==0 && m==0) return sm_string(src);
   332:   else return sm_string(src+m,n-m);
   333: }
   334: 
   335: sm_string dirname(char const *src)
   336: {
   337:   int n = std::strlen(src);
   338:   while(n>0 && (src[n-1] == '/' || src[n-1] == '\\' || src[n-1] == ':'))--n;
   339:   int m = n;
   340:   while(m>0 && src[m-1] != '/' && src[m-1] != '\\' && src[m-1] != ':')--m;
   341:   if(n==0 && m==0) return sm_string("."); // JMS: this is just wrong ..
   342:   else return sm_string(src,m);
   343: }
   344: 
   345: 
   346: // I will expand this definition as I go to get more knowledge about
   347: // English irregularities as I need it
   348: sm_string plural(int n, char const *prefix)
   349: {
   350:   if (n==1) {
   351:     return sm_string(prefix);
   352:   }
   353: 
   354:   if (0==strcmp(prefix, "was")) {
   355:     return sm_string("were");
   356:   }
   357:   if (prefix[std::strlen(prefix)-1] == 'y') {
   358:     return sm_stringc << sm_string(prefix, std::strlen(prefix)-1) << "ies";
   359:   }
   360:   else {
   361:     return sm_stringc << prefix << "s";
   362:   }
   363: }
   364: 
   365: sm_string pluraln(int n, char const *prefix)
   366: {
   367:   return sm_stringc << n << " " << plural(n, prefix);
   368: }
   369: 
   370: 
   371: char *copyToStaticBuffer(char const *s)
   372: {
   373:   enum { SZ=200 };
   374:   static char buf[SZ+1];
   375: 
   376:   int len = std::strlen(s);
   377:   if (len > SZ) len=SZ;
   378:   std::memcpy(buf, s, len);
   379:   buf[len] = 0;
   380: 
   381:   return buf;
   382: }
   383: 
   384: 
   385: bool prefixEquals(char const *str, char const *prefix)
   386: {
   387:   int slen = std::strlen(str);
   388:   int plen = std::strlen(prefix);
   389:   return slen >= plen &&
   390:          0==std::memcmp(str, prefix, plen);
   391: }
   392: 
   393: bool suffixEquals(char const *str, char const *suffix)
   394: {
   395:   int slen = std::strlen(str);
   396:   int ulen = std::strlen(suffix);    // sUffix
   397:   return slen >= ulen &&
   398:          0==std::memcmp(str+slen-ulen, suffix, ulen);
   399: }
   400: 
   401: 
   402: void writeStringToFile(char const *str, char const *fname)
   403: {
   404:   AutoFILE fp(fname, "w");
   405: 
   406:   if (fputs(str, fp) < 0) {
   407:     xbase("fputs: EOF");
   408:   }
   409: }
   410: 
   411: 
   412: sm_string readStringFromFile(char const *fname)
   413: {
   414:   AutoFILE fp(fname, "r");
   415: 
   416:   sm_stringBuilder sb;
   417: 
   418:   char buf[4096];
   419:   for (;;) {
   420:     int len = fread(buf, 1, 4096, fp);
   421:     if (len < 0) {
   422:       xbase("fread failed");
   423:     }
   424:     if (len == 0) {
   425:       break;
   426:     }
   427: 
   428:     sb.append(buf, len);
   429:   }
   430: 
   431:   return sb;
   432: }
   433: 
   434: 
   435: bool readLine(sm_string &dest, FILE *fp)
   436: {
   437:   char buf[80];
   438: 
   439:   if (!fgets(buf, 80, fp)) {
   440:     return false;
   441:   }
   442: 
   443:   if (buf[std::strlen(buf)-1] == '\n') {
   444:     // read a newline, we got the whole line
   445:     dest = buf;
   446:     return true;
   447:   }
   448: 
   449:   // only got part of the sm_string; need to iteratively construct
   450:   sm_stringBuilder sb;
   451:   while (buf[std::strlen(buf)-1] != '\n') {
   452:     sb << buf;
   453:     if (!fgets(buf, 80, fp)) {
   454:       // found eof after partial; return partial *without* eof
   455:       // indication, since we did in fact read something
   456:       break;
   457:     }
   458:   }
   459: 
   460:   dest = sb;
   461:   return true;
   462: }
   463: 
   464: 
   465: sm_string chomp(char const *src)
   466: {
   467:   if (src && src[std::strlen(src)-1] == '\n') {
   468:     return sm_string(src, std::strlen(src)-1);
   469:   }
   470:   else {
   471:     return sm_string(src);
   472:   }
   473: }
   474: 
   475: 
   476: // ----------------------- test code -----------------------------
   477: #ifdef TEST_STRUTIL
   478: 
   479: #include "sm_test.h"
   480: #include <stdio.h>     // printf
   481: 
   482: void expRangeVector(char const *in, char const *out)
   483: {
   484:   printf("expRangeVector(%s, %s)\n", in, out);
   485:   sm_string result = expandRanges(in);
   486:   xassert(result.equals(out));
   487: }
   488: 
   489: void trVector(char const *in, char const *srcSpec, char const *destSpec, char const *out)
   490: {
   491:   printf("trVector(%s, %s, %s, %s)\n", in, srcSpec, destSpec, out);
   492:   sm_string result = translate(in, srcSpec, destSpec);
   493:   xassert(result.equals(out));
   494: }
   495: 
   496: void decodeVector(char const *in, char const *out, int outLen)
   497: {
   498:   printf("decodeVector: \"%s\"\n", in);
   499:   sm_string dest;
   500:   int destLen;
   501:   decodeEscapes(dest, destLen, in, '\0' /*delim, ignored*/, false /*allowNewlines*/);
   502:   xassert(destLen == outLen);
   503:   xassert(0==std::memcmp(out, dest.pcharc(), destLen));
   504: }
   505: 
   506: void basenameVector(char const *in, char const *out)
   507: {
   508:   printf("basenameVector(%s, %s)\n", in, out);
   509:   sm_string result = sm_basename(in);
   510:   xassert(result.equals(out));
   511: }
   512: 
   513: void dirnameVector(char const *in, char const *out)
   514: {
   515:   printf("dirnameVector(%s, %s)\n", in, out);
   516:   sm_string result = dirname(in);
   517:   xassert(result.equals(out));
   518: }
   519: 
   520: void pluralVector(int n, char const *in, char const *out)
   521: {
   522:   printf("pluralVector(%d, %s, %s)\n", n, in, out);
   523:   sm_string result = plural(n, in);
   524:   xassert(result.equals(out));
   525: }
   526: 
   527: 
   528: void entry()
   529: {
   530:   expRangeVector("abcd", "abcd");
   531:   expRangeVector("a", "a");
   532:   expRangeVector("a-k", "abcdefghijk");
   533:   expRangeVector("0-9E-Qz", "0123456789EFGHIJKLMNOPQz");
   534: 
   535:   trVector("foo", "a-z", "A-Z", "FOO");
   536:   trVector("foo BaR", "a-z", "A-Z", "FOO BAR");
   537:   trVector("foo BaR", "m-z", "M-Z", "fOO BaR");
   538: 
   539:   decodeVector("\\r\\n", "\r\n", 2);
   540:   decodeVector("abc\\0def", "abc\0def", 7);
   541:   decodeVector("\\033", "\033", 1);
   542:   decodeVector("\\x33", "\x33", 1);
   543:   decodeVector("\\?", "?", 1);
   544: 
   545:   basenameVector("a/b/c", "c");
   546:   basenameVector("abc", "abc");
   547:   basenameVector("/", "");
   548:   basenameVector("a/b/c/", "c");
   549: 
   550:   dirnameVector("a/b/c", "a/b");
   551:   dirnameVector("a/b/c/", "a/b");
   552:   dirnameVector("/a", "/");
   553:   dirnameVector("abc", ".");
   554:   dirnameVector("/", "/");
   555: 
   556:   pluralVector(1, "path", "path");
   557:   pluralVector(2, "path", "paths");
   558:   pluralVector(1, "fly", "fly");
   559:   pluralVector(2, "fly", "flies");
   560:   pluralVector(2, "was", "were");
   561: }
   562: 
   563: 
   564: USUAL_MAIN
   565: 
   566: #endif // TEST_STRUTIL
End cpp section to elk/sm_strutil.cpp[1]
Start cpp section to elk/sm_svdict.cpp[1 /1 ]
     1: #line 13187 "./lpsrc/sm.pak"
     2: // svdict.cc            see license.txt for copyright and terms of use
     3: // code for svdict.h
     4: 
     5: #include "sm_svdict.h"
     6: #include <cstring>         // strcmp
     7: 
     8: 
     9: #define FOREACH_NODE(itervar) \
    10:   for(Node *itervar = top; itervar != NULL; itervar = itervar->next)
    11: 
    12: #define FOREACH_ITER(dict, itervar) \
    13:   for(Iter itervar = (dict).getIter(); !itervar.isDone(); itervar.next())
    14: 
    15: #define FOREACH_ITERC(dict, itervar) \
    16:   for(IterC itervar = (dict).getIterC(); !itervar.isDone(); itervar.next())
    17: 
    18: #define MUTABLE_SORT(obj) (const_cast<StringVoidDict&>(obj)).sort()
    19: 
    20: 
    21: STATICDEF char const *StringVoidDict::Node::getKey(StringVoidDict::Node const *n)
    22: {
    23:   return n->key.pcharc();
    24: }
    25: 
    26: 
    27: StringVoidDict::StringVoidDict()
    28:   : top(NULL),
    29:     hash((StringHash::GetKeyFn)Node::getKey)
    30: {}
    31: 
    32: 
    33: StringVoidDict::StringVoidDict(StringVoidDict const &obj)
    34:   : top(NULL),
    35:     hash((StringHash::GetKeyFn)Node::getKey)
    36: {
    37:   *this = obj;
    38: }
    39: 
    40: 
    41: StringVoidDict::~StringVoidDict()
    42: {
    43:   SELFCHECK();
    44:   empty();
    45: }
    46: 
    47: 
    48: StringVoidDict& StringVoidDict::operator= (StringVoidDict const &obj)
    49: {
    50:   if (this == &obj) {
    51:     return *this;
    52:   }
    53: 
    54:   empty();
    55: 
    56:   Node *end = top;
    57:   FOREACH_ITERC(obj, src) {
    58:     // const_cast needed because the resulting dictionary can now access
    59:     // the data objects and modify them... hmm...
    60:     Node *newnode = new Node(src.key(), const_cast<void*>(src.value()));
    61:     if (!end) {
    62:       // first element of list
    63:       end = top = newnode;
    64:     }
    65:     else {
    66:       // adding to end of nonempty list
    67:       end = end->next = newnode;
    68:     }
    69:     hash.add(newnode->key, newnode);
    70:   }
    71: 
    72:   SELFCHECK();
    73:   return *this;
    74: }
    75: 
    76: 
    77: bool StringVoidDict::operator== (StringVoidDict const &obj) const
    78: {
    79:   // sort both lists
    80:   MUTABLE_SORT(*this);
    81:   MUTABLE_SORT(obj);
    82: 
    83:   IterC ths(*this), other(obj);
    84:   while (!ths.isDone() && !other.isDone()) {
    85:     if (0!=std::strcmp(ths.key(), other.key()) ||
    86:         ths.value() != other.value()) {
    87:       return false;
    88:     }
    89:     ths.next();
    90:     other.next();
    91:   }
    92: 
    93:   if (!ths.isDone() || !other.isDone()) {
    94:     // one finished first, so they can't be equal
    95:     return false;
    96:   }
    97: 
    98:   return true;
    99: }
   100: 
   101: 
   102: bool StringVoidDict::isEmpty() const
   103: {
   104:   return top == NULL;
   105: }
   106: 
   107: 
   108: int StringVoidDict::size() const
   109: {
   110:   return hash.getNumEntries();
   111: }
   112: 
   113: 
   114: bool StringVoidDict::query(char const *key, void *&value) const
   115: {
   116:   Node *n = (Node*)hash.get(key);
   117:   if (!n) {
   118:     return false;
   119:   }
   120: 
   121:   value = n->value;
   122:   return true;
   123: }
   124: 
   125: 
   126: void *StringVoidDict::queryf(char const *key) const
   127: {
   128:   void *ret;
   129:   bool ok = query(key, ret);
   130:   xassert(ok);
   131:   return ret;
   132: }
   133: 
   134: 
   135: void *StringVoidDict::queryif(char const *key) const
   136: {
   137:   void *ret;
   138:   if (query(key, ret)) {
   139:     return ret;
   140:   }
   141:   else {
   142:     return NULL;
   143:   }
   144: }
   145: 
   146: 
   147: bool StringVoidDict::isMapped(char const *key) const
   148: {
   149:   void *dummy;
   150:   return query(key, dummy);
   151: }
   152: 
   153: 
   154: void StringVoidDict::add(char const *key, void *value)
   155: {
   156:   xassert(!isMapped(key));
   157: 
   158:   // just prepend; we'll sort later (when an iterator is retrieved)
   159:   top = new Node(key, value, top);
   160:   hash.add(key, top);
   161: 
   162:   SELFCHECK();
   163: }
   164: 
   165: 
   166: void *StringVoidDict::modify(char const *key, void *newValue)
   167: {
   168:   Iter entry = find(key);
   169:   xassert(!entry.isDone());
   170: 
   171:   void *ret = entry.value();
   172:   entry.value() = newValue;
   173: 
   174:   SELFCHECK();
   175:   return ret;
   176: }
   177: 
   178: 
   179: StringVoidDict::Iter StringVoidDict::find(char const *key)
   180: {
   181:   Node *n = (Node*)hash.get(key);
   182:   return Iter(n);
   183: }
   184: 
   185: 
   186: void *StringVoidDict::remove(char const *key)
   187: {
   188:   void *ret;     // will be previous value
   189:   xassert(top);
   190: 
   191:   // check for removal of top element
   192:   if (0==std::strcmp(top->key, key)) {
   193:     Node *temp = top;
   194:     top = top->next;
   195:     ret = temp->value;
   196:     hash.remove(temp->key);
   197:     delete temp;
   198:   }
   199: 
   200:   // find node to remove in tail of list
   201:   else {
   202:     Node *p = top;
   203:     while (p->next && 0!=std::strcmp(p->next->key, key)) {
   204:       p = p->next;
   205:     }
   206: 
   207:     if (!p->next) {
   208:       // reached the end of the list without finding the key
   209:       xfailure("failed to find key");
   210:     }
   211: 
   212:     // remove p->next from the list
   213:     Node *temp = p->next;
   214:     p->next = p->next->next;
   215:     ret = temp->value;
   216:     hash.remove(temp->key);
   217:     delete temp;
   218:   }
   219: 
   220:   SELFCHECK();
   221:   return ret;
   222: }
   223: 
   224: 
   225: void StringVoidDict::empty()
   226: {
   227:   emptyAndDel(NULL);
   228: }
   229: 
   230: void StringVoidDict::emptyAndDel(DelFn func)
   231: {
   232:   while (top) {
   233:     Node *temp = top;
   234:     top = top->next;
   235: 
   236:     if (func != NULL) {
   237:       func(temp->value);
   238:     }
   239:     hash.remove(temp->key);
   240:     delete temp;
   241:   }
   242: 
   243:   SELFCHECK();
   244: }
   245: 
   246: 
   247: StringVoidDict::Iter StringVoidDict::getIter()
   248: {
   249:   sort();        // must return items in sorted order
   250:   return Iter(top);
   251: }
   252: 
   253: 
   254: StringVoidDict::IterC StringVoidDict::getIterC() const
   255: {
   256:   //sort();
   257:   const_cast<StringVoidDict*>(this)->sort();    // mutable
   258:   return IterC(top);
   259: }
   260: 
   261: 
   262: void StringVoidDict::foreach(ForeachFn func, void *extra) const
   263: {
   264:   const_cast<StringVoidDict*>(this)->sort();    // mutable
   265: 
   266:   for (Node *n = top; n != NULL; n = n->next) {
   267:     if (func(n->key, n->value, extra)) {
   268:       return;
   269:     }
   270:   }
   271: }
   272: 
   273: 
   274: // use simple insertion sort for now
   275: /*mutable*/ void StringVoidDict::sort()
   276: {
   277:   if (!top) {
   278:     return;
   279:   }
   280: 
   281:   // invariant: sequence of nodes from 'top' to 'walker', inclusive,
   282:   //            is always sorted
   283:   Node *walker = top;
   284:   while (walker->next != NULL) {
   285:     // see if walker->next is out of order
   286:     if (0 <= std::strcmp(walker->key, walker->next->key)) {
   287:       // it's in order
   288:       walker = walker->next;
   289:       continue;
   290:     }
   291: 
   292:     // remove walker->next from where it is (note that this has
   293:     // the effect of advancing walker, so below here we won't
   294:     // have another movement of walker)
   295:     Node *mover = walker->next;
   296:     walker->next = walker->next->next;
   297:     mover->next = NULL;       // (redundant because of (**) lines)
   298: 
   299:     // insert at head?
   300:     if (0 < std::strcmp(mover->key, top->key)) {
   301:       mover->next = top;            // (**)
   302:       top = mover;
   303:       continue;
   304:     }
   305: 
   306:     // must find correct place to insert mover (will find the place
   307:     // where we can insert mover just before searcher->next)
   308:     Node *searcher = top;
   309:     while (0 < std::strcmp(searcher->next->key, mover->key)) {
   310:       searcher = searcher->next;
   311:       xassert(searcher != walker);
   312:         // otherwise how could mover have been out of order to begin with?
   313:     }
   314: 
   315:     // insert mover before searcher->next
   316:     mover->next = searcher->next;   // (**)
   317:     searcher->next = mover;
   318:   }
   319: 
   320:   SELFCHECK();
   321: 
   322:   #ifndef NDEBUG
   323:     verifySorted();
   324:   #endif
   325: }
   326: 
   327: 
   328: void StringVoidDict::verifySorted() const
   329: {
   330:   if (!top) {
   331:     return;
   332:   }
   333: 
   334:   Node *p = top;
   335:   while (p->next) {
   336:     xassert(0 <= std::strcmp(p->key, p->next->key));
   337:     p = p->next;
   338:   }
   339: }
   340: 
   341: 
   342: // verify that the list is well structured
   343: void StringVoidDict::selfCheck() const
   344: {
   345:   {
   346:     Node *fast = top, *slow = top;
   347:     while (fast && fast->next) {
   348:       fast = fast->next->next;
   349:       slow = slow->next;
   350: 
   351:       xassert(fast != slow);
   352:         // if these become equal, the list is circular
   353:     }
   354:   }
   355: 
   356:   // check counts, mappings
   357:   int ct=0;
   358:   for (Node *n = top; n != NULL; n = n->next, ct++) {
   359:     xassert(hash.get(n->key) == n);
   360:   }
   361:   xassert(hash.getNumEntries() == ct);
   362: }
   363: 
   364: 
   365: void StringVoidDict::insertOstream(std::ostream &os) const
   366: {
   367:   FOREACH_ITERC(*this, entry) {
   368:     os << entry.key() << " = " << entry.value() << std::endl;
   369:   }
   370: }
   371: 
   372: 
   373: sm_string StringVoidDict::toString() const
   374: {
   375:   sm_stringBuilder sb;
   376:   sb << "{";
   377:   int count=0;
   378:   FOREACH_ITERC(*this, entry) {
   379:     if (count++ > 0) {
   380:       sb << ",";
   381:     }
   382:     sb << " " << entry.key() << "=\"" << entry.value() << "\"";
   383:   }
   384:   sb << " }";
   385:   return sb;
   386: }
   387: 
   388: 
   389: // -------------------- test code ------------------------
   390: #ifdef TEST_SVDICT
   391: 
   392: #include "sm_test.h"
   393: #include <stdlib.h>    // rand
   394: 
   395: #define myrandom(n) (rand()%(n))
   396: 
   397: char randChar()
   398: {
   399:   return (char)(myrandom(127-32+1)+32);
   400: }
   401: 
   402: sm_string randString(int len)
   403: {
   404:   sm_stringBuilder str;
   405:   loopj(len) {
   406:     str << randChar();
   407:   }
   408:   return str;
   409: }
   410: 
   411: sm_string randStringRandLen(int maxlen)
   412: {
   413:   return randString(myrandom(maxlen)+1);
   414: }
   415: 
   416: sm_string randKey(StringVoidDict const &dict)
   417: {
   418:   int size = dict.size();
   419:   xassert(size > 0);
   420: 
   421:   int nth = myrandom(size);
   422:   StringVoidDict::IterC entry(dict);
   423:   for (; nth > 0; entry.next(), nth--)
   424:     {}
   425: 
   426:   return entry.key();
   427: }
   428: 
   429: void *randVoidPtr()
   430: {
   431:   return (void*)(myrandom(100) * 8);
   432: }
   433: 
   434: 
   435: void entry()
   436: {
   437:   StringVoidDict dict;
   438:   int size=0, collisions=0;
   439: 
   440:   int iters = 1000;
   441:   loopi(iters) {
   442:     switch (myrandom(6)) {
   443:       case 0: {
   444:         // insert a random element
   445:         sm_string key = randStringRandLen(10);
   446:         void *value = randVoidPtr();
   447: 
   448:         if (!dict.isMapped(key)) {
   449:           dict.add(key, value);
   450:           size++;
   451:         }
   452:         else {
   453:           collisions++;
   454:         }
   455:         break;
   456:       }
   457: 
   458:       case 1: {
   459:         // remove a random element
   460:         if (dict.isEmpty()) {
   461:           break;
   462:         }
   463: 
   464:         sm_string key = randKey(dict);
   465:         dict.remove(key);
   466:         size--;
   467:         break;
   468:       }
   469: 
   470:       case 2: {
   471:         // check a random element that should not be there
   472:         sm_string key = randStringRandLen(10);
   473:         if (dict.isMapped(key)) {
   474:           collisions++;
   475:         }
   476:         break;
   477:       }
   478: 
   479:       case 3: {
   480:         // verify that computed length is right
   481:         xassert(size == dict.size());
   482:         break;
   483:       }
   484: 
   485:       case 4: {
   486:         // test == and =
   487:         StringVoidDict dict2(dict);
   488:         xassert(dict2 == dict);
   489:         xassert(dict2.size() == dict.size());
   490: 
   491:         // modify it, then verify inequality
   492:         if (!dict2.isEmpty()) {
   493:           sm_string key = randKey(dict2);
   494:           void *value = dict2.queryf(key);
   495: 
   496:           if (myrandom(2) == 0) {
   497:             dict2.remove(key);
   498:           }
   499:           else {
   500:             dict2.modify(key, (void*)((int)value + 24));
   501:           }
   502:           xassert(dict2 != dict);
   503:         }
   504: 
   505:         break;
   506:       }
   507: 
   508:       case 5: {
   509:         // random modification
   510:         if (!dict.isEmpty()) {
   511:           sm_string key = randKey(dict);
   512:           dict.modify(key, randVoidPtr());
   513:         }
   514:         break;
   515:       }
   516: 
   517:       default:
   518:         xfailure("huh?");
   519:         break;
   520:     }
   521:   }
   522: 
   523:   std::cout << "final size: " << size
   524:        << "\ncollisions: " << collisions
   525:        << "\n";
   526: 
   527:   std::cout << "all tests passed\n";
   528: }
   529: 
   530: USUAL_MAIN
   531: 
   532: #endif // TEST_STRDICT
End cpp section to elk/sm_svdict.cpp[1]
Start cpp section to elk/sm_trace.cpp[1 /1 ]
     1: #line 13720 "./lpsrc/sm.pak"
     2: // trace.cc            see license.txt for copyright and terms of use
     3: // code for trace.h
     4: 
     5: #include "sm_trace.h"
     6: #include "sm_objlist.h"
     7: #include "sm_str.h"
     8: #include "sm_strtokp.h"
     9: #include "sm_nonport.h"
    10: 
    11: #include <fstream>   // ofstream
    12: #include <stdlib.h>    // getenv
    13: 
    14: 
    15: // auto-init
    16: static bool inited = false;
    17: 
    18: // list of active tracers, initially empty
    19: static ObjList<sm_string> tracers;
    20: 
    21: // stream connected to /dev/null
    22: std::ofstream devNullObj("/dev/null");
    23: static std::ostream *devNull = &devNullObj;
    24: 
    25: 
    26: // initialize
    27: static void init()
    28: {
    29:   if (inited) {
    30:     return;
    31:   }
    32: 
    33:   // there's a more efficient way to do this, but I don't care to dig
    34:   // around and find out how
    35:   // this leaks, and now that I'm checking for them, it's a little
    36:   // annoying...
    37:   //devNull = new ofstream("/dev/null");
    38: 
    39:   inited = true;
    40: }
    41: 
    42: 
    43: void traceAddSys(char const *sysName)
    44: {
    45:   init();
    46: 
    47:   tracers.prepend(new sm_string(sysName));
    48: }
    49: 
    50: 
    51: void traceRemoveSys(char const *sysName)
    52: {
    53:   init();
    54: 
    55:   MUTATE_EACH_OBJLIST(sm_string, tracers, mut) {
    56:     if (mut.data()->compareTo(sysName) == 0) {
    57:       mut.deleteIt();
    58:       return;
    59:     }
    60:   }
    61:   xfailure("traceRemoveSys: tried to remove system that isn't there");
    62: }
    63: 
    64: 
    65: bool tracingSys(char const *sysName)
    66: {
    67:   init();
    68: 
    69:   FOREACH_OBJLIST(sm_string, tracers, iter) {
    70:     if (iter.data()->compareTo(sysName) == 0) {
    71:       return true;
    72:     }
    73:   }
    74:   return false;
    75: }
    76: 
    77: 
    78: void traceRemoveAll()
    79: {
    80:   tracers.deleteAll();
    81: }
    82: 
    83: 
    84: std::ostream &trace(char const *sysName)
    85: {
    86:   init();
    87: 
    88:   if (tracingSys(sysName)) {
    89:     std::cout << "%%% " << sysName << ": ";
    90:     return std::cout;
    91:   }
    92:   else {
    93:     return *devNull;
    94:   }
    95: }
    96: 
    97: 
    98: void trstr(char const *sysName, char const *traceString)
    99: {
   100:   trace(sysName) << traceString << std::endl;
   101: }
   102: 
   103: 
   104: std::ostream &traceProgress(int level)
   105: {
   106:   if ( (level == 1) ||
   107:        (level == 2 && tracingSys("progress2")) ) {
   108:     static long progStart = getMilliseconds();
   109: 
   110:     return trace("progress") << (getMilliseconds() - progStart) << "ms: ";
   111:   }
   112:   else {
   113:     return *devNull;
   114:   }
   115: }
   116: 
   117: 
   118: void traceAddMultiSys(char const *systemNames)
   119: {
   120:   StrtokParse tok(systemNames, ",");
   121:   loopi(tok) {
   122:     if (tok[i][0] == '-') {
   123:       // treat a leading '-' as a signal to *remove*
   124:       // a tracing flag, e.g. from some defaults specified
   125:       // statically
   126:       char const *name = tok[i]+1;
   127:       if (tracingSys(name)) {      // be forgiving here
   128:         traceRemoveSys(name);
   129:       }
   130:       else {
   131:         std::cout << "Currently, `" << name << "' is not being traced.\n";
   132:       }
   133:     }
   134: 
   135:     else {
   136:       // normal behavior: add things to the trace list
   137:       traceAddSys(tok[i]);
   138:     }
   139:   }
   140: }
   141: 
   142: 
   143: bool traceProcessArg(int &argc, char **&argv)
   144: {
   145:   traceAddFromEnvVar();
   146: 
   147:   if (argc >= 3  &&  0==std::strcmp(argv[1], "-tr")) {
   148:     traceAddMultiSys(argv[2]);
   149:     argc -= 2;
   150:     argv += 2;
   151:     return true;
   152:   }
   153:   else {
   154:     return false;
   155:   }
   156: }
   157: 
   158: 
   159: bool ignoreTraceEnvVar = false;
   160: 
   161: void traceAddFromEnvVar()
   162: {
   163:   if (ignoreTraceEnvVar) {
   164:     return;
   165:   }
   166: 
   167:   char const *var = getenv("TRACE");
   168:   if (var) {
   169:     traceAddMultiSys(var);
   170:   }
   171: 
   172:   ignoreTraceEnvVar = true;
   173: }
   174: 
   175: 
   176: // EOF
End cpp section to elk/sm_trace.cpp[1]
Start cpp section to elk/sm_trdelete.cpp[1 /1 ]
     1: #line 13897 "./lpsrc/sm.pak"
     2: // trdelete.cc            see license.txt for copyright and terms of use
     3: // code for trdelete.h
     4: 
     5: #include <stdlib.h>       // abs
     6: #include "sm_trdelete.h"
     7: #include <cstring>       // std::memset
     8: #include "sm_breaker.h"
     9: 
    10: 
    11: // There is a surprising twist to our story.  When an object is created with
    12: // 'new' on the dynamic heap, and it throws an exception, its destructor is not
    13: // called, because the object is not fully constructed yet.  However, the
    14: // storage allocated for that object should be freed; the exception runtimes
    15: // do this.
    16: 
    17: // But, there is a bug (under Borland C++ 4.5) when operator delete is called
    18: // under these circumstances, the size argument is wrong.  I've been getting
    19: // values like 0x12fc14, which are pointers to objects on the stack.  After
    20: // quite a bit of dump tracing, I can't find an easy way to turn this pointer
    21: // into the desired value.
    22: 
    23: // However, the dynamic heap node format for the Borland runtimes is pretty
    24: // simple, and an estimate of the heap size can readily be determined; the
    25: // dword just before the passed block pointer has such an estimate.  Therefore,
    26: // my strategy is to compare the given size with the dword at offset -4, and if
    27: // they are within 64 of each other (I've never seen differences greater than
    28: // 16), the size argument is considered valid.  If they aren't there isn't a
    29: // reliable way to convert the estimate into a precise size, so I just skip
    30: // trashing the memory altogether.
    31: 
    32: 
    33: static void trash(void *blk, size_t size)
    34: {
    35:   #ifdef __BORLANDC__
    36:   long guess = ((long*)blk)[-1];
    37:   if (abs(guess - (long)size) > 64) {
    38:     // assume the size is bogus
    39:     breaker();     // I want to know about it, for now
    40:     return;
    41:   }
    42:   #endif
    43: 
    44:   // assume the size is ok
    45:   std::memset(blk, 0xAA, size);
    46:     // the choice of AA is made as it is easily recognizable,
    47:     // and 0xAAAAAAAA is pretty much always a bad pointer
    48: }
    49: 
    50: 
    51: void trashingDelete(void *blk, size_t size)
    52: {
    53:   trash(blk, size);
    54: 
    55:   // use the global delete operator to free the memory;
    56:   // gratuitous cast to char* to silence gcc warning
    57:   // "not a pointer-to-object type"
    58:   ::delete((char*)blk);
    59: }
    60: 
    61: 
    62: void trashingDeleteArr(void *blk, size_t size)
    63: {
    64:   trash(blk, size);
    65: 
    66:   // use the global delete operator to free the memory;
    67:   // (see comment about gratuitious cast, above)
    68:   ::delete[]((char*)blk);
    69: }
    70: 
    71: 
    72: // ------------------------ test code ---------------------
    73: #ifdef TEST_TRDELETE
    74: 
    75: #include <stdio.h>      // printf
    76: 
    77: class Foo {
    78: public:
    79:   TRASHINGDELETE
    80:   int junk[10];         // stay away from malloc's data structures
    81:   int x;
    82:   int moreJunk[10];     // more padding
    83: };
    84: 
    85: class Bar {
    86: public:
    87:   int junk[10];         // stay away from malloc's data structures
    88:   int x;
    89:   int moreJunk[10];     // more padding
    90: };
    91: 
    92: 
    93: int main()
    94: {
    95:   printf("malloc: %p\n", malloc(10));
    96: 
    97:   Foo *f = new Foo;
    98:   f->x = 5;
    99:   delete f;
   100:   if (f->x == 5) {
   101:     printf("trashing-delete failed\n");
   102:     return 2;
   103:   }
   104: 
   105:   Bar *b = new Bar;
   106:   b->x = 7;
   107:   delete b;
   108:   if ((unsigned)b->x == 0xAAAAAAAAu) {    // did it trash it anyway?
   109:     printf("non-trashing-delete failed\n");
   110:     return 2;
   111:   }
   112: 
   113:   printf("trashing delete works\n");
   114:   return 0;
   115: }
   116: 
   117: #endif // TEST_TRDELETE
End cpp section to elk/sm_trdelete.cpp[1]
Start cpp section to elk/sm_tsobjlist.cpp[1 /1 ]
     1: #line 14015 "./lpsrc/sm.pak"
     2: // tsobjlist.cc            see license.txt for copyright and terms of use
     3: // test of sobjlist.h
     4: 
     5: #include "sm_sobjlist.h"
     6: #include <stdio.h>       // printf
     7: 
     8: int main()
     9: {
    10:   char const *hi = "hi there";
    11:   char const *what = "what's up?";
    12: 
    13:   // the real purpose of this test is to make sure it's ok to
    14:   // add a 'const' qualifier inside the angle brackets, and get
    15:   // the effect I'm after
    16:   SObjList<char const> list;
    17: 
    18:   // 'prepend' accepts a T*, which should become a char const *;
    19:   // if it only becomes (e.g.) a char*, then this call should
    20:   // trigger a compile error
    21:   list.prepend(hi);
    22: 
    23:   list.append(what);
    24: 
    25:   // 'indexOf' accepts a T const *, so here I'm essentially verifying
    26:   // the compiler doesn't mind seeing 'const' twice
    27:   int i = list.indexOf(hi);
    28:   printf("index of 'hi' is %d\n", i);
    29: 
    30:   i = list.indexOf(what);
    31:   printf("index of 'what' is %d\n", i);
    32: 
    33:   // random test of extra 'const' outside the template context
    34:   // (gcc-2.95.3 doesn't like it, interesting..)
    35:   int const /*const*/ x = 5;
    36:   printf("x is %d\n", x);
    37: 
    38:   return 0;
    39: }
    40: 
    41: // EOF
End cpp section to elk/sm_tsobjlist.cpp[1]
Start cpp section to elk/sm_vdtllist.cpp[1 /1 ]
     1: #line 14057 "./lpsrc/sm.pak"
     2: // vdtllist.cc            see license.txt for copyright and terms of use
     3: // code for vdtllist.h
     4: 
     5: #include "sm_vdtllist.h"
     6: 
     7: void VoidTailList::steal(VoidTailList *src)
     8: {
     9:   if (src) {
    10:     top = src->top;
    11:     tail = src->tail;
    12:     src->top = NULL;    // paranoia
    13:     delete src;
    14:   }
    15:   else {
    16:     top = NULL;
    17:     tail = NULL;
    18:   }
    19: }
    20: 
    21: void VoidTailList::prepend(void *newitem)
    22: {
    23:   VoidList::prepend(newitem);
    24:   if (!tail) {
    25:     tail = top;
    26:   }
    27: }
    28: 
    29: void VoidTailList::append(void *newitem)
    30: {
    31:   if (isEmpty()) {
    32:     prepend(newitem);
    33:   }
    34:   else {
    35:     // payoff: constant-time append
    36:     tail->next = new VoidNode(newitem, NULL);
    37:     tail = tail->next;
    38:   }
    39: }
    40: 
    41: void VoidTailList::insertAt(void *newitem, int index)
    42: {
    43:   VoidList::insertAt(newitem, index);
    44:   adjustTail();
    45: }
    46: 
    47: void VoidTailList::concat(VoidTailList &srcList)
    48: {
    49:   // grab what will be the tail of the concatenated list
    50:   VoidNode *catTail = srcList.top? srcList.tail : tail;
    51: 
    52:   // build proper backbone structure
    53:   VoidList::concat(srcList);
    54: 
    55:   // fix tails
    56:   tail = catTail;
    57:   srcList.tail = NULL;
    58: }
    59: 
    60: void VoidTailList::adjustTail()
    61: {
    62:   if (!tail) {
    63:     tail = top;
    64:   }
    65:   else if (tail->next) {
    66:     tail = tail->next;
    67:   }
    68:   xassert(tail->next == NULL);
    69: }
    70: 
    71: void *VoidTailList::removeFirst()
    72: {
    73:   xassert(top);
    74:   if (top == tail) {
    75:     tail = NULL;
    76:   }
    77:   void *retval = top->data;
    78:   VoidNode *tmp = top;
    79:   top = top->next;
    80:   delete tmp;
    81:   return retval;
    82: }
    83: 
    84: void *VoidTailList::removeLast()
    85: {
    86:   xassert(top);
    87:   if (top == tail) {
    88:     return removeFirst();
    89:   }
    90: 
    91:   VoidNode *before = top;
    92:   while (before->next != tail) {
    93:     before = before->next;
    94:   }
    95:   void *retval = tail->data;
    96:   delete tail;
    97:   tail = before;
    98:   tail->next = NULL;
    99:   return retval;
   100: }
   101: 
   102: void *VoidTailList::removeAt(int index)
   103: {
   104:   xassert(top);
   105:   if (index == 0) {
   106:     return removeFirst();
   107:   }
   108: 
   109:   VoidNode *before = top;    // will point to node before one to be removed
   110:   index--;
   111:   while (index > 0) {
   112:     before = before->next;
   113:     index--;
   114:   }
   115:   xassert(index == 0);
   116: 
   117:   // fix 'tail' if necessary
   118:   if (tail == before->next) {
   119:     tail = before;
   120:   }
   121: 
   122:   // patch around before->next
   123:   VoidNode *toDelete = before->next;
   124:   void *retval = toDelete->data;
   125:   before->next = toDelete->next;
   126:   delete toDelete;
   127: 
   128:   return retval;
   129: }
   130: 
   131: void VoidTailList::removeAll()
   132: {
   133:   VoidList::removeAll();
   134:   tail = NULL;
   135: }
   136: 
   137: bool VoidTailList::prependUnique(void *newitem)
   138: {
   139:   bool retval = VoidList::prependUnique(newitem);
   140:   adjustTail();
   141:   return retval;
   142: }
   143: 
   144: bool VoidTailList::appendUnique(void *newitem)
   145: {
   146:   bool retval = VoidList::appendUnique(newitem);
   147:   adjustTail();
   148:   return retval;
   149: }
   150: 
   151: void VoidTailList::selfCheck() const
   152: {
   153:   VoidList::selfCheck();
   154: 
   155:   if (isNotEmpty()) {
   156:     // get last node
   157:     VoidNode *n = top;
   158:     while (n->next) {
   159:       n = n->next;
   160:     }
   161: 
   162:     // 'tail' should be the last one
   163:     xassert(tail == n);
   164:   }
   165:   else {
   166:     xassert(tail == NULL);
   167:   }
   168: }
   169: 
   170: 
   171: // --------------------- test code ------------------
   172: #ifdef TEST_VDTLLIST
   173: 
   174: #include <stdio.h>    // printf
   175: 
   176: int main()
   177: {
   178:   VoidTailList list;
   179:   int zero, one, two, three;
   180: 
   181:   // This isn't a very exhaustive test; it's mainly to check that
   182:   // selfCheck doesn't do anything really stupid (it used to).
   183: 
   184:   list.selfCheck();
   185: 
   186:   list.append(&two);     list.selfCheck();
   187:   list.prepend(&one);    list.selfCheck();
   188:   list.append(&three);   list.selfCheck();
   189:   list.prepend(&zero);   list.selfCheck();
   190: 
   191:   xassert(list.nth(0) == &zero);
   192:   xassert(list.nth(1) == &one);
   193:   xassert(list.nth(2) == &two);
   194:   xassert(list.nth(3) == &three);
   195: 
   196:   list.removeAll();
   197:   list.selfCheck();
   198: 
   199:   printf("vdtllist works\n");
   200: 
   201:   return 0;
   202: }
   203: 
   204: #endif // TEST_VDTLLIST
End cpp section to elk/sm_vdtllist.cpp[1]
Start cpp section to elk/sm_voidlist.cpp[1 /1 ]
     1: #line 14262 "./lpsrc/sm.pak"
     2: // voidlist.cc            see license.txt for copyright and terms of use
     3: // code for voidlist.h
     4: #include <functional>
     5: 
     6: #include "sm_voidlist.h"
     7: #include "sm_breaker.h"
     8: #include "sm_str.h"
     9: #include "sm_ckheap.h"
    10: 
    11: #include <stdlib.h>     // rand()
    12: #include <stdio.h>      // printf()
    13: 
    14: 
    15: VoidList::VoidList(VoidList const &obj)
    16:   : top(NULL)
    17: {
    18:   *this = obj;
    19: }
    20: 
    21: 
    22: // # of items in list
    23: int VoidList::count() const
    24: {
    25:   int ct=0;
    26:   for(VoidNode *p = top; p; p = p->next) {
    27:     ct++;
    28:   }
    29:   return ct;
    30: }
    31: 
    32: 
    33: // get particular item, 0 is first (item must exist)
    34: void *VoidList::nth(int which) const
    35: {
    36:   VoidNode *p;
    37:   xassert(which>=0);
    38:   for (p = top; which > 0; which--) {
    39:     xassert(p);
    40:     p = p->next;
    41:   }
    42:   if (p == NULL) {
    43:     xfailure(sm_stringc << "asked for list element "
    44:                      << (count()+which) << " (0-based) but list only has "
    45:                      << count() << " elements");
    46:   }
    47:   return p->data;
    48: }
    49: 
    50: 
    51: // fail assertion if list fails integrity check
    52: void VoidList::selfCheck() const
    53: {
    54:   if (!top) {
    55:     return;
    56:   }
    57: 
    58:   // The technique here is the fast/slow list traversal to find loops (which
    59:   // are the only way a singly-linked list can be bad). Basically, if there
    60:   // is a loop then the fast ptr will catch up to and equal the slow one; if
    61:   // not, the fast will simply find the terminating null. It is the only way
    62:   // I know of to find loops in O(1) space and O(n) time.
    63: 
    64:   VoidNode *slow=top, *fast=top->next;
    65:   while (fast && fast != slow) {
    66:     // check heap properties
    67:     checkHeapNode(fast);
    68: 
    69:     slow = slow->next;
    70:     fast = fast->next;
    71:     if (fast) {
    72:       checkHeapNode(fast);
    73:       fast = fast->next;      // usually, fast jumps 2 spots per slow's 1
    74:     }
    75:   }
    76: 
    77:   if (fast == slow) {
    78:     xfailure("linked list has a cycle");
    79:   }
    80:   else {
    81:     return;         // no loop
    82:   }
    83: }
    84: 
    85: 
    86: void VoidList::checkHeapDataPtrs() const
    87: {
    88:   for (VoidNode *p=top; p!=NULL; p=p->next) {
    89:     checkHeapNode(p->data);
    90:   }
    91: }
    92: 
    93: 
    94: void VoidList::checkUniqueDataPtrs() const
    95: {
    96:   for (VoidNode *p=top; p!=NULL; p=p->next) {
    97:     // run 'q' from 'top' to 'p', looking for any
    98:     // collisions with 'p->data'
    99:     for (VoidNode *q=top; q!=p; q=q->next) {
   100:       if (q->data == p->data) {
   101:         xfailure("linked list with duplicate element");
   102:       }
   103:     }
   104:   }
   105: }
   106: 
   107: 
   108: // insert at front
   109: void VoidList::prepend(void *newitem)
   110: {
   111:   top = new VoidNode(newitem, top);
   112: }
   113: 
   114: 
   115: // insert at rear
   116: void VoidList::append(void *newitem)
   117: {
   118:   if (!top) {
   119:     prepend(newitem);
   120:   }
   121:   else {
   122:     VoidNode *p;
   123:     for (p = top; p->next; p = p->next)
   124:       {}
   125:     p->next = new VoidNode(newitem);
   126:   }
   127: }
   128: 
   129: 
   130: // insert at particular point, index of new node becomes 'index'
   131: void VoidList::insertAt(void *newitem, int index)
   132: {
   133:   if (index == 0 || isEmpty()) {
   134:     // special case prepending or an empty list
   135:     xassert(index == 0);     // if it's empty, index should be 0
   136:     prepend(newitem);
   137:   }
   138: 
   139:   else {
   140:     // Looking at the loop below, the key things to note are:
   141:     //  1. If index started as 1, the loop isn't executed, and the new
   142:     //     node goes directly after the top node.
   143:     //  2. p is never allowed to become NULL, so we can't walk off the
   144:     //     end of the list.
   145: 
   146:     index--;
   147:     VoidNode *p;
   148:     for (p = top; p->next && index; p = p->next) {
   149:       index--;
   150:     }
   151:     xassert(index == 0);
   152:       // if index isn't 0, then index was greater than count()
   153: 
   154:     // put a node after p
   155:     VoidNode *n = new VoidNode(newitem);
   156:     n->next = p->next;
   157:     p->next = n;
   158:   }
   159: }
   160: 
   161: 
   162: void VoidList::insertSorted(void *newitem, VoidDiff diff, void *extra)
   163: {
   164:   // put it first?
   165:   if (!top ||
   166:       diff(newitem, top->data, extra) <= 0) {                // newitem <= top
   167:     prepend(newitem);
   168:     return;
   169:   }
   170: 
   171:   // we will be considering adding 'newitem' *after* cursor, as we go
   172:   VoidNode *cursor = top;
   173:   while (cursor->next != NULL &&
   174:          diff(cursor->next->data, newitem, extra) < 0) {     // cursor->next < newitem
   175:     cursor = cursor->next;
   176:   }
   177: 
   178:   // insert 'newitem' after 'cursor'
   179:   VoidNode *newNode = new VoidNode(newitem);
   180:   newNode->next = cursor->next;
   181:   cursor->next = newNode;
   182: }
   183: 
   184: 
   185: // ----------------- list-as-set stuff -------------------
   186: // get the index of an item's first occurrance
   187: int VoidList::indexOf(void *item) const
   188: {
   189:   int index = 0;
   190:   for (VoidNode *p = top; p != NULL; p = p->next, index++) {
   191:     if (p->data == item) {
   192:       return index;
   193:     }
   194:   }
   195:   return -1;
   196: }
   197: 
   198: 
   199: int VoidList::indexOfF(void *item) const
   200: {
   201:   int ret = indexOf(item);
   202:   xassert(ret >= 0);
   203:   return ret;
   204: }
   205: 
   206: 
   207: bool VoidList::prependUnique(void *newitem)
   208: {
   209:   if (!contains(newitem)) {
   210:     prepend(newitem);
   211:     return true;
   212:   }
   213:   else {
   214:     return false;   // no change
   215:   }
   216: }
   217: 
   218: 
   219: bool VoidList::appendUnique(void *newitem)
   220: {
   221:   if (!top) {
   222:     prepend(newitem);
   223:     return true;
   224:   }
   225: 
   226:   // walk to the end of list, while checking to
   227:   // see if 'newitem' is already in the list
   228:   VoidNode *p;
   229:   for (p = top; p->next; p = p->next) {
   230:     if (p->data == newitem) {
   231:       return false;      // item is already on list; no change
   232:     }
   233:   }
   234:   if (p->data == newitem) {
   235:     return false;
   236:   }
   237: 
   238:   p->next = new VoidNode(newitem);
   239:   return true;
   240: }
   241: 
   242: 
   243: bool VoidList::removeIfPresent(void *item)
   244: {
   245:   // for now, not a real fast implementation
   246:   int index = indexOf(item);
   247:   if (index == -1) {
   248:     return false;   // not there
   249:   }
   250:   else {
   251:     removeAt(index);
   252:     return true;
   253:   }
   254: }
   255: 
   256: 
   257: void VoidList::removeItem(void *item)
   258: {
   259:   bool wasThere = removeIfPresent(item);
   260:   xassert(wasThere);
   261: }
   262: 
   263: // ----------------- end of list-as-set stuff -------------------
   264: 
   265: 
   266: void *VoidList::removeAt(int index)
   267: {
   268:   if (index == 0) {
   269:     xassert(top != NULL);   // element must exist to remove
   270:     VoidNode *temp = top;
   271:     void *retval = temp->data;
   272:     top = top->next;
   273:     delete temp;
   274:     return retval;
   275:   }
   276: 
   277:   // will look for the node just before the one to delete
   278:   index--;
   279: 
   280:   VoidNode *p;
   281:   for (p = top; p->next && index>0;
   282:        p = p->next, index--)
   283:     {}
   284: 
   285:   if (p->next) {
   286:     // index==0, so p->next is node to remove
   287:     VoidNode *temp = p->next;
   288:     void *retval = temp->data;
   289:     p->next = p->next->next;
   290:     delete temp;
   291:     return retval;
   292:   }
   293:   else {
   294:     // p->next==NULL, so index was too large
   295:     xfailure("Tried to remove an element not on the list");
   296:     return NULL;    // silence warning
   297:   }
   298: }
   299: 
   300: 
   301: void VoidList::removeAll()
   302: {
   303:   while (top != NULL) {
   304:     VoidNode *temp = top;
   305:     top = top->next;
   306:     delete temp;
   307:   }
   308: }
   309: 
   310: 
   311: void VoidList::reverse()
   312: {
   313:   // transfer list to a temporary
   314:   VoidNode *oldlist = top;
   315:   top = NULL;
   316: 
   317:   // prepend all nodes
   318:   while (oldlist != NULL) {
   319:     // take first node from oldlist
   320:     VoidNode *node = oldlist;
   321:     oldlist = oldlist->next;
   322: 
   323:     // prepend it to new list
   324:     node->next = top;
   325:     top = node;
   326:   }
   327: }
   328: 
   329: 
   330: //   The difference function should return <0 if left should come before
   331: // right, 0 if they are equivalent, and >0 if right should come before
   332: // left. For example, if we are sorting numbers into ascending order,
   333: // then 'diff' would simply be subtraction.
   334: //   The 'extra' parameter passed to sort is passed to diff each time it
   335: // is called.
   336: //   O(n^2) time, O(1) space
   337: void VoidList::insertionSort(VoidDiff diff, void *extra)
   338: {
   339:   VoidNode *primary = top;                   // primary sorting pointer
   340:   while (primary && primary->next) {
   341:     if (diff(primary->data, primary->next->data, extra) > 0) {   // must move next node?
   342:       VoidNode *tomove = primary->next;
   343:       primary->next = primary->next->next;    // patch around moving node
   344: 
   345:       if (diff(tomove->data, top->data, extra) < 0) {           // new node goes at top?
   346:         tomove->next = top;
   347:         top = tomove;
   348:       }
   349: 
   350:       else {                                  // new node *not* at top
   351:         VoidNode *searcher = top;
   352:         while (diff(tomove->data, searcher->next->data, extra) > 0) {
   353:           searcher = searcher->next;
   354:         }
   355: 
   356:         tomove->next = searcher->next;        // insert new node into list
   357:         searcher->next = tomove;
   358:       }
   359:     }
   360: 
   361:     else {
   362:       primary = primary->next;              // go on if no changes
   363:     }
   364:   }
   365: }
   366: 
   367: 
   368: // O(n log n) time, O(log n) space
   369: void VoidList::mergeSort(VoidDiff diff, void *extra)
   370: {
   371:   if (top == NULL || top->next == NULL) {
   372:     return;   // base case: 0 or 1 elements, already sorted
   373:   }
   374: 
   375:   // half-lists
   376:   VoidList leftHalf;
   377:   VoidList rightHalf;
   378: 
   379:   // divide the list
   380:   {
   381:     // to find the halfway point, we use the slow/fast
   382:     // technique; to get the right node for short lists
   383:     // (like 2-4 nodes), we start fast off one ahead
   384: 
   385:     VoidNode *slow = top;
   386:     VoidNode *fast = top->next;
   387: 
   388:     while (fast && fast->next) {
   389:       slow = slow->next;
   390:       fast = fast->next;
   391:       fast = fast->next;
   392:     }
   393: 
   394:     // at this point, 'slow' points to the node
   395:     // we want to be the last of the 'left' half;
   396:     // the left half will either be the same length
   397:     // as the right half, or one node longer
   398: 
   399:     // division itself
   400:     rightHalf.top = slow->next;  // top half to right
   401:     leftHalf.top = this->top;    // bottom half to left
   402:     slow->next = NULL;           // cut the link between the halves
   403:   }
   404: 
   405:   // recursively sort the halves
   406:   leftHalf.mergeSort(diff, extra);
   407:   rightHalf.mergeSort(diff, extra);
   408: 
   409:   // merge the halves into a single, sorted list
   410:   VoidNode *merged = NULL;       // tail of merged list
   411:   while (leftHalf.top != NULL &&
   412:          rightHalf.top != NULL) {
   413:     // see which first element to select, and remove it
   414:     VoidNode *selected;
   415:     if (diff(leftHalf.top->data, rightHalf.top->data, extra) < 0) {
   416:       selected = leftHalf.top;
   417:       leftHalf.top = leftHalf.top->next;
   418:     }
   419:     else {
   420:       selected = rightHalf.top;
   421:       rightHalf.top = rightHalf.top->next;
   422:     }
   423: 
   424:     // append it to the merged list
   425:     if (merged == NULL) {
   426:       // first time; special case
   427:       merged = this->top = selected;
   428:     }
   429:     else {
   430:       // 2nd and later; normal case
   431:       merged = merged->next = selected;
   432:     }
   433:   }
   434: 
   435:   // one of the halves is exhausted; concat the
   436:   // remaining elements of the other one
   437:   if (leftHalf.top != NULL) {
   438:     merged->next = leftHalf.top;
   439:     leftHalf.top = NULL;
   440:   }
   441:   else {
   442:     merged->next = rightHalf.top;
   443:     rightHalf.top = NULL;
   444:   }
   445: 
   446:   // verify both halves are now exhausted
   447:   xassert(leftHalf.top == NULL &&
   448:           rightHalf.top == NULL);
   449: 
   450:   // list is sorted
   451: }
   452: 
   453: 
   454: bool VoidList::isSorted(VoidDiff diff, void *extra) const
   455: {
   456:   if (isEmpty()) {
   457:     return true;
   458:   }
   459: 
   460:   void *prev = top->data;
   461:   VoidListIter iter(*this);
   462:   iter.adv();
   463:   for (; !iter.isDone(); iter.adv()) {
   464:     void *current = iter.data();
   465: 
   466:     if (diff(prev, current, extra) <= 0) {
   467:       // ok: prev <= current
   468:     }
   469:     else {
   470:       return false;
   471:     }
   472: 
   473:     prev = current;
   474:   }
   475: 
   476:   return true;
   477: }
   478: 
   479: 
   480: // attach tail's nodes to this; empty the tail
   481: void VoidList::concat(VoidList &tail)
   482: {
   483:   if (!top) {
   484:     top = tail.top;
   485:   }
   486:   else {
   487:     VoidNode *n = top;
   488:     for(; n->next; n = n->next)
   489:       {}
   490:     n->next = tail.top;
   491:   }
   492: 
   493:   tail.top = NULL;
   494: }
   495: 
   496: 
   497: // attach some of source's nodes to this, removing them from 'source'
   498: void VoidList::stealTailAt(int index, VoidList &source)
   499: {
   500:   if (index == 0) {
   501:     concat(source);
   502:     return;
   503:   }
   504: 
   505:   // find the node in 'source' just before the first one that
   506:   // will be transferred
   507:   VoidNode *beforeTransfer = source.top;
   508:   index--;
   509:   while (index--) {
   510:     beforeTransfer = beforeTransfer->next;
   511:   }
   512: 
   513:   // break off the tail
   514:   VoidNode *tailStart = beforeTransfer->next;
   515:   beforeTransfer->next = NULL;
   516: 
   517:   // transfer 'tailStart' and beyond to 'this'
   518:   if (!top) {
   519:     top = tailStart;
   520:   }
   521:   else {
   522:     // find the end of 'this' list
   523:     VoidNode *n = top;
   524:     for(; n->next; n = n->next)
   525:       {}
   526:     n->next = tailStart;
   527:   }
   528: }
   529: 
   530: 
   531: void VoidList::appendAll(VoidList const &tail)
   532: {
   533:   // make a dest iter and move it to the end
   534:   VoidListMutator destIter(*this);
   535:   while (!destIter.isDone()) {
   536:     destIter.adv();
   537:   }
   538: 
   539:   VoidListIter srcIter(tail);
   540:   for (; !srcIter.isDone(); srcIter.adv()) {
   541:     destIter.append(srcIter.data());
   542:   }
   543: }
   544: 
   545: 
   546: VoidList& VoidList::operator= (VoidList const &src)
   547: {
   548:   if (this != &src) {
   549:     removeAll();
   550:     appendAll(src);
   551:   }
   552:   return *this;
   553: }
   554: 
   555: 
   556: bool VoidList::equalAsLists(VoidList const &otherList, VoidDiff diff, void *extra) const
   557: {
   558:   return 0==compareAsLists(otherList, diff, extra);
   559: }
   560: 
   561: int VoidList::compareAsLists(VoidList const &otherList, VoidDiff diff, void *extra) const
   562: {
   563:   VoidListIter mine(*this);
   564:   VoidListIter his(otherList);
   565: 
   566:   while (!mine.isDone() && !his.isDone()) {
   567:     int cmp = diff(mine.data(), his.data(), extra);
   568:     if (cmp == 0) {
   569:       // they are equal; keep going
   570:     }
   571:     else {
   572:       // unequal, return which way comparison went
   573:       return cmp;
   574:     }
   575: 
   576:     mine.adv();
   577:     his.adv();
   578:   }
   579: 
   580:   if (!mine.isDone() || !his.isDone()) {
   581:     // unequal lengths: shorter compares as less
   582:     return mine.isDone()? -1 : +1;
   583:   }
   584: 
   585:   return 0;        // everything matches
   586: }
   587: 
   588: 
   589: bool VoidList::equalAsSets(VoidList const &otherList, VoidDiff diff, void *extra) const
   590: {
   591:   return this->isSubsetOf(otherList, diff, extra) &&
   592:          otherList.isSubsetOf(*this, diff, extra);
   593: }
   594: 
   595: 
   596: bool VoidList::isSubsetOf(VoidList const &otherList, VoidDiff diff, void *extra) const
   597: {
   598:   for (VoidListIter iter(*this); !iter.isDone(); iter.adv()) {
   599:     if (!otherList.containsByDiff(iter.data(), diff, extra)) {
   600:       return false;
   601:     }
   602:   }
   603:   return true;
   604: }
   605: 
   606: 
   607: bool VoidList::containsByDiff(void *item, VoidDiff diff, void *extra) const
   608: {
   609:   for (VoidListIter iter(*this); !iter.isDone(); iter.adv()) {
   610:     if (0==diff(item, iter.data(), extra)) {
   611:       return true;
   612:     }
   613:   }
   614:   return false;
   615: }
   616: 
   617: 
   618: STATICDEF int VoidList::pointerAddressDiff(void *left, void *right, void*)
   619: {
   620:   // originally, left - right which is +ve if right < left
   621:   return
   622:     std::less<void*>()(right, left) ? 1 :
   623:     ((left == right) ? 0 : -1);
   624: }
   625: 
   626: 
   627: void VoidList::debugPrint() const
   628: {
   629:   printf("{ ");
   630:   for (VoidListIter iter(*this); !iter.isDone(); iter.adv()) {
   631:     printf("%p ", iter.data());
   632:   }
   633:   printf("}");
   634: }
   635: 
   636: 
   637: // --------------- VoidListMutator ------------------
   638: VoidListMutator&
   639:   VoidListMutator::operator=(VoidListMutator const &obj)
   640: {
   641:   // we require that the underlying lists be the same
   642:   // because we can't reset the 'list' reference if they
   643:   // aren't
   644:   xassert(&list == &obj.list);
   645: 
   646:   prev = obj.prev;
   647:   current = obj.current;
   648: 
   649:   return *this;
   650: }
   651: 
   652: 
   653: void VoidListMutator::insertBefore(void *item)
   654: {
   655:   if (prev == NULL) {
   656:     // insert at start of list
   657:     list.prepend(item);
   658:     reset();
   659:   }
   660:   else {
   661:     current = prev->next = new VoidNode(item, current);
   662:   }
   663: }
   664: 
   665: 
   666: void VoidListMutator::insertAfter(void *item)
   667: {
   668:   xassert(!isDone());
   669:   current->next = new VoidNode(item, current->next);
   670: }
   671: 
   672: 
   673: void VoidListMutator::append(void *item)
   674: {
   675:   xassert(isDone());
   676:   insertBefore(item);
   677:   adv();
   678: }
   679: 
   680: 
   681: void *VoidListMutator::remove()
   682: {
   683:   xassert(!isDone());
   684:   void *retval = data();
   685:   if (prev == NULL) {
   686:     // removing first node
   687:     list.top = current->next;
   688:     delete current;
   689:     current = list.top;
   690:   }
   691:   else {
   692:     current = current->next;
   693:     delete prev->next;   // old 'current'
   694:     prev->next = current;
   695:   }
   696:   return retval;
   697: }
   698: 
   699: 
   700: // --------------- VoidListIter --------------------
   701: VoidListIter::VoidListIter(VoidList const &list, int pos)
   702: {
   703:   reset(list);
   704:   while (pos--) {
   705:     adv();
   706:   }
   707: }
   708: 
   709: 
   710: // -------------- testing --------------
   711: #ifdef TEST_VOIDLIST
   712: #include "sm_test.h"
   713: 
   714: // assumes we're using pointerAddressDiff as the comparison fn
   715: // (I don't use isSorted because this fn will throw at the disequality,
   716: // whereas isSorted would forget that info)
   717: void verifySorted(VoidList const &list)
   718: {
   719:   int prev = 0;
   720:   VoidListIter iter(list);
   721:   for (; !iter.isDone(); iter.adv()) {
   722:     int current = (int)iter.data();
   723:     xassert(prev <= current);    // numeric address test
   724:     prev = current;
   725:   }
   726: }
   727: 
   728: 
   729: #define PRINT(lst) printf("%s: ", #lst); lst.debugPrint(); printf("\n") /* user ; */
   730: 
   731: void testSorting()
   732: {
   733:   enum { ITERS=100, ITEMS=20 };
   734: 
   735:   loopi(ITERS) {
   736:     // construct a list (and do it again if it ends up already sorted)
   737:     VoidList list1;
   738:     VoidList list3;     // this one will be constructed sorted one at a time
   739:     int numItems;
   740:     do {
   741:       list1.removeAll();    // clear them in case we have to build it more than once
   742:       list3.removeAll();
   743:       numItems = rand()%ITEMS;
   744:       loopj(numItems) {
   745:         void *toInsert = (void*)( (rand()%ITEMS) * 4 );
   746:         list1.prepend(toInsert);
   747:         list3.insertSorted(toInsert, VoidList::pointerAddressDiff);
   748:       }
   749:     } while (list1.isSorted(VoidList::pointerAddressDiff));
   750: 
   751:     // list3 should be sorted already
   752:     //PRINT(list3);
   753:     verifySorted(list3);
   754: 
   755:     // duplicate it for use with other algorithm
   756:     VoidList list2;
   757:     list2 = list1;
   758: 
   759:     // sort them
   760:     list1.insertionSort(VoidList::pointerAddressDiff);
   761:     xassert(list1.equalAsPointerSets(list2));      // verify intermediate equality
   762:     xassert(!list1.equalAsPointerLists(list2));    // but not elementwise
   763:     list2.mergeSort(VoidList::pointerAddressDiff);
   764:     //PRINT(list1);
   765: 
   766:     // verify structure
   767:     list1.selfCheck();
   768:     list2.selfCheck();
   769: 
   770:     // verify length
   771:     xassert(list1.count() == numItems && list2.count() == numItems);
   772: 
   773:     // verify sortedness
   774:     verifySorted(list1);
   775:     verifySorted(list2);
   776: 
   777:     // verify equality
   778:     xassert(list1.equalAsPointerLists(list2));
   779:     xassert(list1.equalAsPointerLists(list3));
   780: 
   781:     // to test as-sets equality
   782:     void *first = list1.first();
   783:     while (list1.removeIfPresent(first))
   784:       {}     // remove all occurrances of 'first'
   785:     xassert(!list1.equalAsPointerSets(list2));
   786:   }
   787: }
   788: 
   789: 
   790: void entry()
   791: {
   792:   // first set of tests
   793:   {
   794:     // some sample items
   795:     void *a=(void*)4, *b=(void*)8, *c=(void*)12, *d=(void*)16;
   796: 
   797:     VoidList list;
   798: 
   799:     // test simple modifiers and info
   800:     list.append(c);     PRINT(list);   // c
   801:     list.prepend(b);    PRINT(list);   // b c
   802:     list.append(d);     PRINT(list);   // b c d
   803:     list.prepend(a);    PRINT(list);   // a b c d
   804:     list.removeAt(2);   PRINT(list);   // a b d
   805: 
   806:     xassert( list.count() == 3 &&
   807:              !list.isEmpty() &&
   808:              list.nth(0) == a &&
   809:              list.nth(1) == b &&
   810:              list.nth(2) == d &&
   811:              list.indexOf(a) == 0 &&
   812:              list.indexOf(b) == 1 &&
   813:              list.indexOf(c) == -1 &&
   814:              list.indexOf(d) == 2
   815:            );
   816:     list.selfCheck();
   817: 
   818:     // test mutator s
   819:     {
   820:       VoidListMutator mut(list);
   821:       mut.adv();
   822:         // now it's pointing at b
   823:       mut.insertAfter(c);
   824:         // now list is (a b c d) and mut points at b still
   825:       verifySorted(list);
   826:       mut.remove();
   827:         // now list is (a c d) and mut points at c
   828:       xassert(mut.data() == c);
   829: 
   830:       // copy the mutator
   831:       VoidListMutator mut2(mut);
   832:       mut2.adv();
   833:       xassert(mut.data() == c  &&  mut2.data() == d);
   834: 
   835:       // copy to a normal iterator
   836:       VoidListIter iter(mut);
   837:       iter.adv();
   838:       xassert(iter.data() == d);
   839:       iter.adv();
   840:       xassert(iter.isDone()  &&  mut.data() == c);
   841: 
   842:       PRINT(list);
   843:     }
   844: 
   845:     // test appendUnique and prependUnique
   846:     // list starts as (a c d)
   847:     xassert(list.appendUnique(c) == false &&
   848:             list.prependUnique(d) == false &&
   849:             list.prependUnique(b) == true);
   850:       // now it is (b a c d)
   851:     list.removeItem(a);
   852:     xassert(list.removeIfPresent(a) == false);
   853:       // now it is (b c d)
   854:     verifySorted(list);
   855:     PRINT(list);
   856: 
   857:     // test reverse
   858:     list.reverse();
   859:       // list is now (d c b)
   860:     xassert(list.indexOf(d) == 0 &&
   861:             list.indexOf(c) == 1 &&
   862:             list.indexOf(b) == 2);
   863:     PRINT(list);
   864: 
   865:     // test stealTailAt
   866:     VoidList thief;
   867:     thief.stealTailAt(1, list);
   868:       // list is now (d)
   869:       // thief is now (c b)
   870:     xassert(list.count() == 1 &&
   871:             list.indexOf(d) == 0 &&
   872:             thief.count() == 2 &&
   873:             thief.indexOf(c) == 0 &&
   874:             thief.indexOf(b) == 1);
   875:   }
   876: 
   877:   // this hits most of the remaining code
   878:   // (a decent code coverage tool for C++ would be nice!)
   879:   testSorting();
   880: 
   881:   printf("voidlist ok\n");
   882: }
   883: 
   884: USUAL_MAIN
   885: 
   886: #endif // NDEBUG
   887: 
End cpp section to elk/sm_voidlist.cpp[1]
Start cpp section to elk/sm_vptrmap.cpp[1 /1 ]
     1: #line 15150 "./lpsrc/sm.pak"
     2: // vptrmap.cc
     3: // code for vptrmap.h
     4: 
     5: #include "sm_vptrmap.h"
     6: #include "sm_xassert.h"
     7: 
     8: #include <stddef.h>      // NULL
     9: #include <cstring>      // std::memset
    10: 
    11: 
    12: // ------------------ VoidPtrMap -------------------
    13: int VoidPtrMap::lookups = 0;
    14: int VoidPtrMap::probes = 0;
    15: 
    16: 
    17: VoidPtrMap::VoidPtrMap()
    18:   : hashTable(NULL),
    19:     tableSize(0),
    20:     tableSizeBits(0),
    21:     numEntries(0),
    22:     iterators(0)
    23: {
    24:   alloc(4);    // 16 entries initially
    25:   empty();
    26: }
    27: 
    28: VoidPtrMap::~VoidPtrMap()
    29: {
    30:   delete[] hashTable;
    31: }
    32: 
    33: 
    34: void VoidPtrMap::alloc(int bits)
    35: {
    36:   tableSizeBits = bits;
    37:   tableSize = 1 << bits;
    38:   hashTable = new Entry[tableSize];
    39: }
    40: 
    41: 
    42: inline unsigned VoidPtrMap::hashFunc(unsigned multiplier, unsigned key) const
    43: {
    44:   // see Cormen/Leiserson/Rivest (CLR), section 12.3.2
    45: 
    46:   // multiply, throwing away the overflow high bits
    47:   unsigned ret = key * multiplier;
    48: 
    49:   // we want to extract the 'tableSizeBits' most sigificant bits
    50:   ret = ret >> ((sizeof(unsigned)*8) - tableSizeBits);
    51:   ret = ret & (tableSize-1);
    52: 
    53:   return ret;
    54: }
    55: 
    56: 
    57: VoidPtrMap::Entry &VoidPtrMap::findEntry(void const *key) const
    58: {
    59:   xassert(key != NULL);
    60:   lookups++;
    61: 
    62:   // constants used in the hash functions
    63:   enum {
    64:     // value is  floor(  (sqrt(5)-1)/2 * 2^32  )
    65:     //
    66:     // This is the golden ratio.  CLR says Knuth says it's good.
    67:     CONST1 = 0x9E3779B9U,
    68: 
    69:     // value is  floor(  (sqrt(3)-1)/2 * 2^32  )
    70:     //
    71:     // Some random website claims irrational constants are good,
    72:     // and I can't find any source (I don't have Knuth..) for
    73:     // another constant, so I just decided to substitute 3 for
    74:     // 5 in the golden ratio formula.  Since I trust this one
    75:     // less, I use it for the less important role (stride).
    76:     CONST2 = 0x5DB3D742U
    77:   };
    78: 
    79:   // compute first hash function, which gives the starting index
    80:   // for the probe sequence
    81:   unsigned index = hashFunc(CONST1, (SM_RAWADDRESS)key);
    82: 
    83:   // analyze the first entry now, before computing the second
    84:   // hash function (stride) value
    85:   {
    86:     probes++;
    87:     Entry &e = hashTable[index];
    88:     if (e.key == NULL ||
    89:         e.key == key) {
    90:       return e;
    91:     }
    92:   }
    93: 
    94:   // compute stride; it has to be odd so that it is relatively
    95:   // prime to the table size (which is a power of 2), so I just
    96:   // turn on the least significant bit
    97:   unsigned stride = hashFunc(CONST2, (SM_RAWADDRESS)key) | 1;
    98: 
    99:   // uncomment this to experiment with linear hashing; when ITERS2MAX
   100:   // is 10000, I see a small increase in avgprobes when using linear
   101:   // hashing over double hashing
   102:   //unsigned stride = 1;
   103: 
   104:   // collision; stride over the entries
   105:   for (int i=0; i<tableSize; i++) {
   106:     index = (index + stride) & (tableSize-1);
   107: 
   108:     probes++;
   109:     Entry &e = hashTable[index];
   110:     if (e.key == NULL ||
   111:         e.key == key) {
   112:       return e;
   113:     }
   114:   }
   115: 
   116:   // searched all entries with no success; but if this happens,
   117:   // then our load factor must be 1, which violates the invariant
   118:   // that numEntries < tableSize
   119:   xfailure("findEntry traversed all entries");
   120:   return *((Entry*)NULL);     // silence warning
   121: }
   122: 
   123: 
   124: void VoidPtrMap::add(void *key, void *value)
   125: {
   126:   xassert(iterators == 0);
   127: 
   128:   // if load factor would exceed 3/4, expand
   129:   if (numEntries+1 > (tableSize/2 + tableSize/4)) {
   130:     expand();
   131:   }
   132: 
   133:   Entry &e = findEntry(key);
   134:   if (e.key == NULL) {
   135:     e.key = key;              // new mapping
   136:     numEntries++;
   137:   }
   138:   else {
   139:     xassert(e.key == key);    // update existing mapping
   140:   }
   141:   e.value = value;
   142: }
   143: 
   144: 
   145: void VoidPtrMap::expand()
   146: {
   147:   Entry *oldHashTable = hashTable;
   148:   int oldTableSize = tableSize;
   149: 
   150:   alloc(tableSizeBits + 1);
   151:   empty();
   152: 
   153:   // re-insert all of the old elements
   154:   for (int i=0; i < oldTableSize; i++) {
   155:     Entry &e = oldHashTable[i];
   156:     if (e.key) {
   157:       add(e.key, e.value);
   158:     }
   159:   }
   160: 
   161:   delete[] oldHashTable;
   162: }
   163: 
   164: 
   165: void VoidPtrMap::empty()
   166: {
   167:   xassert(iterators == 0);
   168: 
   169:   // establishes invariant that NULL keys have NULL values
   170:   std::memset(hashTable, 0, sizeof(*hashTable) * tableSize);
   171:   numEntries = 0;
   172: }
   173: 
   174: 
   175: // ------------------- VoidPtrMap::Iter ------------------
   176: VoidPtrMap::Iter::Iter(VoidPtrMap const &m)
   177:   : map(m),
   178:     index(map.tableSize)
   179: {
   180:   map.iterators++;
   181:   adv();
   182: }
   183: 
   184: VoidPtrMap::Iter::~Iter()
   185: {
   186:   map.iterators--;
   187: }
   188: 
   189: 
   190: void VoidPtrMap::Iter::adv()
   191: {
   192:   xassert(index >= 0);
   193:   index--;
   194:   while (index >= 0 &&
   195:          map.hashTable[index].key == NULL) {
   196:     index--;
   197:   }
   198: }
   199: 
   200: 
   201: // ------------------- test code ---------------------
   202: #ifdef TEST_VPTRMAP
   203: 
   204: #include "sm_test.h"
   205: #include "sm_array.h"
   206: #include "sm_ckheap.h"
   207: #include "sm_ptrmap.h"
   208: 
   209: #include <stdlib.h>    // rand, qsort
   210: #include <stdio.h>     // printf
   211: 
   212: 
   213: class Node {
   214: public:
   215:   int *value;
   216:   bool found;
   217: 
   218: public:
   219:   Node() {
   220:     value = new int(0);
   221:     found = false;
   222:   }
   223:   ~Node() {
   224:     delete value;
   225:   }
   226: };
   227: 
   228: 
   229: int doubleCompar(void const *dp1, void const *dp2)
   230: {
   231:   double d1 = *((double*)dp1);
   232:   double d2 = *((double*)dp2);
   233:   if (d1 < d2) return -1;
   234:   if (d1 > d2) return +1;
   235:   return 0;    // almost never happens
   236: }
   237: 
   238: 
   239: void test1()
   240: {
   241:   printf("test1: testing PtrMap\n");
   242: 
   243:   enum { ITERS1=10, ITERS2MAX=2000 };
   244: 
   245:   double avgprobes[ITERS1];
   246: 
   247:   printf("  iter  iters  entries  lookups  probes  avgprobes\n");
   248:   printf("  ----  -----  -------  -------  ------  ---------\n");
   249: 
   250:   for (int i=0; i < ITERS1; i++) {
   251:     // I actually test PtrMap, the type-safe wrapper on top
   252:     // of VoidPtrMap, so I can test that class too; the casts
   253:     // that I used to need for VoidPtrMap are now protected by
   254:     // this CAST macro
   255:     //#define CAST(something) (something)
   256:     #define CAST(something) /*nothing*/
   257: 
   258:     PtrMap<Node,int> map;
   259:     ObjArrayStack<Node> stack;
   260: 
   261:     int iters2 = rand() % ITERS2MAX;
   262:     for (int j=0; j < iters2; j++) {
   263:       int op = rand() % 100;
   264: 
   265:       if (op <= 40) {         // insert
   266:         Node *n = new Node;
   267:         stack.push(n);
   268:         map.add(n, n->value);
   269:       }
   270: 
   271:       else if (op <= 80) {    // find exist
   272:         if (stack.isNotEmpty()) {
   273:           Node *n = stack[rand() % stack.length()];
   274:           int *v = CAST(int*)map.get(n);
   275:           xassert(v && v == n->value);
   276: 
   277:           if (rand() % 10 == 0) {
   278:             // reassign
   279:             delete n->value;
   280:             n->value = new int(0);
   281:             map.add(n, n->value);
   282:           }
   283:         }
   284:       }
   285: 
   286:       else if (op <= 90) {    // find non-exist
   287:         Node *n = new Node;
   288:         int *v = CAST(int*)map.get(n);
   289:         xassert(!v);
   290:         delete n;
   291:       }
   292: 
   293:       else if (op <= 100) {   // traverse
   294:         // clear all 'found'
   295:         int k;
   296:         for (k=0; k < stack.length(); k++) {
   297:           stack[k]->found = false;
   298:         }
   299: 
   300:         // walk via map; should find each one exactly once
   301:         int numFound = 0;
   302:         //VoidPtrMap::Iter iter(map);
   303:         PtrMap<Node,int>::Iter iter(map);
   304:         for (; !iter.isDone(); iter.adv()) {
   305:           Node *n = CAST(Node*)iter.key();
   306:           int *v = CAST(int*)iter.value();
   307: 
   308:           xassert(v == n->value);
   309:           xassert(n->found == false);
   310:           n->found = true;
   311:           numFound++;
   312:         }
   313: 
   314:         // check all 'found' (launch all 'zig')
   315:         for (k=0; k < stack.length(); k++) {
   316:           xassert(stack[k]->found == true);
   317:         }
   318:         xassert(numFound == stack.length());
   319:       }
   320:     }
   321: 
   322:     xassert(map.getNumEntries() == stack.length());
   323:     //     "  iter  iters  entries  lookups  probes  avgprobes"
   324:     avgprobes[i] = ((double)VoidPtrMap::probes) / ((double)VoidPtrMap::lookups);
   325:     printf("  %4d  %5d  %7d  %7d  %6d    %g\n",
   326:            i,
   327:            iters2,
   328:            map.getNumEntries(),
   329:            VoidPtrMap::lookups,
   330:            VoidPtrMap::probes,
   331:            avgprobes[i]);
   332: 
   333:     VoidPtrMap::probes = 0;
   334:     VoidPtrMap::lookups = 0;
   335:   }
   336: 
   337:   // compute median of avgprobes
   338:   qsort(avgprobes, ITERS1, sizeof(avgprobes[0]), doubleCompar);
   339:   printf("median avgprobe: %g\n", avgprobes[ITERS1/2]);
   340: 
   341:   //malloc_stats();
   342: }
   343: 
   344: 
   345: struct A {
   346:   int x;
   347:   A(int x0) : x(x0) {}
   348: };
   349: 
   350: void test2()
   351: {
   352:   printf("test2: testing PtrSet\n");
   353: 
   354:   PtrSet<A> s;
   355:   xassert(s.isEmpty());
   356:   xassert(s.getNumEntries() == 0);
   357: 
   358:   A *a1 = new A(1);
   359:   s.add(a1);
   360:   xassert(s.isNotEmpty());
   361:   xassert(s.getNumEntries() == 1);
   362: 
   363:   A *a2 = new A(2);
   364:   s.add(a2);
   365:   xassert(s.isNotEmpty());
   366:   xassert(s.getNumEntries() == 2);
   367: 
   368:   xassert(s.contains(a1));
   369:   xassert(s.contains(a2));
   370: 
   371:   s.empty();                    // make empty
   372: 
   373:   xassert(!s.contains(a1));
   374:   xassert(!s.contains(a2));
   375:   xassert(s.isEmpty());
   376:   xassert(s.getNumEntries() == 0);
   377: 
   378:   A *a3 = new A(3);
   379:   s.add(a3);
   380:   xassert(s.isNotEmpty());
   381:   xassert(s.getNumEntries() == 1);
   382: }
   383: 
   384: 
   385: void entry()
   386: {
   387:   printf("testing vptrmap\n");
   388:   test1();
   389:   test2();
   390:   printf("vptrmap is ok\n");
   391: }
   392: 
   393: 
   394: USUAL_MAIN
   395: 
   396: #endif // TEST_VPTRMAP
End cpp section to elk/sm_vptrmap.cpp[1]
Start cpp section to elk/sm_breaker.cpp[1 /1 ]
     1: #line 15547 "./lpsrc/sm.pak"
     2: // breaker.cc            see license.txt for copyright and terms of use
     3: // code for breaker.h
     4: // Scott McPeak, 1997,1998  This file is public domain.
     5: 
     6: #include "sm_breaker.h"
     7: 
     8: #ifdef __BORLANDC__
     9: # pragma warn -use
    10: #endif
    11: 
    12: void ackackack(int*) {}
    13: 
    14: void breaker()
    15: {
    16:   static int i=0;
    17:   int a=1;               // all this junk is just to make sure
    18:                          // that this function has a complete
    19:   ackackack(&a);         // stack frame, so the debugger can unwind
    20:   i++;                   // the stack
    21: }
    22: 
    23: #ifdef __BORLANDC__
    24: #  pragma warn .use
    25: #endif
    26: 
    27: // (tweak for CVS)
End cpp section to elk/sm_breaker.cpp[1]
Start cpp section to elk/sm_crc.cpp[1 /1 ]
     1: #line 15575 "./lpsrc/sm.pak"
     2: // crc.cc            see license.txt for copyright and terms of use
     3: // adapted slightly by Scott McPeak
     4: 
     5: // originally was:
     6: /* crc32h.c -- package to compute 32-bit CRC one byte at a time using   */
     7: /*             the high-bit first (Big-Endian) bit ordering convention  */
     8: /*                                                                      */
     9: /* Synopsis:                                                            */
    10: /*  gen_crc_table() -- generates a 256-word table containing all CRC    */
    11: /*                     remainders for every possible 8-bit byte.  It    */
    12: /*                     must be executed (once) before any CRC updates.  */
    13: /*                                                                      */
    14: /*  unsigned update_crc(crc_accum, data_blk_ptr, data_blk_size)         */
    15: /*           unsigned crc_accum; char *data_blk_ptr; int data_blk_size; */
    16: /*           Returns the updated value of the CRC accumulator after     */
    17: /*           processing each byte in the addressed block of data.       */
    18: /*                                                                      */
    19: /*  It is assumed that an unsigned long is at least 32 bits wide and    */
    20: /*  that the predefined type char occupies one 8-bit byte of storage.   */
    21: /*                                                                      */
    22: /*  The generator polynomial used for this version of the package is    */
    23: /*  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 */
    24: /*  as specified in the Autodin/Ethernet/ADCCP protocol standards.      */
    25: /*  Other degree 32 polynomials may be substituted by re-defining the   */
    26: /*  symbol POLYNOMIAL below.  Lower degree polynomials must first be    */
    27: /*  multiplied by an appropriate power of x.  The representation used   */
    28: /*  is that the coefficient of x^0 is stored in the LSB of the 32-bit   */
    29: /*  word and the coefficient of x^31 is stored in the most significant  */
    30: /*  bit.  The CRC is to be appended to the data most significant byte   */
    31: /*  first.  For those protocols in which bytes are transmitted MSB      */
    32: /*  first and in the same order as they are encountered in the block    */
    33: /*  this convention results in the CRC remainder being transmitted with */
    34: /*  the coefficient of x^31 first and with that of x^0 last (just as    */
    35: /*  would be done by a hardware shift register mechanization).          */
    36: /*                                                                      */
    37: /*  The table lookup technique was adapted from the algorithm described */
    38: /*  by Avram Perez, Byte-wise CRC Calculations, IEEE Micro 3, 40 (1983).*/
    39: 
    40: #define POLYNOMIAL 0x04c11db7L
    41: 
    42: static unsigned long crc_table[256];
    43: 
    44: void gen_crc_table()
    45:  /* generate the table of CRC remainders for all possible bytes */
    46:  { register int i, j;  register unsigned long crc_accum;
    47:    for ( i = 0;  i < 256;  i++ )
    48:        { crc_accum = ( (unsigned long) i << 24 );
    49:          for ( j = 0;  j < 8;  j++ )
    50:               { if ( crc_accum & 0x80000000L )
    51:                    crc_accum =
    52:                      ( crc_accum << 1 ) ^ POLYNOMIAL;
    53:                 else
    54:                    crc_accum =
    55:                      ( crc_accum << 1 ); }
    56:          crc_table[i] = crc_accum; }
    57:    return; }
    58: 
    59: unsigned long update_crc(unsigned long crc_accum, char const *data_blk_ptr,
    60:                                                     int data_blk_size)
    61:  /* update the CRC on the data block one byte at a time */
    62:  { register int i, j;
    63:    for ( j = 0;  j < data_blk_size;  j++ )
    64:        { i = ( (int) ( crc_accum >> 24) ^ *data_blk_ptr++ ) & 0xff;
    65:          crc_accum = ( crc_accum << 8 ) ^ crc_table[i]; }
    66:    return crc_accum; }
    67: 
    68: 
    69: // SM: block-level application
    70: static int made_table = 0;
    71: unsigned long crc32(unsigned char const *data, int length)
    72: {
    73:   if (!made_table) {
    74:     gen_crc_table();
    75:     made_table = 1;
    76:   }
    77: 
    78:   return update_crc(0xFFFFFFFF, (char*)data, length);
    79: }
    80: 
    81: 
    82: // ----------------- test code ------------------------------
    83: #ifdef TEST_CRC
    84: 
    85: #include <stdio.h>     // printf, FILE, etc.
    86: #include <stdlib.h>    // malloc
    87: 
    88: 
    89: int errors=0;
    90: 
    91: void testCrc(char const *data, int length, unsigned long crc)
    92: {
    93:   unsigned long val = crc32((unsigned char*)data, length);
    94:   printf("computed crc is 0x%08lX, expected is 0x%08lX\n",
    95:          val, ~crc);       // why is 'crc' inverted?
    96:   if (val != ~crc) {
    97:     errors++;
    98:   }
    99: }
   100: 
   101: 
   102: int main(int argc, char *argv[])
   103: {
   104:   // if there's an argument, crc that
   105:   if (argc >= 2) {
   106:     FILE *fp = fopen(argv[1], "r");
   107:     if (!fp) {
   108:       printf("error opening %s: %m\n", argv[1]);
   109:       return 2;
   110:     }
   111: 
   112:     // get length
   113:     fseek(fp, 0, SEEK_END);
   114:     int len = ftell(fp);
   115:     fseek(fp, 0, SEEK_SET);
   116: 
   117:     // read the entire contents
   118:     unsigned char *buf = (unsigned char*)malloc(len);
   119:     if (fread(buf, 1, len, fp) != (size_t)len) {
   120:       printf("read error, or short count..\n");
   121:       return 2;
   122:     }
   123: 
   124:     // crc it
   125:     long val = crc32(buf, len);
   126:     printf("crc32: 0x%08lX\n", val);
   127: 
   128:     return 0;
   129:   }
   130: 
   131:   /* 40 Octets filled with "0" */
   132:   /* CPCS-UU = 0, CPI = 0, Length = 40, CRC-32 = 864d7f99 */
   133:   char pkt_data1[48]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   134:                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   135:                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   136:                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   137:                       0x00,0x00,0x00,0x28,0x86,0x4d,0x7f,0x99};
   138: 
   139:   /* 40 Octets filled with "1" */
   140:   /* CPCS-UU = 0, CPI = 0, Length = 40, CRC-32 = c55e457a */
   141:   char pkt_data2[48]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   142:                       0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   143:                       0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   144:                       0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   145:                       0x00,0x00,0x00,0x28,0xc5,0x5e,0x45,0x7a};
   146: 
   147:   /* 40 Octets counting: 1 to 40 */
   148:   /* CPCS-UU = 0, CPI = 0, Length = 40, CRC-32 = bf671ed0 */
   149:   char pkt_data3[48]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,
   150:                       0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,
   151:                       0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,
   152:                       0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
   153:                       0x00,0x00,0x00,0x28,0xbf,0x67,0x1e,0xd0};
   154: 
   155:   /* 40 Octets counting: 1 to 40 */
   156:   /* CPCS-UU = 11, CPI = 22, CRC-32 = acba602a */
   157:   char pkt_data4[48]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,
   158:                       0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,
   159:                       0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,
   160:                       0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
   161:                       0x11,0x22,0x00,0x28,0xac,0xba,0x60,0x2a};
   162: 
   163:   testCrc(pkt_data1, 44, 0x864d7f99);
   164:   testCrc(pkt_data2, 44, 0xc55e457a);
   165:   testCrc(pkt_data3, 44, 0xbf671ed0);
   166:   testCrc(pkt_data4, 44, 0xacba602a);
   167: 
   168:   return errors;
   169: }
   170: 
   171: #endif // TEST_CRC
   172: 
   173: 
   174: 
End cpp section to elk/sm_crc.cpp[1]
Start cpp section to elk/sm_datablok.cpp[1 /1 ]
     1: #line 15750 "./lpsrc/sm.pak"
     2: // datablok.cc            see license.txt for copyright and terms of use
     3: // code for datablok.h
     4: // Scott McPeak, 1998-2000  This file is public domain.
     5: 
     6: #include "sm_datablok.h"
     7: #include "sm_exc.h"
     8: #include "sm_crc.h"
     9: #include "sm_syserr.h"
    10: 
    11: #include <stdio.h>        // printf
    12: #include <cstring>       // std::memcpy
    13: #include <ctype.h>        // isprint
    14: 
    15: 
    16: // define the endpost byte as something we hope is
    17: // unlikely to coincidentally be written during an
    18: // overrun
    19: byte const DataBlock::endpost = 0xBB;
    20: 
    21: 
    22: void DataBlock::init(int allocatedSize)
    23: {
    24:   xassert(allocatedSize >= 0);
    25:   dataLen = 0;
    26:   allocated = allocatedSize;
    27:   if (allocated) {
    28:     data = allocate(allocated);
    29:   }
    30:   else {
    31:     data = NULL;
    32:   }
    33: 
    34:   SELFCHECK();
    35: }
    36: 
    37: 
    38: STATICDEF byte *DataBlock::allocate(int size)
    39: {
    40:   byte *ret = new byte[size+1];
    41:   ret[size] = endpost;
    42:   return ret;
    43: }
    44: 
    45: 
    46: void DataBlock::selfCheck() const
    47: {
    48:   if (!( 0 <= dataLen && dataLen <= allocated )) {
    49:     breaker();    // having trouble discovering the precise state under gdb
    50:   }
    51:   xassert(0 <= dataLen && dataLen <= allocated);
    52:   xassert( (data==NULL) == (allocated==0) );
    53:   xassert( data==NULL || data[allocated]==endpost );
    54: }
    55: 
    56: 
    57: DataBlock::DataBlock(int allocatedSize)
    58: {
    59:   init(allocatedSize);
    60:   SELFCHECK();
    61: }
    62: 
    63: 
    64: DataBlock::DataBlock(char const *srcString)
    65: {
    66:   init(0);
    67:   setFromString(srcString);
    68:   SELFCHECK();
    69: }
    70: 
    71: 
    72: void DataBlock::ctor(byte const *srcData, int dataLen)
    73: {
    74:   init(0);
    75:   setFromBlock(srcData, dataLen);
    76:   SELFCHECK();
    77: }
    78: 
    79: 
    80: void DataBlock::ctor(byte const *srcData, int srcDataLen, int allocatedSize)
    81: {
    82:   init(allocatedSize);
    83:   dataLen = srcDataLen;
    84:   std::memcpy(data, srcData, dataLen);
    85:   SELFCHECK();
    86: }
    87: 
    88: 
    89: DataBlock::DataBlock(DataBlock const &obj)
    90: {
    91:   init(obj.allocated);
    92:   copyCtorShared(obj);
    93: }
    94: 
    95: void DataBlock::copyCtorShared(DataBlock const &obj)
    96: {
    97:   dataLen = obj.dataLen;
    98:   if (dataLen > 0) {
    99:     std::memcpy(data, obj.data, dataLen);
   100:   }
   101:   SELFCHECK();
   102: }
   103: 
   104: 
   105: DataBlock::DataBlock(DataBlock const &obj, int minToAllocate)
   106: {
   107:   init(max(obj.getAllocated(), minToAllocate));
   108:   copyCtorShared(obj);
   109: }
   110: 
   111: 
   112: DataBlock::~DataBlock()
   113: {
   114:   try {
   115:     SELFCHECK();
   116:     if (data) {
   117:       delete[] data;
   118:     }
   119:   }
   120:   CAUTIOUS_RELAY
   121: }
   122: 
   123: 
   124: bool DataBlock::allEqual(DataBlock const &obj) const
   125: {
   126:   SELFCHECK();
   127:   return allocated == obj.allocated &&
   128:          dataEqual(obj);
   129: }
   130: 
   131: 
   132: bool DataBlock::dataEqual(DataBlock const &obj) const
   133: {
   134:   SELFCHECK();
   135:   if (dataLen != obj.dataLen ||
   136:       (dataLen > 0 &&
   137:        0 != std::memcmp(data, obj.data, dataLen))) {
   138:     return false;
   139:   }
   140:   else {
   141:     return true;
   142:   }
   143: }
   144: 
   145: 
   146: 
   147: void DataBlock::setDataLen(int newLen)
   148: {
   149:   SELFCHECK();
   150:   xassert(0 <= newLen && newLen <= allocated);
   151:   dataLen = newLen;
   152:   SELFCHECK();
   153: }
   154: 
   155: 
   156: void DataBlock::setAllocated(int newAllocated)
   157: {
   158:   SELFCHECK();
   159:   xassert(newAllocated >= 0);
   160:   if (allocated != newAllocated) {
   161:     // allocate new buffer
   162:     byte *newData = NULL;
   163:     if (newAllocated > 0) {
   164:       newData = allocate(newAllocated);
   165:     }
   166: 
   167:     // truncate defined data
   168:     if (dataLen > newAllocated) {
   169:       dataLen = newAllocated;
   170:     }
   171: 
   172:     // transfer data
   173:     if (dataLen > 0) {
   174:       std::memcpy(newData, data, dataLen);
   175:     }
   176: 
   177:     // deallocate old buffer and replace with new buffer
   178:     delete[] data;
   179:     data = newData;
   180:     allocated = newAllocated;
   181:   }
   182:   SELFCHECK();
   183: }
   184: 
   185: 
   186: void DataBlock::ensureAtLeast(int minAllocated)
   187: {
   188:   if (allocated < minAllocated) {
   189:     setAllocated(minAllocated);
   190:   }
   191: }
   192: 
   193: 
   194: void DataBlock::growDataLen(int changeAmount)
   195: {
   196:   ensureAtLeast(getDataLen() + changeAmount);
   197:   changeDataLen(changeAmount);
   198: }
   199: 
   200: 
   201: void DataBlock::addNull()
   202: {
   203:   SELFCHECK();
   204:   data[dataLen] = 0;
   205:   setDataLen(dataLen + 1);
   206:   SELFCHECK();
   207: }
   208: 
   209: 
   210: void DataBlock::setFromString(char const *srcString)
   211: {
   212:   SELFCHECK();
   213:   int len = std::strlen(srcString)+1;
   214:     // a sm_string is its contents and the null terminator
   215:   setFromBlock((byte const*)srcString, len);
   216:   SELFCHECK();
   217: }
   218: 
   219: void DataBlock::setFromBlock(byte const *srcData, int len)
   220: {
   221:   SELFCHECK();
   222:   if (len > allocated) {
   223:     setAllocated(len);
   224:   }
   225:   setDataLen(len);
   226:   if (len > 0) {
   227:     std::memcpy(data, srcData, len);
   228:   }
   229:   SELFCHECK();
   230: }
   231: 
   232: 
   233: DataBlock& DataBlock::operator= (DataBlock const &obj)
   234: {
   235:   SELFCHECK();
   236:   if (this != &obj) {
   237:     setAllocated(obj.allocated);
   238:     dataLen = obj.dataLen;
   239:     std::memcpy(data, obj.data, dataLen);
   240:   }
   241:   SELFCHECK();
   242:   return *this;
   243: }
   244: 
   245: 
   246: void DataBlock::print(char const *label, int bytesPerLine) const
   247: {
   248:   xassert(bytesPerLine >= 1);
   249:   SELFCHECK();
   250: 
   251:   if (label) {
   252:     printf("---- %s, length = %d, crc32 = 0x%lX ---- {\n",
   253:            label, getDataLen(),
   254:            crc32(getDataC(), getDataLen()));
   255:   }
   256: 
   257:   int cursor = 0;
   258:   while (cursor < getDataLen()) {
   259:     int linelen = min(bytesPerLine, getDataLen() - cursor);
   260:     xassert(linelen >= 1);    // ensure can't loop infinitely
   261: 
   262:     printf("  ");     // indent
   263:     printHexLine(getDataC() + cursor, linelen, bytesPerLine);
   264:     printf("   ");
   265:     printPrintableLine(getDataC() + cursor, linelen);
   266:     printf("\n");
   267: 
   268:     cursor += linelen;
   269:   }
   270: 
   271:   if (label) {
   272:     printf("}\n");
   273:   }
   274:   SELFCHECK();
   275: }
   276: 
   277: 
   278: // print 'length' bytes of 'data' in hex
   279: // blank-pad the output as if 'linelen' bytes were present
   280: STATICDEF void DataBlock::printHexLine(byte const *data, int length, int linelen)
   281: {
   282:   xassert(data != NULL &&
   283:           length >= 1 &&
   284:           linelen >= length);
   285: 
   286:   for (int i=0; i<linelen; i++) {
   287:     if (i < length) {
   288:       printf("%02X ", (byte)*data);
   289:       data++;
   290:     }
   291:     else {
   292:       printf("   ");
   293:     }
   294:   }
   295: }
   296: 
   297: 
   298: // print 'length' bytes of 'data', substituting 'unprintable' for bytes for
   299: // which 'isprint' is false
   300: STATICDEF void DataBlock::printPrintableLine(byte const *data, int length,
   301:                                              char unprintable)
   302: {
   303:   xassert(data != NULL &&
   304:           length >= 1);
   305: 
   306:   while (length--) {
   307:     if (isprint(*data)) {
   308:       printf("%c", *data);
   309:     }
   310:     else {
   311:       printf("%c", unprintable);
   312:     }
   313:     data++;
   314:   }
   315: }
   316: 
   317: 
   318: #if 0
   319: void DataBlock::print(char const *label) const
   320: {
   321:   enum { MARGIN = 70 };
   322: 
   323:   if (label) {
   324:     printf("------ %s (length=%d) -------\n", label, getDataLen());
   325:   }
   326: 
   327:   byte *p = data;
   328:   int i;
   329:   int column=0;
   330:   for (i=0; i<dataLen; i++, p++) {
   331:     if (isprint(*p)) {
   332:       if (*p != '\\') {
   333:         column += printf("%c", *p);
   334:       }
   335:       else {
   336:         printf("\\\\");     // otherwise '\\','x','nn','nn' would be ambiguous
   337:       }
   338:     }
   339:     else {
   340:       column += printf("\\x%02X", *p);
   341:     }
   342: 
   343:     if (column >= MARGIN && (i+1) < dataLen) {
   344:       printf("\\\n");       // continuation lines end with backslash
   345:       column = 0;
   346:     }
   347:   }
   348: 
   349:   // this makes spaces at the end of a buffer invisible.. oh well..
   350:   if (column != 0) {    // if didn't just newline...
   351:     printf("\n");
   352:   }
   353: 
   354:   if (label) {
   355:     printf("------ end of %s -------\n", label);
   356:   }
   357: }
   358: #endif // 0
   359: 
   360: 
   361: void DataBlock::dontPrint(char const *, int) const
   362: {}
   363: 
   364: 
   365: void DataBlock::writeToFile(char const *fname) const
   366: {
   367:   FILE *fp = fopen(fname, "wb");
   368:   if (!fp) {
   369:     xsyserror("fopen", fname);
   370:   }
   371: 
   372:   // finally giving in and silencing those *stupid* g++ warnings
   373:   // about comparing signed and unsigned for EQUALITY!!!
   374:   // I'll get you yet, you big stinking GNU!!
   375:   if ((int)fwrite(getDataC(), 1, getDataLen(), fp) != getDataLen()) {
   376:     xsyserror("fwrite", fname);
   377:   }
   378: 
   379:   if (fclose(fp) != 0) {
   380:     xsyserror("fclose", fname);
   381:   }
   382: }
   383: 
   384: 
   385: void DataBlock::readFromFile(char const *fname)
   386: {
   387:   FILE *fp = fopen(fname, "rb");
   388:   if (!fp) {
   389:     xsyserror("fopen", fname);
   390:   }
   391: 
   392:   // seek to end to know how much to allocate
   393:   if (fseek(fp, 0, SEEK_END) != 0) {
   394:     xsyserror("fseek", fname);
   395:   }
   396: 
   397:   long len = ftell(fp);
   398:   if (len < 0) {
   399:     xsyserror("ftell", fname);
   400:   }
   401: 
   402:   setAllocated(len);
   403: 
   404:   // read data
   405:   if (fseek(fp, 0, SEEK_SET) != 0) {
   406:     xsyserror("fseek", fname);
   407:   }
   408: 
   409:   if ((long)fread(getData(), 1, len, fp) != len) {
   410:     xsyserror("fread", fname);
   411:   }
   412: 
   413:   setDataLen(len);
   414: 
   415:   if (fclose(fp) != 0) {
   416:     xsyserror("fclose", fname);
   417:   }
   418: }
   419: 
   420: 
   421: // ------------- self test code --------------
   422: #ifdef DATABLOK_TEST
   423: 
   424: int doit()
   425: {
   426:   // nest everything so the dtors are inside
   427:   {
   428:     // test printing function
   429:     {
   430:       DataBlock b(260);
   431:       for (int i=0; i<260; i++) {
   432:         b.getData()[i] = (byte)i;
   433:       }
   434:       b.setDataLen(260);
   435:       b.print("all bytes plus 4 extra");
   436:     }
   437: 
   438:     DataBlock block("yadda smacker");
   439:     xassert(block.getDataLen() == 14);
   440: 
   441:     DataBlock block2((byte*)"yadda smacker", 13, 14);
   442:     block2.addNull();
   443:     xassert(block == block2);
   444: 
   445:     DataBlock block3;
   446:     block3 = block2;
   447:     xassert(block3 == block);
   448: 
   449:     block3.setAllocated(5);       // truncates
   450:     block2.setAllocated(25);
   451:     xassert(block3 != block2);
   452: 
   453:     // test file save/load
   454:     block.writeToFile("tempfile.blk");
   455:     DataBlock block4;
   456:     block4.readFromFile("tempfile.blk");
   457:     xassert(block == block4);
   458: 
   459:     // test overrun detection
   460:     try {
   461:       {
   462:         DataBlock b(block);
   463:         b.getData()[block.getAllocated()] = 0;   // overrun
   464: 
   465:         printf("this should cause an assertion failure:\n");
   466:         // invoke selfcheck in destructor
   467:       }
   468:       return printf("failed to detect overrun\n");
   469:     }
   470:     catch (...) {}
   471:   }
   472: 
   473:   printf("test succeeded\n");
   474:   return 0;
   475: }
   476: 
   477: int main()
   478: {
   479:   try {
   480:     return doit();
   481:   }
   482:   catch (xBase &x) {
   483:     return printf("failed: %s\n", x.why());
   484:   }
   485: }
   486: 
   487: #endif // DATABLOK_TEST
   488: 
End cpp section to elk/sm_datablok.cpp[1]
Start cpp section to elk/sm_exc.cpp[1 /1 ]
     1: #line 16239 "./lpsrc/sm.pak"
     2: // exc.cc            see license.txt for copyright and terms of use
     3: // code for exc.h
     4: // Scott McPeak, 1996-1998  This file is public domain.
     5: 
     6: #include "sm_exc.h"
     7: 
     8: #include <cstring>       // std::strlen, std::strcpy
     9: #include <iostream>     // clog
    10: #include <stdarg.h>       // va_xxx
    11: #include <ctype.h>        // toupper, tolower
    12: 
    13: 
    14: // ------------------------- xBase -----------------
    15: bool xBase::logExceptions = true;
    16: int xBase::creationCount = 0;
    17: 
    18: 
    19: xBase::xBase(char const *m)
    20:   : msg(m)
    21: {
    22:   if (logExceptions) {
    23:     std::clog << "Exception thrown: " << m << std::endl;
    24:   }
    25: 
    26:   // done at very end when we know this object will
    27:   // successfully be created
    28:   creationCount++;
    29: }
    30: 
    31: 
    32: xBase::xBase(xBase const &obj)
    33:   : msg(obj.msg)
    34: {
    35:   creationCount++;
    36: }
    37: 
    38: 
    39: xBase::~xBase()
    40: {
    41:   creationCount--;
    42: }
    43: 
    44: 
    45: // this is obviously not perfect, since exception objects can be
    46: // created and not thrown; I heard the C++ standard is going to,
    47: // or already does, include (by this name?) a function that does this
    48: // correctly; until then, this will serve as a close approximation
    49: // (this kind of test is, IMO, not a good way to handle the underlying
    50: // problem, but it does reasonably handle 70-90% of the cases that
    51: // arise in practice, so I will endorse it for now)
    52: bool unwinding()
    53: {
    54:   return xBase::creationCount != 0;
    55: }
    56: 
    57: 
    58: // tweaked version
    59: bool unwinding_other(xBase const &)
    60: {
    61:   // we know the passed xBase exists.. any others?
    62:   return xBase::creationCount > 1;
    63: }
    64: 
    65: 
    66: void xBase::insert(std::ostream &os) const
    67: {
    68:   os << why();
    69: }
    70: 
    71: 
    72: void xbase(char const *msg)
    73: {
    74:   xBase x(msg);
    75:   THROW(x);
    76: }
    77: 
    78: 
    79: // ------------------- x_assert -----------------
    80: x_assert::x_assert(char const *cond, char const *fname, int line)
    81:   : xBase(sm_stringb(
    82:       "Assertion failed: " << cond <<
    83:       ", file " << fname <<
    84:       " line " << line)),
    85:     condition(cond),
    86:     filename(fname),
    87:     lineno(line)
    88: {}
    89: 
    90: x_assert::x_assert(x_assert const &obj)
    91:   : xBase(obj),
    92:     condition(obj.condition),
    93:     filename(obj.filename),
    94:     lineno(obj.lineno)
    95: {}
    96: 
    97: x_assert::~x_assert()
    98: {}
    99: 
   100: 
   101: // failure function, declared in xassert.h
   102: void x_assert_fail(char const *cond, char const *file, int line)
   103: {
   104:   THROW(x_assert(cond, file, line));
   105: }
   106: 
   107: 
   108: // --------------- xFormat ------------------
   109: xFormat::xFormat(char const *cond)
   110:   : xBase(sm_stringb("Formatting error: " << cond)),
   111:     condition(cond)
   112: {}
   113: 
   114: xFormat::xFormat(xFormat const &obj)
   115:   : xBase(obj),
   116:     condition(obj.condition)
   117: {}
   118: 
   119: xFormat::~xFormat()
   120: {}
   121: 
   122: 
   123: void xformat(char const *condition)
   124: {
   125:   xFormat x(condition);
   126:   THROW(x);
   127: }
   128: 
   129: void formatAssert_fail(char const *cond, char const *file, int line)
   130: {
   131:   xFormat x(sm_stringc << "format assertion failed, "
   132:                     << file << ":" << line << ": "
   133:                     << cond);
   134:   THROW(x);
   135: }
   136: 
   137: 
   138: // -------------------- XOpen -------------------
   139: XOpen::XOpen(char const *fname)
   140:   : xBase(sm_stringc << "failed to open file: " << fname),
   141:     filename(fname)
   142: {}
   143: 
   144: XOpen::XOpen(XOpen const &obj)
   145:   : xBase(obj),
   146:     DMEMB(filename)
   147: {}
   148: 
   149: XOpen::~XOpen()
   150: {}
   151: 
   152: 
   153: void throw_XOpen(char const *fname)
   154: {
   155:   XOpen x(fname);
   156:   THROW(x);
   157: }
   158: 
   159: 
   160: // ---------------- test code ------------------
   161: #ifdef TEST_EXC
   162: 
   163: int main()
   164: {
   165:   xBase x("yadda");
   166:   std::cout << x << std::endl;
   167: 
   168:   try {
   169:     THROW(x);
   170:   }
   171:   catch (xBase &x) {
   172:     std::cout << "caught xBase: " << x << std::endl;
   173:   }
   174: 
   175:   return 0;
   176: }
   177: 
   178: #endif // TEST_EXC
   179: 
End cpp section to elk/sm_exc.cpp[1]
Start cpp section to elk/sm_missing.cpp[1 /1 ]
     1: #line 16419 "./lpsrc/sm.pak"
     2: // missing.cc            see license.txt for copyright and terms of use
     3: // code for missing.h
     4: // Scott McPeak, 1998  This file is public domain.
     5: 
     6: #include "sm_missing.h"
     7: 
     8: #include <ctype.h>       // tolower
     9: 
    10: int missing_stricmp(char const *s1, char const *s2)
    11: {
    12:   while (*s1 && *s2) {
    13:     // the choice between tolower and toupper affects lexicographic
    14:     // comparisons between letters and the symbols between Z and a;
    15:     // I don't know which is the "correct" way.
    16:     int d = tolower(*s1) - tolower(*s2);
    17:     if (d != 0) {
    18:       return d;
    19:     }
    20:     s1++;
    21:     s2++;
    22:   }
    23: 
    24:   // one or both are at the null terminator
    25:   return *s1 - *s2;
    26: }
End cpp section to elk/sm_missing.cpp[1]
Start cpp section to elk/sm_nonport.cpp[1 /1 ]
     1: #line 16446 "./lpsrc/sm.pak"
     2: // nonport.cpp            see license.txt for copyright and terms of use
     3: // code for nonport.h
     4: // Scott McPeak and Dan Bonachea, 1998-1999  This file is public domain.
     5: 
     6: #include <stdio.h>       // printf
     7: #include <stdlib.h>      // abort, exit
     8: #include <cstring>      // std::strncpy
     9: 
    10: #ifdef _WIN32
    11: #  ifdef USE_MINWIN_H
    12: #    include "minwin.h"   // api
    13: #  else
    14: #    include <windows.h>  // api
    15: #  endif
    16: 
    17: //#  include <strstream>   // ostrstream
    18: #  include <sstream>   // ostrstream
    19: #  include <conio.h>      // getch or _getch
    20: #  include <dos.h>        // sleep
    21: #  include <io.h>         // chmod
    22: #  ifdef __BORLANDC__
    23: #    include <dir.h>      // mkdir, chdir
    24: #    pragma warn -par     // warning: parameter not used
    25: #  else    // MSVC
    26: #    include <errno.h> // ENOENT
    27: #    include <direct.h>   // _mkdir, _chdir
    28: #    define getch _getch
    29: #    define mkdir _mkdir    // why must VC be such an oddball?
    30: #    define chdir _chdir
    31: #    define chmod _chmod
    32:      void sleep(unsigned sec) {
    33:        Sleep(sec * 1000);
    34:        return;
    35:        }
    36: #  endif
    37: #  define DIRSLASH '\\'
    38: #  define DIRSLASHES "\\/"
    39: 
    40: #else   // unix
    41: #  include <sys/time.h>   // struct timeval, gettimeofday
    42: #  include <sys/types.h>  // mkdir constants, DIR, struct dirent
    43: #  include <fcntl.h>      // mkdir constants
    44: #  include <unistd.h>     // mkdir, sleep, chdir, geteuid
    45: #  include <errno.h>      // errno
    46: #  include <pwd.h>        // getpwuid, struct passwd
    47: #  define DIRSLASH '/'
    48: #  define DIRSLASHES "/"
    49: 
    50: #endif
    51: 
    52: #include <sys/stat.h>     // chmod, mode macros
    53: #include <time.h>         // tzset, localtime, time
    54: #include <iostream>     // std::cout
    55: 
    56: #if !defined(_WIN32) || defined(__BORLANDC__)
    57:   #include <dirent.h>       // opendir
    58: #endif
    59: 
    60: #include "sm_nonport.h"
    61: 
    62: 
    63: NonportFailFunc nonportFail = defaultNonportFail;
    64: 
    65: void defaultNonportFail(char const *, char const *)
    66: {}
    67: 
    68: // convenience
    69: inline void fail(char const *call, char const *ctx=NULL)
    70: {
    71:   nonportFail(call, ctx);
    72: }
    73: 
    74: 
    75: void setRawMode(bool raw)
    76: {
    77: # ifdef _WIN32
    78:     // nothing necessary; getConsoleChar handles it
    79: 
    80: # else
    81:     int res;
    82:     if (raw) {
    83:       // turn off UNIX term output, put in raw mode
    84:       res = system("stty -echo raw");
    85:     }
    86:     else {
    87:       // back to normal mode
    88:       res = system("stty echo -raw");
    89:     }
    90: 
    91:     if (res != 0) {
    92:       //fail("system", "setRawMode");
    93:     }
    94: # endif
    95: }
    96: 
    97: 
    98: // get the next character typed, without any intervening interpretation
    99: // or buffering
   100: char getConsoleChar()
   101: {
   102: # ifdef _WIN32
   103:     // this function always bypasses 'cooked' console mode
   104:     return (char)getch();
   105: 
   106: # else
   107:     // relies on raw mode for console
   108:     int ch = getchar();
   109:     if (ch == EOF) {
   110:       fail("getchar", "getConsoleChar");
   111:     }
   112:     return ch;
   113: # endif
   114: }
   115: 
   116: 
   117: // return the # of milliseconds since some unspecified, but
   118: // constant for the life of the program, event
   119: long getMilliseconds()
   120: {
   121: # ifdef _WIN32
   122:     // # of milliseconds since program started
   123:     return GetTickCount();
   124: 
   125: # else
   126:     // some unspecified millisecond count (unspecified
   127:     // because tv.tv_sec * 1000 will overflow a 32-bit
   128:     // long, for typical values)
   129:     struct timeval tv;
   130:     gettimeofday(&tv, NULL);
   131:       // according to HPUX man page, this always returns 0 and does
   132:       // not have any errors defined
   133: 
   134:     //printf("tv.tv_sec = %d, tv.tv_usec = %d\n",
   135:     //       tv.tv_sec, tv.tv_usec);
   136:     return tv.tv_sec * 1000 + tv.tv_usec / 1000;
   137: # endif
   138: }
   139: 
   140: 
   141: bool limitFileAccess(char const *fname)
   142: {
   143:   // read/write for user, nothing for group or others
   144:   if (chmod(fname, 0600) != 0) {
   145:     fail("chmod", fname);
   146:     return false;
   147:   }
   148:   else {
   149:     return true;
   150:   }
   151: }
   152: 
   153: 
   154: bool createDirectory(char const *dirname)
   155: {
   156:   int res;
   157: # ifdef _WIN32
   158:     // no 'mode' argument
   159:     res = mkdir(dirname);
   160: # else   // unix
   161:     // read/write/execute permission for user, no one else
   162:     res = mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR);
   163: # endif
   164: 
   165:   if (res != 0) {
   166:     fail("mkdir", dirname);
   167:     return false;
   168:   }
   169:   else {
   170:     return true;
   171:   }
   172: }
   173: 
   174: 
   175: bool changeDirectory(char const *dirname)
   176: {
   177:   if (0!=chdir(dirname)) {
   178:     fail("chdir", dirname);
   179:     return false;
   180:   }
   181:   else {
   182:     return true;
   183:   }
   184: }
   185: 
   186: 
   187: bool getCurrentDirectory(char *dirname, int len)
   188: {
   189:   bool ok = getcwd(dirname, len) != NULL;
   190:   if (!ok) {
   191:     fail("getcwd");
   192:   }
   193:   return ok;
   194: }
   195: 
   196: 
   197: bool removeFile(char const *fname)
   198: {
   199:   bool ok = unlink(fname) == 0;
   200:   if (!ok) {
   201:     fail("unlink", fname);
   202:   }
   203:   return ok;
   204: }
   205: 
   206: 
   207: // this may in fact use a portable subset of the standard
   208: // C library.. but I'm not holding my breath, so that's why
   209: // this routine is in the 'nonport' module
   210: void getCurrentDate(int &month, int &day, int &year)
   211: {
   212:   // tzset is apparently required (recommended?) before
   213:   // calling localtime()
   214:   #if !defined(__CYGWIN__)
   215:   tzset();
   216:   #endif
   217: 
   218:   // retrieve standard UNIX time
   219:   time_t unixTime;
   220:   time(&unixTime);
   221: 
   222:   // convert to month/day/year
   223:   struct tm *t = localtime(&unixTime);
   224: 
   225:   // write into return variables
   226:   month = t->tm_mon + 1;
   227:   day = t->tm_mday;
   228:   year = t->tm_year + 1900;    // this is not a y2k bug!
   229: }
   230: 
   231: 
   232: void portableSleep(unsigned seconds)
   233: {
   234:   // with proper headers included (above), "sleep" works
   235:   sleep(seconds);
   236: }
   237: 
   238: 
   239: /*
   240: void getCurrentUsername(char *buf, int buflen)
   241: {
   242:   #ifdef _WIN32
   243:     DWORD len = buflen;
   244:     if (!GetUserName(buf, &len)) {
   245:       fail("GetUserName");
   246:       std::strncpy(buf, "(unknown)", buflen);
   247:     }
   248: 
   249:   #else    // unix (SunOS only?  we'll see..)
   250:     #if 0     // old.. linux man page insists 'cuserid' is a bad fn to call
   251:       char temp[L_cuserid];
   252:       cuserid(temp);         // fail return?
   253:     #endif // 0
   254: 
   255:     char const *temp;
   256:     struct passwd *pw = getpwuid(geteuid());
   257:     if (!pw) {
   258:       fail("getpwuid(geteuid())");
   259:       temp = "(unknown)";
   260:     }
   261:     else {
   262:       temp = pw->pw_name;
   263:     }
   264: 
   265:     std::strncpy(buf, temp, buflen);
   266:   #endif
   267: }
   268: */
   269: 
   270: // loop reading characters, return when finished
   271: static void nonechoLoop(char *buf, int len)
   272: {
   273:   int cursor = 0;
   274:   for(;;) {
   275:     char ch = getConsoleChar();
   276:     switch (ch) {
   277:       case '\r':    // carriage return
   278:         buf[cursor] = 0;
   279:         return;
   280: 
   281:       case '\b':    // backspace
   282:         if (cursor > 0) {
   283:           cursor--;
   284:         }
   285:         break;
   286: 
   287:       default:
   288:         buf[cursor++] = ch;
   289:         if (cursor >= len-1) {
   290:           // end of buffer
   291:           buf[len-1] = 0;
   292:           return;
   293:         }
   294:         break;
   295:     }
   296:   }
   297: }
   298: 
   299: 
   300: void readNonechoString(char *buf, int len, char const *prompt)
   301: {
   302:   std::cout << prompt;
   303:   std::cout.flush();
   304: 
   305:   setRawMode(true);
   306: 
   307:   try {
   308:     nonechoLoop(buf, len);
   309:   }
   310:   catch (...) {
   311:     setRawMode(false);    // make sure it gets reset
   312:     throw;
   313:   }
   314: 
   315:   setRawMode(false);
   316: 
   317:   std::cout << "\n";
   318:   std::cout.flush();
   319: }
   320: 
   321: 
   322: void applyToCwdContents(PerFileFunc func, void *extra)
   323: {
   324:   applyToDirContents(".", func, extra);
   325: }
   326: 
   327: 
   328: void applyToDirContents(char const *dirName,
   329:                         PerFileFunc func, void *extra)
   330: {
   331:   // SM: we had find{first,next} code here for win32, but
   332:   //     my Borland has opendir & friends, so let's see if
   333:   //     that works everywhere.. (old code is below, in
   334:   //     trash section)
   335:   // DOB: VC doesn't have opendir-
   336:   //  I think this is the only way to do it in the Win32 API
   337:   #if defined(_WIN32) && !defined(__BORLANDC__)
   338:     struct _finddata_t fb;
   339:     char* buf = new char[std::strlen(dirName)+5];
   340:     std::strcpy(buf, dirName);
   341:     if (buf[std::strlen(buf)-1] != '\\') strcat(buf, "\\");
   342:     strcat(buf, "*");
   343:     long handle = _findfirst(buf, &fb);
   344:     delete buf;
   345:     int done = (handle == -1);
   346:     if (handle == -1 && errno != ENOENT) // ENOENT = no matching entries
   347:       fail("_findfirst", dirName);
   348:     while (!done) {
   349:       if (!func(fb.name, extra)) {
   350:         break;
   351:         }
   352:       done = _findnext(handle, &fb);
   353:       }
   354:     if (handle != -1) {
   355:       if (_findclose(handle)) fail("_findclose", dirName);
   356:       }
   357: 
   358:   #else     // unix and borland
   359:     DIR *dir = opendir(dirName);
   360:     if (!dir) {
   361:       fail("opendir", dirName);
   362:       return;
   363:     }
   364: 
   365:     for(;;) {
   366:       // grab next directory entry
   367:       struct dirent *ent = readdir(dir);
   368:       if (!ent) {
   369:         break;     // end of listing
   370:       }
   371: 
   372:       if (!func(ent->d_name, extra)) {
   373:         break;     // user wants to stop listing
   374:       }
   375:     }
   376: 
   377:     if (closedir(dir) != 0) {
   378:       fail("closedir", dirName);
   379:     }
   380:   #endif
   381: }
   382: 
   383: 
   384: bool isDirectory(char const *path)
   385: {
   386:   struct stat st;
   387:   if (0!=stat(path, &st)) {
   388:     fail("stat", path);
   389:     return false;
   390:   }
   391:   #if defined(_WIN32) && !defined(__BORLANDC__)
   392:     return !!(st.st_mode & _S_IFDIR); // this is how it works for VC
   393:   #else
   394:     return S_ISDIR(st.st_mode);
   395:   #endif
   396: }
   397: 
   398: 
   399: bool fileOrDirectoryExists(char const *path)
   400: {
   401:   struct stat st;
   402:   if (0!=stat(path, &st)) {
   403:     return false;     // assume error is because of nonexistence
   404:   }
   405:   else {
   406:     return true;
   407:   }
   408: }
   409: 
   410: 
   411: // adapted from Dan's keyutils.cpp (and indentation style
   412: // ruthlessly changed! :) )
   413: bool ensurePath(char const *filename, bool isDirectory)
   414: {
   415:   // make a temporary buffer so we can modify it safely
   416:   int len = std::strlen(filename);
   417:   char *temp = new char[len+1];
   418:   std::strcpy(temp, filename);
   419: 
   420:   if (isDirectory) {
   421:     len++;    // also consider final '\0' (strchr returns ptr to it)
   422:   }
   423: 
   424:   // start at 1 (and not 0) because if the path starts with a slash,
   425:   // then starting at 0 will cause us to try mkdir("")
   426:   for (int i=1; i < len; i++) {
   427:     if (strchr(DIRSLASHES, temp[i])) {
   428:       // wherever there is a slash (or '\0'), truncate and test
   429:       temp[i] = '\0';
   430:       if (!fileOrDirectoryExists(temp)) {
   431:         // make directory if necessary; automatically limits file access
   432:         if (!createDirectory(temp)) {
   433:           delete[] temp;
   434:           return false;
   435:         }
   436:       }
   437:       temp[i] = DIRSLASH;      // may kill final '\0', doesn't matter
   438:     }
   439:   }
   440: 
   441:   // was leaking this.. found the leak with the feudal-C instrumentor!
   442:   delete[] temp;
   443:   return true;
   444: }
   445: 
   446: 
   447: // underlying test
   448: bool hsrcHelper()
   449: {
   450: /*
   451:   #ifdef __UNIX__
   452:     // see if /dev/random exists and is readable
   453:     int fd = open("/dev/random", O_RDONLY);
   454:     if (fd < 0) {
   455:       return false;
   456:     }
   457: 
   458:     // looks ok!
   459:     if (close(fd) < 0) {
   460:       perror("close");
   461:       return false;      // seems unlikely, but..
   462:     }
   463: 
   464:     return true;
   465: 
   466:   #else    // windows
   467:     return false;
   468:   #endif
   469: */
   470:   return false;
   471: }
   472: 
   473: bool hasSystemCryptoRandom()
   474: {
   475:   static bool cached = false;
   476:   static bool cachedAnswer;
   477: 
   478:   if (!cached) {
   479:     cachedAnswer = hsrcHelper();
   480:     cached = true;
   481:   }
   482: 
   483:   return cachedAnswer;
   484: }
   485: 
   486: 
   487: // assume we are only called if the above fn returned true;
   488: // for this fn, any failure is considered fatal because we
   489: // don't have a way to communicate it, and the possibility of
   490: // returning nonrandom values is not tolerable (if something
   491: // I haven't thought of is causing failure, add it to the list
   492: // of things hasSystemCryptoRandom checks)
   493: unsigned getSystemCryptoRandom()
   494: {
   495: /*
   496:   #ifdef __UNIX__
   497:     // open /dev/random
   498:     int fd = open("/dev/random", O_RDONLY);
   499:     if (!fd) {
   500:       perror("open");
   501:       exit(2);
   502:     }
   503: 
   504:     // grab 4 bytes
   505:     union {
   506:       unsigned ret;
   507:       char c[4];
   508:     };
   509:     int got = 0;
   510: 
   511:     while (got < 4) {
   512:       int ct = read(fd, c+got, 4-got);
   513:       if (ct < 0) {
   514:         perror("read");
   515:         exit(2);
   516:       }
   517:       if (ct == 0) {
   518:         fprintf(stderr, "got 0 bytes from /dev/random.. it's supposed to block!!\n");
   519:         exit(2);
   520:       }
   521:       got += ct;
   522:     }
   523: 
   524:     if (close(fd) < 0) {
   525:       perror("close");
   526:       exit(2);
   527:     }
   528: 
   529:     return ret;
   530: 
   531:   #else     // windows
   532:     fprintf(stderr, "no system crypto random function available!\n");
   533:     exit(2);
   534:     return 0;    // silence warning
   535:   #endif
   536: */
   537:     fprintf(stderr, "no system crypto random function available!\n");
   538:     exit(2);
   539:     return 0;    // silence warning
   540: 
   541: }
   542: 
   543: 
   544: #if 0
   545: int getProcessId()
   546: {
   547:   #ifdef __UNIX__
   548:     return getpid();
   549: 
   550:   #else // windows
   551:     return GetCurrentProcessId();
   552: 
   553:   #endif
   554: }
   555: #endif
   556: 
   557: // do we have access to C99 vsnprintf?
   558: #if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
   559:   #if __GLIBC__ >= 3 || (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1)
   560:     // glibc-2.1 or later: yes (glibc-2.0.6 has a function called
   561:     // vsnprintf, but the interface is wrong)
   562:     #define HAS_C99_VSNPRINTF
   563:   #endif
   564: #endif
   565: 
   566: // note to reader: if your system has C99 VSNPRINTF, feel free to add
   567: // appropriate detection code
   568: 
   569: #ifndef HAS_C99_VSNPRINTF
   570:   // no vsnprintf, will use gprintf (which is slow, and overestimates sometimes)
   571: #include "sm_gprintf.h"
   572: 
   573:   static int counting_output_function(void *extra, int ch)
   574:   {
   575:     // 'extra' is a pointer to the count
   576:     int *ct = (int*)extra;
   577:     (*ct)++;
   578:     return 0;    // no failure
   579:   }
   580: #endif // !HAS_C99_VSNPRINTF
   581: 
   582: int vnprintf(char const *format, va_list args)
   583: {
   584:   #ifdef HAS_C99_VSNPRINTF
   585:     // can use this directly
   586:     return vsnprintf(NULL, 0, format, args);
   587: 
   588:   #else
   589:     // conservatively interpret the format sm_string using gprintf
   590:     int count = 0;
   591:     general_vprintf(counting_output_function, &count, format, args);
   592:     return count;
   593:   #endif
   594: }
   595: 
   596: 
   597: int nprintf(char const *format, ...)
   598: {
   599:   va_list args;
   600:   va_start(args, format);
   601:   int ret = vnprintf(format, args);
   602:   va_end(args);
   603:   return ret;
   604: }
   605: 
   606: 
   607: // ----------------- test code --------------------
   608: #ifdef TEST_NONPORT
   609: 
   610: #include <stdio.h>       // printf
   611: 
   612: 
   613: // helper for testing applyToCwdFiles
   614: bool printFirst10(char const *name, void *extra)
   615: {
   616:   int &count = *((int*)extra);
   617:   count++;
   618:   if (count <= 10) {
   619:     printf("  %s\n", name);
   620:     return true;    // continue
   621:   }
   622:   else {
   623:     return false;   // stop
   624:   }
   625: }
   626: 
   627: 
   628: bool printIt(char const *name, void*)
   629: {
   630:   printf("%s\n", name);
   631:   return true;    // continue
   632: }
   633: 
   634: 
   635: void testingFail(char const *call, char const *ctx)
   636: {
   637:   printf("FAIL: call=%s, ctx=%s, errno=%d\n",
   638:          call, (ctx? ctx : "(null)"), errno);
   639: }
   640: 
   641: 
   642: void nprintfVector(char const *format, ...)
   643: {
   644:   va_list args;
   645: 
   646:   // run vnprintf to obtain estimate
   647:   va_start(args, format);
   648:   int est = vnprintf(format, args);
   649:   va_end(args);
   650: 
   651:   // make that much space
   652:   char *buf = new char[est+1 + 50 /*safety margin*/];
   653: 
   654:   // run the real vsprintf
   655:   va_start(args, format);
   656:   int len = vsprintf(buf, format, args);
   657:   va_end(args);
   658: 
   659:   if (len > est) {
   660:     printf("nprintf failed to conservatively estimate!\n");
   661:     printf("    format: %s\n", format);
   662:     printf("  estimate: %d\n", est);
   663:     printf("    actual: %d\n", len);
   664:     exit(2);
   665:   }
   666: 
   667:   if (len != est) {
   668:     // print the overestimates; they shouldn't be much noise in the
   669:     // common case, but might hint at a problem earlier than I'd
   670:     // otherwise notice
   671:     printf("nprintf overestimate:\n");
   672:     printf("    format: %s\n", format);
   673:     printf("  estimate: %d\n", est);
   674:     printf("    actual: %d\n", len);
   675:   }
   676: 
   677:   delete[] buf;
   678: }
   679: 
   680: 
   681: int main(int argc, char **argv)
   682: {
   683:   nonportFail = testingFail;
   684: 
   685:   char s[4];
   686:   s[0] = '-';
   687:   s[1] = 'l';
   688:   s[2] = 's';
   689:   s[3] = 0;
   690:   if (0!=std::strcmp(s, "-ls")) {
   691:     printf("std::strcmp failed!\n");
   692:     return 4;
   693:   }
   694: 
   695:   // process arguments
   696:   bool interactive = false;
   697:   for (int i=1; i<argc; i++) {
   698:     if (0==std::strcmp("-ls", argv[i])) {
   699:       // do an ls, and bail
   700:       applyToCwdContents(printIt);
   701:       return 0;
   702:     }
   703:     else if (0==std::strcmp("-noninteractive", argv[i])) {
   704:       // don't do the interactive stuff
   705:       interactive = false;
   706:     }
   707:     else {
   708:       printf("unknown option: %s\n", argv[i]);
   709:       return 2;
   710:     }
   711:   }
   712: 
   713:   // trying to figure out why backspace sometimes gives ^? crap
   714:   // (turns out Konsole sometimes sends ^? in response to BS,
   715:   // even when the option looks unchecked)
   716:   //char buf[80];
   717:   //printf("type stuff and try backspace: ");
   718:   //gets(buf);
   719:   //printf("%s (%d chars)\n", buf, std::strlen(buf));
   720:   //return 0;
   721: 
   722:   long startTime = getMilliseconds();
   723: 
   724:   if (interactive) {
   725:     printf("Type some characters; you should see each\n"
   726:            "character echoed once as you type it (q to stop):\n");
   727:     setRawMode(true);
   728:     char ch;
   729:     do {
   730:       ch = getConsoleChar();
   731:       printf("%c", ch);
   732:     } while (ch != 'q');
   733: 
   734:     setRawMode(false);
   735: 
   736:     printf("\n\nYou typed for %ld milliseconds\n",
   737:            getMilliseconds() - startTime);
   738:   }
   739: 
   740:   limitFileAccess("chmod.test");
   741: 
   742:   printf("if the current dir contains a file called "
   743:          "chmod.test, I just attempted to limit\n"
   744:          "its access to just the owner\n");
   745: 
   746:   createDirectory("test.dir");
   747: 
   748:   // test chdir, which also implicitly tests mkdir
   749:   bool didFirst=false;
   750:   if (!changeDirectory("test.dir") || (didFirst=true, false) ||
   751:       !changeDirectory("..")) {
   752:     printf("failed while trying to chdir to %s\n",
   753:            (didFirst? ".." : "test.dir"));
   754:   }
   755: 
   756:   // more straightforward
   757:   if (!fileOrDirectoryExists("test.dir")) {
   758:     printf("test.dir didn't get created?\n");
   759:   }
   760: 
   761:   printf("what's more, I just tried to mkdir & chdir test.dir\n");
   762: 
   763:   // test ensurePath
   764:   if (!ensurePath("test.dir/a/b/c/d", false /*isDirectory*/)) {
   765:     printf("ensurePath test.dir/a/b/c/d failed\n");
   766:   }
   767: 
   768:   // try to list partial directory contents
   769:   printf("listing of first 10 files in this directory:\n");
   770:   {
   771:     int count = 0;
   772:     applyToCwdContents(printFirst10, &count);
   773:   }
   774: 
   775:   // test date function
   776:   {
   777:     int m, d, y;
   778:     getCurrentDate(m, d, y);
   779: 
   780:     printf("I think the date is (m/d/yyyy): %d/%d/%d\n",
   781:            m, d, y);
   782:   }
   783: 
   784:   // test sleep (mostly just to make sure it doesn't segfault)
   785:   printf("sleeping for 1 second...\n");
   786:   portableSleep(1);
   787: 
   788:   /*
   789:   // test user name
   790:   char buf[80];
   791:   getCurrentUsername(buf, 80);
   792:   printf("current user name is: %s\n", buf);
   793:   */
   794: 
   795:   if (interactive) {
   796:     // test nonecho reading
   797:     printf("Type something and press Enter; it won't be echoed (yet):\n");
   798:     readNonechoString(buf, 80, "  > ");
   799:     printf("You typed: %s\n", buf);
   800:   }
   801: 
   802:   // test random stuff
   803:   printf("hasSystemCryptoRandom: ");
   804:   if (!hasSystemCryptoRandom()) {
   805:     printf("no\n");
   806:   }
   807:   else {
   808:     printf("yes\n");
   809: 
   810:     printf("three random numbers: %u %u %u\n",
   811:            getSystemCryptoRandom(),
   812:            getSystemCryptoRandom(),
   813:            getSystemCryptoRandom());
   814:   }
   815: 
   816:   printf("testing nprintf...\n");
   817:   nprintfVector("simple");
   818:   nprintfVector("a %s more", "little");
   819:   nprintfVector("some %4d more %s complicated %c stuff",
   820:                 33, "yikes", 'f');
   821:   nprintfVector("%f", 3.4);
   822: 
   823:   printf("nonport works\n");
   824:   return 0;
   825: }
   826: 
   827: #endif // TEST_NONPORT
   828: 
   829: 
   830: // -------------- trash ----------------
   831: #if 0
   832: void limitFileAccess(char const *fname)
   833: {
   834:   // we'll decide whether this is possible by whether
   835:   // or not the necessary macros are defined
   836: #ifndef S_IRGRP     // assume rest are if this is
   837:     // probably can't do anything
   838:     return;
   839: #else
   840: 
   841:   // modify file permissions (to simplify things,
   842:   // we'll just set permissions, rather than
   843:   // read-modify-write); don't bother testing for
   844:   // error condition since there isn't much we
   845:   // can do about it anyway
   846:   chmod(fname, S_IRUSR | S_IWUSR);
   847: # endif
   848: }
   849: 
   850: # ifdef _WIN32     // findfirst, findnext
   851: #   ifdef __BORLANDC__
   852:       struct ffblk fb;
   853:       int done = findfirst("*", &fb, 0);
   854:       while (!done) {
   855:         if (!func(fb.ff_name, extra)) {
   856:           break;
   857:         }
   858:         done = findnext(&fb);
   859:       }
   860: #   else  // DOB: VC has a totally diff interface for this
   861:       struct _finddata_t fb;
   862:       long handle = _findfirst("*", &fb);
   863:       int done = (handle == -1);
   864:       while (!done) {
   865:         if (!func(fb.name, extra)) {
   866:           break;
   867:         }
   868:         done = _findnext(handle, &fb);
   869:       }
   870:       if (handle != -1) _findclose(handle);
   871: #   endif
   872: # else    // unix     // {open,read,close}dir, stat
   873: # endif
   874: 
   875: #endif // 0    (trash)
   876: 
End cpp section to elk/sm_nonport.cpp[1]
Start cpp section to elk/sm_str.cpp[1 /1 ]
     1: #line 17323 "./lpsrc/sm.pak"
     2: // str.cpp            see license.txt for copyright and terms of use
     3: // code for str.h
     4: // Scott McPeak, 1995-2000  This file is public domain.
     5: 
     6: #include "sm_str.h"
     7: 
     8: #include <stdlib.h>         // atoi
     9: #include <stdio.h>          // sprintf
    10: #include <ctype.h>          // isspace
    11: #include <cstring>         // std::strcmp
    12: #include <iostream>       // std::ostream << char*
    13: #include <assert.h>         // assert
    14: 
    15: #include "sm_xassert.h"
    16: #include "sm_ckheap.h"
    17: #include "sm_flatten.h"
    18: #include "sm_nonport.h"
    19: 
    20: 
    21: // ----------------------- sm_string ---------------------
    22: // ideally the compiler would arrange for 'empty', and the
    23: // "" it points to, to live in a read-only section of mem..;
    24: // but I can't declare it 'char const *' because I assign
    25: // it to 's' in many places..
    26: char * const sm_string::empty = "";
    27: 
    28: 
    29: sm_string::sm_string(char const *src, int length)
    30: {
    31:   s=empty;
    32:   setlength(length);       // setlength already has the +1; sets final NUL
    33:   std::memcpy(s, src, length);
    34: }
    35: 
    36: 
    37: void sm_string::dup(char const *src)
    38: {
    39:   if (!src || src[0]==0) {
    40:     s = empty;
    41:   }
    42:   else {
    43:     s = new char[ std::strlen(src) + 1 ];
    44:     xassert(s);
    45:     std::strcpy(s, src);
    46:   }
    47: }
    48: 
    49: void sm_string::kill()
    50: {
    51:   if (s != empty) {
    52:     delete s;
    53:   }
    54: }
    55: 
    56: 
    57: sm_string::sm_string(Flatten&)
    58:   : s(empty)
    59: {}
    60: 
    61: void sm_string::xfer(Flatten &flat)
    62: {
    63:   flat.xferCharString(s);
    64: }
    65: 
    66: 
    67: int sm_string::length() const
    68: {
    69:   xassert(s);
    70:   return std::strlen(s);
    71: }
    72: 
    73: bool sm_string::contains(char c) const
    74: {
    75:   xassert(s);
    76:   return !!strchr(s, c);
    77: }
    78: 
    79: 
    80: sm_string sm_string::subsm_string(int startIndex, int len) const
    81: {
    82:   xassert(startIndex >= 0 &&
    83:           len >= 0 &&
    84:           startIndex + len <= length());
    85: 
    86:   return sm_string(s+startIndex, len);
    87: }
    88: 
    89: 
    90: sm_string &sm_string::setlength(int length)
    91: {
    92:   kill();
    93:   if (length > 0) {
    94:     s = new char[ length+1 ];
    95:     xassert(s);
    96:     s[length] = 0;      // final NUL in expectation of 'length' chars
    97:     s[0] = 0;           // in case we just wanted to set allocated length
    98:   }
    99:   else {
   100:     xassert(length == 0);     // negative wouldn't make sense
   101:     s = empty;
   102:   }
   103:   return *this;
   104: }
   105: 
   106: 
   107: int sm_string::compareTo(sm_string const &src) const
   108: {
   109:   return compareTo(src.s);
   110: }
   111: 
   112: int sm_string::compareTo(char const *src) const
   113: {
   114:   if (src == NULL) {
   115:     src = empty;
   116:   }
   117:   return std::strcmp(s, src);
   118: }
   119: 
   120: 
   121: sm_string sm_string::operator&(sm_string const &tail) const
   122: {
   123:   sm_string dest(length() + tail.length());
   124:   std::strcpy(dest.s, s);
   125:   strcat(dest.s, tail.s);
   126:   return dest;
   127: }
   128: 
   129: sm_string& sm_string::operator&=(sm_string const &tail)
   130: {
   131:   return *this = *this & tail;
   132: }
   133: 
   134: 
   135: void sm_string::readdelim(std::istream &is, char const *delim)
   136: {
   137:   sm_stringBuilder sb;
   138:   sb.readdelim(is, delim);
   139:   operator= (sb);
   140: }
   141: 
   142: 
   143: void sm_string::write(std::ostream &os) const
   144: {
   145:   os << s;     // standard char* writing routine
   146: }
   147: 
   148: 
   149: void sm_string::selfCheck() const
   150: {
   151:   if (s != empty) {
   152:     checkHeapNode(s);
   153:   }
   154: }
   155: 
   156: 
   157: // --------------------- sm_stringBuilder ------------------
   158: sm_stringBuilder::sm_stringBuilder(int len)
   159: {
   160:   init(len);
   161: }
   162: 
   163: void sm_stringBuilder::init(int initSize)
   164: {
   165:   size = initSize + EXTRA_SPACE + 1;     // +1 to be like sm_string::setlength
   166:   s = new char[size];
   167:   end = s;
   168:   end[initSize] = 0;
   169: }
   170: 
   171: 
   172: void sm_stringBuilder::dup(char const *str)
   173: {
   174:   int len = std::strlen(str);
   175:   init(len);
   176:   std::strcpy(s, str);
   177:   end += len;
   178: }
   179: 
   180: 
   181: sm_stringBuilder::sm_stringBuilder(char const *str)
   182: {
   183:   dup(str);
   184: }
   185: 
   186: 
   187: sm_stringBuilder::sm_stringBuilder(char const *str, int len)
   188: {
   189:   init(len);
   190:   std::memcpy(s, str, len);
   191:   end += len;
   192: }
   193: 
   194: 
   195: sm_stringBuilder& sm_stringBuilder::operator=(char const *src)
   196: {
   197:   if (s != src) {
   198:     kill();
   199:     dup(src);
   200:   }
   201:   return *this;
   202: }
   203: 
   204: 
   205: sm_stringBuilder& sm_stringBuilder::setlength(int newlen)
   206: {
   207:   kill();
   208:   init(newlen);
   209:   return *this;
   210: }
   211: 
   212: 
   213: void sm_stringBuilder::adjustend(char* newend)
   214: {
   215:   xassert(s <= newend  &&  newend < s + size);
   216: 
   217:   end = newend;
   218:   *end = 0;        // sm 9/29/00: maintain invariant
   219: }
   220: 
   221: 
   222: sm_stringBuilder& sm_stringBuilder::operator&= (char const *tail)
   223: {
   224:   append(tail, std::strlen(tail));
   225:   return *this;
   226: }
   227: 
   228: void sm_stringBuilder::append(char const *tail, int len)
   229: {
   230:   ensure(length() + len);
   231: 
   232:   std::memcpy(end, tail, len);
   233:   end += len;
   234:   *end = 0;
   235: }
   236: 
   237: 
   238: sm_stringBuilder& sm_stringBuilder::indent(int amt)
   239: {
   240:   xassert(amt >= 0);
   241:   ensure(length() + amt);
   242: 
   243:   std::memset(end, ' ', amt);
   244:   end += amt;
   245:   *end = 0;
   246: 
   247:   return *this;
   248: }
   249: 
   250: 
   251: void sm_stringBuilder::grow(int newMinLength)
   252: {
   253:   // I want at least EXTRA_SPACE extra
   254:   int newMinSize = newMinLength + EXTRA_SPACE + 1;         // compute resulting allocated size
   255: 
   256:   // I want to grow at the rate of at least 50% each time
   257:   int suggest = size * 3 / 2;
   258: 
   259:   // see which is bigger
   260:   newMinSize = max(newMinSize, suggest);
   261: 
   262:   // remember old length..
   263:   int len = length();
   264: 
   265:   // realloc s to be newMinSize bytes
   266:   char *temp = new char[newMinSize];
   267:   xassert(len+1 <= newMinSize);    // prevent overrun
   268:   std::memcpy(temp, s, len+1);          // copy null too
   269:   delete[] s;
   270:   s = temp;
   271: 
   272:   // adjust other variables
   273:   end = s + len;
   274:   size = newMinSize;
   275: }
   276: 
   277: 
   278: sm_stringBuilder& sm_stringBuilder::operator<< (char c)
   279: {
   280:   ensure(length() + 1);
   281:   *(end++) = c;
   282:   *end = 0;
   283:   return *this;
   284: }
   285: 
   286: 
   287: #define MAKE_LSHIFT(Argtype, fmt)                        \
   288:   sm_stringBuilder& sm_stringBuilder::operator<< (Argtype arg) \
   289:   {                                                      \
   290:     char buf[60];      /* big enough for all types */    \
   291:     int len = sprintf(buf, fmt, arg);                    \
   292:     if (len >= 60) {                                     \
   293:       abort();    /* too big */                          \
   294:     }                                                    \
   295:     return *this << buf;                                 \
   296:   }
   297: 
   298: MAKE_LSHIFT(long, "%ld")
   299: MAKE_LSHIFT(unsigned long, "%lu")
   300: MAKE_LSHIFT(float, "%g")
   301: MAKE_LSHIFT(double, "%g")
   302: MAKE_LSHIFT(void*, "%p")
   303: #ifdef HAVE_LONGLONG
   304: MAKE_LSHIFT(unsigned long long, "%llu")
   305: MAKE_LSHIFT(long long, "%ll")
   306: #endif
   307: #ifdef HAVE_LONGDOUBLE
   308: MAKE_LSHIFT(long double, "%Lg")
   309: #endif
   310: #undef MAKE_LSHIFT
   311: 
   312: 
   313: sm_stringBuilder& sm_stringBuilder::operator<< (
   314:   sm_stringBuilder::Hex const &h)
   315: {
   316:   char buf[32];        // should only need 19 for 64-bit word..
   317:   int len = sprintf(buf, "0x%lX", h.value);
   318:   if (len >= 20) {
   319:     abort();
   320:   }
   321:   return *this << buf;
   322: 
   323:   // the length check above isn't perfect because we only find out there is
   324:   // a problem *after* trashing the environment.  it is for this reason I
   325:   // use 'assert' instead of 'xassert' -- the former calls abort(), while the
   326:   // latter throws an exception in anticipation of recoverability
   327: }
   328: 
   329: 
   330: sm_stringBuilder& sm_stringBuilder::operator<< (Manipulator manip)
   331: {
   332:   return manip(*this);
   333: }
   334: 
   335: 
   336: // slow but reliable
   337: void sm_stringBuilder::readdelim(std::istream &is, char const *delim)
   338: {
   339:   char c;
   340:   is.get(c);
   341:   while (!is.eof() &&
   342:          (!delim || !strchr(delim, c))) {
   343:     *this << c;
   344:     is.get(c);
   345:   }
   346: }
   347: 
   348: 
   349: // ---------------------- toString ---------------------
   350: #define TOSTRING(type)        \
   351:   sm_string toString(type val)   \
   352:   {                           \
   353:     return sm_stringc << val;    \
   354:   }
   355: 
   356: TOSTRING(int)
   357: TOSTRING(unsigned)
   358: TOSTRING(char)
   359: TOSTRING(long)
   360: TOSTRING(float)
   361: 
   362: #undef TOSTRING
   363: 
   364: // this one is more liberal than 'sm_stringc << null' because it gets
   365: // used by the PRINT_GENERIC macro in my astgen tool
   366: sm_string toString(char const *str)
   367: {
   368:   if (!str) {
   369:     return sm_string("(null)");
   370:   }
   371:   else {
   372:     return sm_string(str);
   373:   }
   374: }
   375: 
   376: 
   377: // ------------------- sm_stringf -----------------
   378: sm_string sm_stringf(char const *format, ...)
   379: {
   380:   va_list args;
   381:   va_start(args, format);
   382:   sm_string ret = vsm_stringf(format, args);
   383:   va_end(args);
   384:   return ret;
   385: }
   386: 
   387: 
   388: sm_string vsm_stringf(char const *format, va_list args)
   389: {
   390:   // estimate sm_string length
   391:   // int est = vnprintf(format, args);
   392: 
   393:   va_list args2;
   394:   va_copy(args2, args);
   395:   int est = vnprintf(format, args2);
   396:   va_end(args2);
   397: 
   398:   // allocate space
   399:   sm_string ret(est+1);
   400: 
   401:   // render the sm_string
   402:   int len = vsprintf(ret.pchar(), format, args);
   403: 
   404:   // check the estimate, and fail *hard* if it was low, to avoid any
   405:   // possibility that this might become exploitable in some context
   406:   // (do *not* turn this check off in an NDEGUG build)
   407:   if (len > est) {
   408:     // don't go through fprintf, etc., because the state of memory
   409:     // makes that risky
   410:     static char const msg[] =
   411:       "fatal error: vnprintf failed to provide a conservative estimate,\n"
   412:       "memory is most likely corrupted\n";
   413:     fprintf(stderr, msg);
   414:     abort();
   415:   }
   416: 
   417:   // happy
   418:   return ret;
   419: }
   420: 
   421: 
   422: // ------------------ test code --------------------
   423: #ifdef TEST_STR
   424: 
   425: #include <iostream>    // std::cout
   426: 
   427: void test(unsigned long val)
   428: {
   429:   //std::cout << sm_stringb(val << " in hex: 0x" << sm_stringBuilder::Hex(val)) << std::endl;
   430: 
   431:   std::cout << sm_stringb(val << " in hex: " << SBHex(val)) << std::endl;
   432: }
   433: 
   434: int main()
   435: {
   436:   // for the moment I just want to test the hex formatting
   437:   test(64);
   438:   test(0xFFFFFFFF);
   439:   test(0);
   440:   test((unsigned long)(-1));
   441:   test(1);
   442: 
   443:   std::cout << "sm_stringf: " << sm_stringf("int=%d hex=%X str=%s char=%c float=%f",
   444:                                  5, 0xAA, "hi", 'f', 3.4) << std::endl;
   445: 
   446:   std::cout << "tests passed\n";
   447: 
   448:   return 0;
   449: }
   450: 
   451: #endif // TEST_STR
   452: 
End cpp section to elk/sm_str.cpp[1]
Start cpp section to elk/sm_strtokp.cpp[1 /1 ]
     1: #line 17776 "./lpsrc/sm.pak"
     2: // strtokp.cc            see license.txt for copyright and terms of use
     3: // code for std::strtokp.h
     4: // Scott McPeak, 1997, 1999, 2000  This file is public domain.
     5: 
     6: #include "sm_strtokp.h"
     7: #include "sm_exc.h"
     8: #include <cstring>     // std::strtok
     9: 
    10: 
    11: StrtokParse::StrtokParse(char const *str, char const *delim)
    12: {
    13:   xassert(str != NULL);
    14: 
    15:   // make local copy
    16:   buf = str;
    17: 
    18:   // parse it first time to count # of tokens
    19:   int ct=0;
    20:   char *tok = std::strtok(buf.pchar(), delim);
    21:   while (tok) {
    22:     ct++;
    23:     tok = std::strtok(NULL, delim);
    24:   }
    25: 
    26:   // restore buf
    27:   buf = str;
    28: 
    29:   // allocate storage
    30:   _tokc = ct;
    31:   if (ct) {
    32:     _tokv = new char*[ct+1];
    33:     _tokv[ct] = NULL;     // terminate argv[]-like list
    34:   }
    35:   else {
    36:     _tokv = NULL;
    37:   }
    38: 
    39:   // parse it again, this time saving the values
    40:   ct=0;
    41:   tok = std::strtok(buf.pchar(), delim);
    42:   while (tok) {
    43:     _tokv[ct] = tok;
    44:     ct++;
    45:     tok = std::strtok(NULL, delim);
    46:   }
    47: 
    48:   // simple check just because it's easy
    49:   xassert(ct == _tokc);
    50: }
    51: 
    52: 
    53: StrtokParse::~StrtokParse()
    54: {
    55:   // buf deletes itself
    56: 
    57:   if (_tokv) {
    58:     delete _tokv;
    59:   }
    60: }
    61: 
    62: 
    63: void StrtokParse::validate(int which) const
    64: {
    65:   xassert((unsigned)which < (unsigned)_tokc);
    66: }
    67: 
    68: 
    69: char const *StrtokParse::tokv(int which) const
    70: {
    71:   validate(which);
    72:   return _tokv[which];
    73: }
    74: 
    75: 
    76: sm_string StrtokParse::
    77:   reassemble(int firstTok, int lastTok, char const *original) const
    78: {
    79:   int left = offset(firstTok);
    80:   int right = offset(lastTok) + std::strlen(tokv(lastTok));
    81: 
    82:   return sm_string(original + left, right-left);
    83: }
    84: 
    85: 
    86: sm_string StrtokParse::
    87:   join(int firstTok, int lastTok, char const *separator) const
    88: {
    89:   sm_stringBuilder sb;
    90: 
    91:   for (int i=firstTok; i<=lastTok; i++) {
    92:     if (i > firstTok) {
    93:       sb << separator;
    94:     }
    95:     sb << tokv(i);
    96:   }
    97: 
    98:   return sb;
    99: }
   100: 
   101: 
   102: int StrtokParse::offset(int which) const
   103: {
   104:   return tokv(which) - (char const*)buf;
   105: }
End cpp section to elk/sm_strtokp.cpp[1]
Start cpp section to elk/sm_syserr.cpp[1 /1 ]
     1: #line 17882 "./lpsrc/sm.pak"
     2: // syserr.cpp            see license.txt for copyright and terms of use
     3: // code for syserr.h
     4: // Scott McPeak, 1999-2000  This file is public domain.
     5: 
     6: #include "sm_syserr.h"
     7: 
     8: // ---------------- portable code ----------------
     9: char const * const xSysError::reasonStrings[] = {
    10:   "No error occurred",
    11:   "File not found",
    12:   "Path not found",
    13:   "Access denied",
    14:   "Out of memory (maybe)",    // always a suspicious message
    15:   "Invalid pointer address",
    16:   "Invalid data format",
    17:   "Invalid argument",
    18:   "Attempt to modify read-only data",
    19:   "The object already exists",
    20:   "Resource is temporarily unavailable",
    21:   "Resource is busy",
    22:   "File name is invalid (too long, or bad chars, or ...)",
    23:   "Unknown or unrecognized error",
    24:   "(bug -- invalid reason code)"        // corresponds to NUM_REASONS
    25: };
    26: 
    27: 
    28: STATICDEF char const *xSysError::
    29:   getReasonString(xSysError::Reason r)
    30: {
    31:   // at compile-time, verify consistency between enumeration and sm_string array
    32:   // (it's in here because, at least on Borland, the preprocessor respects
    33:   // the member access rules (strangely..))
    34:   #ifdef __BORLANDC__
    35:     #if TABLESIZE(reasonStrings) != NUM_REASONS+1
    36:       #error table and enumeration do not match
    37:     #endif
    38:   #endif
    39: 
    40:   if ((unsigned)r < NUM_REASONS) {
    41:     return reasonStrings[r];
    42:   }
    43:   else {
    44:     return reasonStrings[NUM_REASONS];
    45:   }
    46: }
    47: 
    48: 
    49: xSysError::xSysError(xSysError::Reason r, int sysCode, char const *sysReason,
    50:                      char const *syscall, char const *ctx)
    51:   : xBase(constructWhyString(r, sysReason, syscall, ctx)),
    52:     reason(r),
    53:     reasonString(getReasonString(r)),
    54:     sysErrorCode(sysCode),
    55:     sysReasonString(sysReason),
    56:     syscallName(syscall),
    57:     context(ctx)
    58: {}
    59: 
    60: 
    61: STATICDEF sm_string xSysError::
    62:   constructWhyString(xSysError::Reason r, char const *sysReason,
    63:                      char const *syscall, char const *ctx)
    64: {
    65:   xassert(syscall);
    66: 
    67:   // build sm_string; start with syscall that failed
    68:   sm_stringBuilder sb;
    69:   sb << syscall << ": ";
    70: 
    71:   // now a failure reason sm_string
    72:   if (r != R_UNKNOWN) {
    73:     sb << getReasonString(r);
    74:   }
    75:   else if ((sysReason != NULL) && (sysReason[0] != 0)) {
    76:     sb << sysReason;
    77:   }
    78:   else {
    79:     // no useful info, use the R_UNKNOWN sm_string
    80:     sb << getReasonString(r);
    81:   }
    82: 
    83:   // finally, the context
    84:   if (ctx != NULL) {
    85:     sb << ", " << ctx;
    86:   }
    87: 
    88:   return sb;
    89: }
    90: 
    91: 
    92: xSysError::xSysError(xSysError const &obj)
    93:   : xBase(obj),
    94:     reason(obj.reason),
    95:     reasonString(obj.reasonString),
    96:     sysErrorCode(obj.sysErrorCode),
    97:     sysReasonString(obj.sysReasonString),
    98:     syscallName(obj.syscallName),
    99:     context(obj.context)
   100: {}
   101: 
   102: 
   103: xSysError::~xSysError()
   104: {}
   105: 
   106: 
   107: STATICDEF void xSysError::
   108:   xsyserror(char const *syscallName, char const *context)
   109: {
   110:   // retrieve system error code
   111:   int code = getSystemErrorCode();
   112: 
   113:   // translate it into one of ours
   114:   sm_string sysMsg;
   115:   Reason r = portablize(code, sysMsg);
   116: 
   117:   // construct an object to throw
   118:   xSysError obj(r, code, sysMsg, syscallName, context);
   119: 
   120:   // toss it
   121:   THROW(obj);
   122: }
   123: 
   124: 
   125: sm_string sysErrorCodeString(int systemErrorCode, char const *syscallName,
   126:                                                char const *context)
   127: {
   128:   sm_string sysMsg;
   129:   xSysError::Reason r = xSysError::portablize(systemErrorCode, sysMsg);
   130:   return xSysError::constructWhyString(
   131:            r, sysMsg,
   132:            syscallName, context);
   133: }
   134: 
   135: 
   136: // ----------------------- Win32 code ------------------------------------
   137: #ifdef _WIN32
   138: 
   139: #ifdef USE_MINWIN_H
   140: #  include "minwin.h"   // api
   141: #else
   142: #  include <windows.h>  // api
   143: #endif
   144: #include <errno.h>      // errno
   145: 
   146: STATICDEF int xSysError::getSystemErrorCode()
   147: {
   148:   int ret = GetLastError();
   149: 
   150:   // update: The confusing behavior I observed was with the Borland 4.5
   151:   // runtime libraries.  When I linked with the non-multithreaded versions,
   152:   // GetLastError worked as expected.  But when I linked with the
   153:   // multithreaded versions, GetLastError always returned 0, and I had to
   154:   // consult errno instead.  Further, the errno values didn't coincide
   155:   // exactly with expected values.  Therefore, the solution (at least for
   156:   // now) is to link only with the non-multithreaded versions, and not
   157:   // look to errno for anything.
   158:   #ifdef MT
   159:   #  error something is fishy with multithreaded..
   160:   #endif
   161: 
   162:   // I thought something was happening, but now it seems
   163:   // it's not..
   164:   #if 0     // ?
   165:   if (ret == ERROR_SUCCESS) {
   166:     // for some calls, like mkdir, GetLastError is not
   167:     // set, but errno is; fortunately, MS decided to
   168:     // (mostly) overlap GetLastError codes with errno codes,
   169:     // so let's try this:
   170:     return errno;
   171:   }
   172:   #endif // 0
   173: 
   174:   return ret;
   175: }
   176: 
   177: 
   178: STATICDEF xSysError::Reason xSysError::portablize(int sysErrorCode, sm_string &sysMsg)
   179: {
   180:   // I'd like to put this into a static class member, but then
   181:   // the table would have to prepend R_ constants with xSysError::,
   182:   // which is a pain.
   183: 
   184:   // Q: how to get a sm_string from win32?
   185:   //if (sysMsg != NULL) {
   186:     sysMsg = NULL;
   187:   //}
   188: 
   189:   static struct S {
   190:     int code;
   191:     Reason reason;
   192:   } const arr[] = {
   193:     { ERROR_SUCCESS,           R_NO_ERROR          },
   194:     { ERROR_FILE_NOT_FOUND,    R_FILE_NOT_FOUND    },
   195:     { ERROR_PATH_NOT_FOUND,    R_PATH_NOT_FOUND    },
   196:     { ERROR_ACCESS_DENIED,     R_ACCESS_DENIED     },
   197:     { ERROR_NOT_ENOUGH_MEMORY, R_OUT_OF_MEMORY     },
   198:     { ERROR_OUTOFMEMORY,       R_OUT_OF_MEMORY     },
   199:     { ERROR_INVALID_BLOCK,     R_SEGFAULT          },
   200:     { ERROR_BAD_FORMAT,        R_FORMAT            },
   201:     { ERROR_INVALID_DATA,      R_INVALID_ARGUMENT  },
   202:     { ERROR_WRITE_PROTECT,     R_READ_ONLY         },
   203:     { ERROR_ALREADY_EXISTS,    R_ALREADY_EXISTS    },
   204:     // ???                     R_AGAIN
   205:     { ERROR_BUSY,              R_BUSY              },
   206:   };
   207: 
   208:   loopi(TABLESIZE(arr)) {
   209:     if (arr[i].code == sysErrorCode) {
   210:       // found it
   211:       return arr[i].reason;
   212:     }
   213:   }
   214: 
   215:   // I don't know
   216:   return R_UNKNOWN;
   217: }
   218: 
   219: 
   220: // ---------------------- unix ---------------------------
   221: #else      // unix
   222: 
   223: #include <errno.h>       // errno
   224: #include <cstring>      // std::strerror
   225: 
   226: // mappings to a set of error codes I can use below
   227: // (I am sure I've already done this somewhere else, but I
   228: // may have lost that file)
   229: #ifndef EZERO
   230: #  define EZERO 0
   231: #endif
   232: #ifndef ENOFILE
   233: #  define ENOFILE ENOENT
   234: #endif
   235: #ifndef ENOPATH
   236: #  define ENOPATH ENOENT
   237: #endif
   238: #ifndef EINVMEM
   239: #  define EINVMEM EFAULT
   240: #endif
   241: #ifndef EINVFMT
   242: #  define EINVFMT 0         // won't be seen because EZERO is first
   243: #endif
   244: 
   245: 
   246: STATICDEF int xSysError::getSystemErrorCode()
   247: {
   248:   return errno;          // why was this "errno()"??
   249: }
   250: 
   251: 
   252: STATICDEF xSysError::Reason xSysError::portablize(int sysErrorCode, sm_string &sysMsg)
   253: {
   254:   sysMsg = std::strerror(sysErrorCode);
   255:     // operator= copies to local storage
   256: 
   257:   static struct S {
   258:     int code;
   259:     Reason reason;
   260:   } const arr[] = {
   261:     { EZERO,        R_NO_ERROR          },
   262:     { ENOFILE,      R_FILE_NOT_FOUND    },
   263:     { ENOPATH,      R_PATH_NOT_FOUND    },
   264:     { EACCES,       R_ACCESS_DENIED     },
   265:     { ENOMEM,       R_OUT_OF_MEMORY     },
   266:     { EINVMEM,      R_SEGFAULT          },
   267:     { EINVFMT,      R_FORMAT            },
   268:     { EINVAL,       R_INVALID_ARGUMENT  },
   269:     { EROFS,        R_READ_ONLY         },
   270:     { EEXIST,       R_ALREADY_EXISTS    },
   271:     { EAGAIN,       R_AGAIN             },
   272:     { EBUSY,        R_BUSY              },
   273:     { ENAMETOOLONG, R_INVALID_FILENAME  },
   274:   };
   275: 
   276:   loopi(TABLESIZE(arr)) {
   277:     if (arr[i].code == sysErrorCode) {
   278:       // found it
   279:       return arr[i].reason;
   280:     }
   281:   }
   282: 
   283:   // I don't know
   284:   return R_UNKNOWN;
   285: }
   286: 
   287: 
   288: #endif  // unix
   289: 
   290: 
   291: // ------------------ test code ------------------------
   292: #ifdef TEST_SYSERR
   293: #include "sm_test.h"
   294: #include "sm_nonport.h"
   295: 
   296: // this is a macro because failingCall needs to be
   297: // call-by-name so I can wrap it in a try
   298: #define TRY_FAIL(failingCall, expectedCode)                       \
   299:   try {                                                           \
   300:     if (failingCall) {                                            \
   301:       std::cout << "ERROR: " #failingCall " should have failed\n";     \
   302:     }                                                             \
   303:     else {                                                        \
   304:       /* got an error to test */                                  \
   305:       xsyserror(#failingCall);                                    \
   306:     }                                                             \
   307:   }                                                               \
   308:   catch (xSysError &x) {                                          \
   309:     if (x.reason != xSysError::expectedCode) {                    \
   310:       std::cout << "ERROR: " #failingCall " returned '"                \
   311:            << x.reasonString << "' but '"                         \
   312:            << xSysError::getReasonString(xSysError::expectedCode) \
   313:            << "' was expected\n";                                 \
   314:       errors++;                                                   \
   315:     }                                                             \
   316:   }
   317: 
   318: void entry()
   319: {
   320:   int errors = 0;
   321:   xBase::logExceptions = false;
   322:   //nonportFail = xSysError::xsyserror;
   323: 
   324:   //TRY_FAIL(createDirectory("/tmp\\Scott\\"),
   325:   //         R_ALREADY_EXISTS);
   326: 
   327:   TRY_FAIL(changeDirectory("some.strange.name/yadda"),
   328:            R_PATH_NOT_FOUND);
   329: 
   330:   TRY_FAIL(createDirectory("/tmp"),
   331:            R_ALREADY_EXISTS);
   332: 
   333:   TRY_FAIL(isDirectory("doesnt.exist"),
   334:            R_FILE_NOT_FOUND);
   335: 
   336:   if (errors == 0) {
   337:     std::cout << "success!\n";
   338:   }
   339:   else {
   340:     std::cout << errors << " error(s)\n";
   341:   }
   342: }
   343: 
   344: USUAL_MAIN
   345: 
   346: #endif // TEST_SYSERR
   347: 
End cpp section to elk/sm_syserr.cpp[1]
Start cpp section to elk/sm_warn.cpp[1 /1 ]
     1: #line 18230 "./lpsrc/sm.pak"
     2: // warn.cc            see license.txt for copyright and terms of use
     3: // code for warn.h
     4: // Scott McPeak, 1999  This file is public domain.
     5: 
     6: #include "sm_warn.h"
     7: #include "sm_typ.h"
     8: #include "sm_breaker.h"
     9: #include <stdio.h>      // fopen, stderr, etc.
    10: #include <time.h>       // time, ctime
    11: 
    12: // globals
    13: WarningHandler warningHandler = defaultWarningHandler;
    14: #ifdef NDEBUG
    15:   WarnLevel logWarnLevel = (WarnLevel)(WARN_ALL - WARN_DEBUG);
    16:   WarnLevel displayWarnLevel = WARN_NONE;
    17: #else
    18:   WarnLevel logWarnLevel = WARN_ALL;
    19:   WarnLevel displayWarnLevel = WARN_ALL;
    20: #endif
    21: 
    22: 
    23: void warning(WarnLevel level, char const *message)
    24: {
    25:   warningHandler(level, message);
    26: }
    27: 
    28: 
    29: void defaultWarningHandler(WarnLevel level, char const *message)
    30: {
    31:   if (level & WARN_DEBUG) {
    32:     // hit a breakpoint if the debugger is attached
    33:     breaker();
    34:   }
    35: 
    36:   if (level & logWarnLevel) {
    37:     defaultWarningLogger(level, message);
    38:   }
    39: 
    40:   if (level & logWarnLevel) {
    41:     defaultWarningPrinter(level, message);
    42:   }
    43: }
    44: 
    45: 
    46: void defaultWarningLogger(WarnLevel /*level*/, char const *message)
    47: {
    48:   static FILE *logfile = NULL;
    49:   static bool failedToOpen = false;
    50: 
    51:   if (!logfile && !failedToOpen) {
    52:     logfile = fopen("warning.log", "a");
    53:     if (!logfile) {
    54:       // don't keep trying
    55:       failedToOpen = true;
    56:     }
    57:     else {
    58:       // start with a timestamp
    59:       time_t t;
    60:       time(&t);
    61:       int len = fprintf(logfile, "\nLog started at %s", ctime(&t));
    62:         // note: astonishingly (bad), the sm_string returned by ctime() has
    63:         //       a newline at the end!
    64: 
    65:       while (len--) {
    66:         fprintf(logfile, "-");
    67:       }
    68:       fprintf(logfile, "\n");
    69:     }
    70:   }
    71: 
    72:   if (logfile) {
    73:     // append the message to the logfile
    74:     fprintf(logfile, "warning: %s\n", message);
    75:     fflush(logfile);
    76:   }
    77: }
    78: 
    79: 
    80: void defaultWarningPrinter(WarnLevel /*level*/, char const *message)
    81: {
    82:   fprintf(stderr, "warning: %s\n", message);
    83:   fflush(stderr);
    84: }
    85: 
    86: 
    87: // no test code because it is my judgment that bugs in this
    88: // module will be easily evident, and it is a very simple
    89: // module, so it isn't worth it to separately test
    90: 
End cpp section to elk/sm_warn.cpp[1]