home *** CD-ROM | disk | FTP | other *** search
/ Graphics 16,000 / graphics-16000.iso / msdos / plotting / pcgplots / basicio.cpp next >
C/C++ Source or Header  |  1992-04-24  |  28KB  |  1,163 lines

  1. // C++ .cc file for gplot, gdoc, gtex basic I/O -*-c++-*-
  2.  
  3. // copyright Phil Andrews, Pittsburgh Supercomputing Center, 1992
  4. // all rights reserved
  5. #ifdef macintosh
  6. #  include <errors.h>
  7. #endif
  8. #if  __MSDOS__
  9. #include <windows.h>
  10. #include <io.h>
  11. #endif
  12. #include <ctype.h>
  13. #include <stdio.h>
  14. #include <values.h>
  15. #include "basicio.h"
  16. #ifndef _toupper
  17. #define _toupper(c) ((c) -'a'+'A')
  18. #endif
  19. ////
  20. // error call
  21. ////
  22. extern void myError(const char *inMsg, const char *inMsg2=NULL,
  23.              int severity=1);
  24. #ifdef macintosh
  25.  #pragma segment CIO1
  26. #endif
  27. ////
  28. // basic file
  29. ////
  30. basicFile::basicFile()
  31. {
  32.   filePtr = NULL;
  33. #if __MSDOS__
  34.   hGlobalBuffer = NULL;
  35.   bufferSize = 65000; // first try, may have to expand
  36.   hGlobalBuffer = GlobalAlloc(GMEM_FIXED,65000);
  37.   if (hGlobalBuffer)
  38.       buffer = (HugePt)GlobalLock(hGlobalBuffer);
  39.   else buffer = NULL;
  40. #else
  41.   bufferSize = 1024; // first try, may have to expand
  42.   buffer = new unsigned char[bufferSize]; // initial allocation of memory
  43. #endif
  44.   tempBufferSize = 0; // not needed yet
  45.   tempBufferIndex = 0;
  46.   tempBuffer = NULL;
  47. }
  48. ////
  49. // destructor
  50. ////
  51. basicFile::~basicFile()
  52. {
  53.   if (filePtr) fclose(filePtr);
  54. #if __MSDOS__
  55.   if (hGlobalBuffer)
  56.       {
  57.       GlobalUnlock(hGlobalBuffer);
  58.       GlobalFree(hGlobalBuffer);
  59.       buffer = NULL;
  60.       }
  61. #endif
  62.  
  63.   if (buffer)    delete buffer;
  64. }
  65. ////
  66. // need more memory, double the buffer size
  67. ////
  68. int basicFile::doubleBuffer(long bIndex)
  69. {
  70. #if  __MSDOS__
  71.   bufferSize += bufferSize;
  72.   HANDLE hGlobalMemoryNew =
  73.         GlobalReAlloc(hGlobalBuffer, bufferSize, GMEM_MOVEABLE);
  74.   if (!hGlobalMemoryNew)
  75.       {
  76.       myError("couldn't double buffer size\n", "aborting", 0);
  77.       return 0;
  78.       }
  79.  
  80.   hGlobalBuffer = hGlobalMemoryNew;
  81.   buffer = GlobalLock(hGlobalBuffer);
  82.   return 1;
  83. #else
  84.   unsigned char * oldBuffer;
  85.   oldBuffer = buffer;
  86.   buffer = new unsigned char[bufferSize = 2 * bufferSize]; // double memory
  87.   if (!buffer) {
  88.      myError("couldn't double buffer size\n", "aborting", 0);
  89.      return 0;
  90.   }
  91.   for (long i=0; i<bIndex; ++i)
  92.      buffer[i] = oldBuffer[i]; // copy old data
  93.   delete oldBuffer;
  94.   return 1;
  95. #endif
  96. }
  97. ////
  98. // output a string
  99. ////
  100. int basicOutput::outS(const char *inStr)
  101. {
  102.   if (!inStr) return 1;
  103.   for (int myLen = 0; inStr[myLen]; ++myLen);
  104.   return outS(inStr, myLen);
  105. }
  106. ////
  107. // general input
  108. ////
  109. int memInput::getBytes(unsigned int noBytes, unsigned char *inPtr)
  110. {
  111.   for (int i=0; (i<noBytes) && (i<mySize); ++i) inPtr[i] = myMem[myIndex++];
  112.   return i;
  113. }
  114.  
  115. ////
  116. // basic file input
  117. ////
  118. // constructor when may need to open the file
  119. ////
  120. #ifdef macintosh
  121. basicInput::basicInput(const char *, short ref_num)
  122.   {
  123.   filePtr = NULL;
  124.   frefHand = ref_num;
  125.   FailOSErr(SetFPos(frefNum, fsFromStart, 0));  // get to beginning
  126.   aIndex = bIndex = 0;
  127.   myLastPos = 0;
  128. }
  129. #elif __MSDOS__
  130. basicInput::basicInput(const char *, short handle)
  131.   {
  132.     filePtr = NULL;
  133.     frefHand = handle;
  134.     lseek( frefHand, 0L,SEEK_SET);   // get to beginning
  135.     aIndex = bIndex = 0;
  136.     myLastPos = 0;
  137. }
  138. #else
  139. basicInput::basicInput(const char *inName, short  )
  140.   {
  141.   filePtr = fopen((char *) inName, "r");
  142.   aIndex = bIndex = 0;
  143.   myLastPos = 0;
  144. }
  145. #endif
  146. ////
  147. // constructor for already opened file
  148. ////
  149.  
  150. ////
  151. // constructor for already opened file
  152. ////
  153. #ifdef macintosh
  154. basicInput::basicInput(FILE * , short ref_num, int offset)
  155.   {
  156.   filePtr = NULL;
  157.   frefHand = ref_num;
  158.   FailOSErr(SetFPos(frefNum, fsFromStart, offset));  // get to right spot
  159.   aIndex = bIndex = 0;
  160. }
  161. #elif __MSDOS__
  162. basicInput::basicInput(FILE *, short handle, int offset )
  163.     {
  164.     filePtr = NULL ;
  165.     frefHand = handle;
  166.     lseek( frefHand, (long)offset, SEEK_SET);   // get to beginning
  167.     aIndex = bIndex = 0;
  168. }
  169. #else
  170. basicInput::basicInput(FILE *inPtr, short  , int offset)
  171.   {
  172.   filePtr = inPtr;
  173.   goTo(offset);
  174.   aIndex = bIndex = 0;
  175. }
  176. #endif
  177. ////
  178. // read in some bytes to an arbitrary location
  179. ////
  180. #if __MSDOS__
  181. int basicInput::getBytes(unsigned int noBytes, HugePt location)
  182. #else
  183. int basicInput::getBytes(unsigned int noBytes, unsigned char *location)
  184. #endif
  185. {
  186.   if (tempBuffer) { // read out of tempBuffer
  187.      for (int i=0; (i<noBytes) && (tempBufferIndex < tempBufferSize); ++i) {
  188.         location[i] = tempBuffer[tempBufferIndex++];
  189.      }
  190.      return i;
  191.   } else { // really get from file
  192. #ifdef macintosh
  193.      long bW = noBytes;
  194.      int ret = FSRead(frefNum, &bW, (Ptr) location);
  195.      if (noBytes == bW) return 1;
  196.      else if (ret == eofErr) myError("premature end of file");
  197.      return 0;
  198. #elif __MSDOS__
  199.     if (location == NULL) { myError("Memory Allocation Problem"); return 0; }
  200.     unsigned char * FarPtr;
  201.     FarPtr = location;
  202.     int ret = read(frefHand, FarPtr, noBytes);
  203.     if ( noBytes == ret)
  204.         return 1;
  205.     else if (ret >= 0)  myError("premature end of file");
  206.     return 0;
  207. #else
  208.      if (fread((char *) location, 1, noBytes, filePtr) == noBytes) return 1;
  209.      else if (feof(filePtr))  myError("premature end of file");
  210.      else if (ferror(filePtr)) myError("error on read");
  211.      return 0;
  212. #endif
  213.   }
  214. }
  215. ////
  216. // basic output
  217. ////
  218. char tmpDigits[]  = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
  219. char *basicFile::digits = tmpDigits;
  220. ////
  221. // constructor
  222. ////
  223. #ifdef macintosh
  224. basicOutput::basicOutput(const char * , short ref_num)
  225.   {
  226.   filePtr = NULL;
  227.   frefHand = ref_num;
  228.   FailOSErr(SetFPos(frefNum, fsFromStart, 0));  // get to beginning
  229.   lineSize = 80;
  230.   lineIndex = 0;
  231.   lastSep = 0;
  232. }
  233. #elif __MSDOS__
  234. basicOutput::basicOutput(const char * ,short handle)
  235.   {
  236.   filePtr = NULL;
  237.   frefHand = handle;
  238.   lseek( frefHand, 0L,SEEK_SET);   // get to beginning
  239.   lineSize = 80;
  240.   lineIndex = 0;
  241.   lastSep = 0;
  242. }
  243. #else
  244. basicOutput::basicOutput(const char *inName, short  )
  245.   {
  246.   filePtr = fopen((char *) inName, "w");
  247.   lineSize = 80;
  248.   lineIndex = 0;
  249.   lastSep = 0;
  250. }
  251. #endif
  252. int basicOutput::outSep() // output one separator (if necessary)
  253. {
  254.   if (!lineIndex) return 1; // at beginning of line
  255.   if (lineIndex >= lineSize) return flushLine(); // at end of line
  256.   lastSep = lineIndex;
  257.   buffer[lineIndex++] = ' ';
  258.   return 1;
  259. }
  260. int basicOutput::outS(const char *inStr, int inLen) // output a bare string
  261. {
  262.   int i;
  263.   // too big for what's left in line ?
  264.   if (((inLen + lineIndex) > lineSize) && ((inLen + lastSep) <= lineSize))
  265.      flushLine(1);
  266.   ////
  267.   // too big for line since last separator ?
  268.   if ((inLen + lineIndex) > lineSize) {
  269.     flushLine();
  270.   }
  271.   // still too big for whole line ? Try to break at spaces
  272.   if (inLen > lineSize) {
  273.     int lastSpace = 0, lastStart = 0;
  274.     for (i=0; i<inLen; ++i) {
  275.       if (isspace(inStr[i])) lastSpace = i; // mark last space
  276.         if (((i-lastStart) >= lineSize) && (lastSpace > lastStart)) {
  277.     for (; lastStart < lastSpace; ++lastStart)
  278.       buffer[lineIndex++] = inStr[lastStart];
  279.     flushLine();
  280.     lastSpace = 0;
  281.       }
  282.     }
  283.     // any left ?
  284.     if (lastStart < inLen) {
  285.       putBytes((unsigned char *) inStr + lastStart, inLen - lastStart);
  286.       flushLine();
  287.     }
  288.     return 1;
  289.   }
  290.   // will fit on one line
  291.   for (i=0; i<inLen; ++i) buffer[lineIndex++] = inStr[i];
  292.   return 1;
  293. }
  294. int basicOutput::flushLine(int breakLine)
  295. {
  296.   int ret;
  297.   if (breakLine && lastSep) { // make some room, if possible
  298.      buffer[lastSep] = '\n';
  299.      ++lastSep; // skip over separator
  300.      ret = putBytes(lastSep);
  301.     for (int i=0; i<(lineIndex - lastSep ); ++i) buffer[i] = buffer[i+lastSep];
  302.     lineIndex = i;
  303.     lastSep = 0;
  304.   } else { // no room, or want to really flush
  305.     if (lastSep == (lineIndex - 1)) --lineIndex; // last char is a separator
  306.     if (lineIndex) { // started a line
  307.       buffer[lineIndex++] = '\n';
  308.       ret = putBytes(lineIndex);
  309.     } else ret = 1;
  310.     lineIndex = 0;
  311.     lastSep = 0;
  312.   }
  313.   return ret;
  314. }
  315. ////
  316. // only routine to do actual output
  317. ////
  318. #if __MSDOS__
  319. int basicOutput::putBytes(HugePt location, int noBytes)
  320. #else
  321. int basicOutput::putBytes(unsigned char *location, int noBytes)
  322. #endif
  323. {
  324. #ifdef macintosh
  325.   long bW = noBytes;
  326.   int ret = FSWrite(frefNum, &bW,(Ptr)location);
  327.   if (noBytes == bW) return 1;
  328.   else  myError("error on write");
  329.   return 0;
  330. #elif __MSDOS__
  331.   if (location == NULL) { myError("Memory Allocation Problem"); return 0; }
  332.   int ret = write(frefHand, location, noBytes);
  333.   if (noBytes == ret) return 1;
  334.   else myError("error on write");
  335.   return 0;
  336. #else
  337.   if (fwrite((char *) location, 1, noBytes, filePtr) == noBytes) return 1;
  338.   else if (ferror(filePtr)) myError("error on write");
  339.   return 0;
  340. #endif
  341. }
  342. ////
  343. // put a real number in text format
  344. ////
  345. int basicOutput::textRealOut(float inFloat)
  346. {
  347.   const int noDecimals = 6;
  348.   int intPart, ret;
  349.   float rest;
  350.   intPart = (int) inFloat; // assume truncation toward zero
  351.   rest = (inFloat < 0) ? intPart - inFloat : inFloat - intPart;
  352.   ret = outSep() && textIntOut(intPart, 1) && outC('.');
  353.   for (int i = 0; (i<noDecimals) && rest; ++i) {
  354.      rest *= 10;
  355.     intPart = (int) rest;
  356.     ret = ret && textIntOut(intPart, 1);
  357.     rest -= intPart;
  358.   }
  359.   return ret;
  360. }
  361. int basicOutput::textIntOut(int i, int noSep) // output an integer
  362. {
  363.   const int maxPwrs = 10; // maximum number size
  364.   char intBuffer[maxPwrs + 2];
  365.   char *cptr = intBuffer + maxPwrs + 1;
  366.   int isNeg = 0, j;
  367.  
  368.   *cptr = 0; // end of string
  369.   if (i < 0) { // negative
  370.     if (!(i << 1)) { // special case for most negative no., assume 2's comp
  371.       *--cptr = digits[8];
  372.       i /= 10;
  373.     }
  374.     isNeg = 1;
  375.     i = -i;
  376.   } else if (!i) *--cptr = digits[0];
  377.  
  378.   while (i && ((cptr + isNeg) > intBuffer)) {
  379.     j = i % 10;
  380.     *--cptr = digits[j];
  381.     i /= 10;
  382.   }
  383.   if (i) myError("too large int to output");
  384.   if (isNeg) *--cptr = '-';
  385.  
  386. #ifdef macintosh
  387.    int ret =   ( noSep ? 1 : outSep() );
  388.    if (ret)    outS(cptr, intBuffer + maxPwrs + 1 - cptr);
  389.  return ret;
  390. #else
  391.   return (noSep ? 1 : outSep()) && outS(cptr, intBuffer + maxPwrs + 1 - cptr);
  392. #endif
  393. }
  394. ////
  395. // make a hex dump
  396. ////
  397. int basicOutput::textHexDump(const unsigned char *inPtr, int size)
  398. {
  399.   int ret = 1;
  400.   for (int i=0; (i<size) && ret; ++i) ret = textHexOut(inPtr[i]);
  401.   return ret;
  402. }
  403. ////
  404. // output one hex character
  405. ////
  406. int basicOutput::textHexOut(unsigned char inC)
  407. {
  408. static char hexChar[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  409.                'A', 'B', 'C', 'D', 'E', 'F'};
  410.   return outC(hexChar[15 & (inC >> 4)]) && outC(hexChar[15 & inC]);
  411. }
  412. ////
  413. // File I/O
  414. ////
  415. ////
  416. // the basic text object, merely do something reasonable for these
  417. ////
  418. textObject::textObject()
  419. {
  420.   myVpos = 0;
  421.   maxCurrentFontNameSize = 127;
  422.   myCurrentFontName = new char[maxCurrentFontNameSize + 1];
  423.   myCurrentFontName[0] = 0;
  424.   myCurrentFontNameSize = 0;
  425.   leftMarginPtr = new posPtr(0);
  426.   rightMarginPtr = new posPtr(0);
  427.   hposPtr = new posPtr(0);
  428.   vposPtr = new posPtr(0);
  429.   myHpos = leftMargin();
  430. }
  431. ////
  432. // destructor
  433. ////
  434. textObject::~textObject()
  435. {
  436.   posPtr *oldPtr;
  437.   // delete the stacks of pointers
  438.   while (oldPtr = leftMarginPtr) {
  439.      leftMarginPtr = leftMarginPtr->next();
  440.     delete oldPtr;
  441.   }
  442.   while (oldPtr = rightMarginPtr) {
  443.     rightMarginPtr = rightMarginPtr->next();
  444.     delete oldPtr;
  445.   }
  446.   while (oldPtr = hposPtr) {
  447.     hposPtr = hposPtr->next();
  448.     delete oldPtr;
  449.   }
  450.   while (oldPtr = vposPtr) {
  451.     vposPtr = vposPtr->next();
  452.     delete oldPtr;
  453.   }
  454. }
  455. ////
  456. // handle pointers
  457. ////
  458. void textObject::pushLeftMargin()
  459. {
  460.   leftMarginPtr = new posPtr(hpos(), leftMarginPtr);
  461. }
  462. int textObject::popLeftMargin()
  463. {
  464.   if (leftMarginPtr->next()) {
  465.      posPtr *oldPtr = leftMarginPtr;
  466.      leftMarginPtr = leftMarginPtr->next();
  467.     delete oldPtr;
  468.     return 1;
  469.   } else {
  470.     return 0;
  471.   }
  472. }
  473. void textObject::pushHpos()
  474. {
  475.   hposPtr = new posPtr(myHpos, hposPtr);
  476. }
  477. int textObject::popHpos()
  478. {
  479.   if (hposPtr) {
  480.     myHpos = hposPtr->value();
  481.     if (hposPtr->next()) {
  482.       posPtr *oldPtr = hposPtr;
  483.       hposPtr = hposPtr->next();
  484.       delete oldPtr;
  485.       return 1;
  486.     } else return 0;
  487.   } else return 0;
  488. }
  489. void textObject::pushRightMargin()
  490. {
  491.   rightMarginPtr = new posPtr(hpos(), rightMarginPtr);
  492. }
  493. int textObject::popRightMargin()
  494. {
  495.   if (rightMarginPtr->next()) {
  496.     posPtr *oldPtr = rightMarginPtr;
  497.     rightMarginPtr = rightMarginPtr->next();
  498.     delete oldPtr;
  499.     return 1;
  500.   } else return 0;
  501. }
  502. void textObject::pushVpos()
  503. {
  504.   vposPtr = new posPtr(myVpos, vposPtr);
  505. }
  506. int textObject::popVpos()
  507. {
  508.   if (vposPtr) {
  509.     myVpos = vposPtr->value();
  510.     if (vposPtr->next()) {
  511.       posPtr *oldPtr = vposPtr;
  512.       vposPtr = vposPtr->next();
  513.       delete oldPtr;
  514.       return 1;
  515.     } else return 0;
  516.   } else return 0;
  517. }
  518. ////
  519. // put out a space
  520. ////
  521. textObject *textObject::outSpace()
  522. {
  523.   int spaceWidth = stringWidth(" ", 1);
  524.   if ((hpos() + spaceWidth) >= pageWidth()) newLine();
  525.   else myHpos += spaceWidth;
  526.   return this;
  527. }
  528. ////
  529. // output a new line
  530. ////
  531. textObject *textObject::newLine(int noLines)
  532. {
  533.   myHpos = leftMargin();
  534.   myVpos += noLines * lineHeight();
  535.   return this;
  536. }
  537. ////
  538. // output at least one blank line
  539. ////
  540. textObject *textObject::blankLine()
  541. {
  542.   if (hpos() != leftMargin()) newLine(2);
  543.   else newLine();
  544.   return this;
  545. }
  546. ////
  547. // ensure we're at the begining of a new line
  548. ////
  549. textObject *textObject::endLine()
  550. {
  551.   if (hpos() != leftMargin()) newLine();
  552.   return this;
  553. }
  554. ////
  555. // break up a string of tokens into left justified set
  556. ////
  557. textObject *textObject::breakLeft(tokenHolder *inHolder)
  558. {
  559.   outputToken *myToken = inHolder->start(), *oldToken, *bestBreak;
  560.   // store our present horizontal position
  561.   pushHpos();
  562.   // go thru all of the tokens
  563.   while (myToken) {
  564.     myHpos = leftMargin(); // start new line
  565.     bestBreak = myToken; // initialize possible break 
  566.     // take care of leading deletable tokens
  567.     while (myToken && (myToken->deletable() || !myToken->width(this))) {
  568.       if (myToken->deletable()) {
  569.     oldToken = myToken;
  570.     myToken = myToken->next();
  571.     delete oldToken;
  572.       } else myToken = myToken->next();
  573.     }
  574.     if (!myToken) break; // done
  575.     // now into real stuff, get one line's worth
  576.     while (myToken && (myHpos <= pageWidth()) && !myToken->newLine()) {
  577.       if (myToken->breakPenalty(this) < 1) { // good place to break
  578.     bestBreak = myToken; // latest one
  579.       }
  580.       myHpos += myToken->width(this);
  581.       myToken = myToken->next();
  582.     }
  583.     // reached end of line
  584.     if (myHpos > pageWidth()) { // need to insert a new line
  585.       myToken = new outputNL(bestBreak); // insert a break
  586.     }
  587.     if (myToken) myToken = myToken->next(); // first of new line
  588.   } // ran out of tokens
  589.   // restore our original horizontal position
  590.   popHpos();
  591.   return this;
  592. }
  593. ////
  594. // break up a string of tokens into a centered set
  595. ////
  596. textObject *textObject::breakCenter(tokenHolder *inHolder)
  597. {
  598.   outputToken *myToken, *nextToken, *bestBreak, *startToken;
  599.   int width, bestWidth;
  600.  
  601.   myToken = inHolder->start();
  602.   while (myToken) {
  603.     while (myToken && (myToken->deletable() || !myToken->width(this))) {
  604.       nextToken = myToken->next();
  605.       // delete leading spaces, skip zero width elements 
  606.       if (myToken->deletable()) delete myToken;
  607.       myToken = nextToken;
  608.     }
  609.     if (!myToken) break; // done
  610.     width = 0; // start new line
  611.     startToken = myToken; // kern before startToken
  612.     bestBreak = myToken; // break at bestBreak
  613.     bestWidth = myToken->width(this);
  614.  
  615.     while (myToken && (width <= pageWidth())) { // do one line
  616.       if (!myToken->breakPenalty(this)) { // can break here
  617.     bestBreak = myToken; // latest one
  618.     bestWidth = (myToken->deletable()) ? width
  619.       : width + myToken->width(this);
  620.       }
  621.       width += myToken->width(this);
  622.       myToken = myToken->next();
  623.     } // end of line
  624.  
  625.     if (width > pageWidth()) { // need to break the line
  626.       myToken = new outputNL(bestBreak); // insert a break
  627.       myToken = myToken->next(); // first of new line
  628.     } else bestWidth = width; // last line
  629.  
  630.     new outputKern((pageWidth() - bestWidth) / 2, startToken);
  631.   }
  632.   return this;
  633. }
  634. ////
  635. // break up a string of tokens into a right justified set
  636. ////
  637. textObject *textObject::breakRight(tokenHolder *inHolder)
  638. {
  639.   return breakLeft(inHolder); // for now
  640. }
  641. ////
  642. // break up a string of tokens into a left and right justified set
  643. ////
  644. textObject *textObject::breakBoth(tokenHolder *inHolder)
  645. {
  646.   return breakLeft(inHolder); // for now
  647. }
  648. ////
  649. // no justification
  650. ////
  651. textObject *textObject::breakNone(tokenHolder*)
  652. {
  653.   return this;
  654. }
  655. ////
  656. // the basic output object
  657. ////
  658. outputObject *outputObject::outString(const char*, int, int, int)
  659. {
  660.   return this; // nothing by default
  661. }
  662. outputObject* outputObject::outString(const char *buffer)
  663. {
  664.   return outString(buffer, strlen(buffer));
  665. }
  666. ////
  667. // output a character
  668. ////
  669. outputObject* outputObject::outChar(char c)
  670. {
  671.   return outString(&c, 1);
  672. }
  673. ////
  674. // the basic file output
  675. ////
  676. fileOutput::fileOutput(const char *file_name, int inSize)
  677. {
  678.   if (!(fout = fopen(file_name, "w"))) {
  679.     myError("couldn't open for output", file_name);
  680.   }
  681.   bufferSize = inSize;
  682.   lineBuffer = new char[bufferSize];
  683.   for (int i=0; i<bufferSize; ++i) lineBuffer[i] = ' ';
  684.   bufferIndex = 0;
  685. }
  686. ////
  687. // destructor
  688. ////
  689. fileOutput::~fileOutput()
  690. {
  691.   if (bufferIndex) flushBuffer();
  692.   delete lineBuffer;
  693.   if (fout) fclose(fout);
  694. }
  695. ////
  696. // output a string
  697. ////
  698. outputObject *fileOutput::outString(const char *buffer, int size,
  699.                     int useInWidth, int inWidth)
  700. {
  701.   long i;
  702.   if (bufferIndex < 0) bufferIndex = 0; // safety
  703.  
  704.   // will it fit on next line, but not this ?
  705.   if ((size<=bufferSize) && (size > (bufferSize - bufferIndex))) {
  706.     flushBuffer();
  707.   }
  708.   // too big for a whole line ? do as well as we can
  709.   while (size > bufferSize) {
  710.     // find space closest to end of line, if it exists
  711.     for (i=bufferSize; (i>0) && !isspace(buffer[i-1]); --i);
  712.     if (i) { // found a space
  713.       if (i) outString(buffer, (int) (i-1), useInWidth, inWidth);
  714.       buffer += i;
  715.       size -= (int) i;
  716.     } else {
  717.       outString(buffer, (int) bufferSize, useInWidth, inWidth);
  718.         buffer += bufferSize;
  719.       size -= (int) bufferSize;
  720.     }
  721.     flushBuffer();    
  722.   }
  723.   // will fit OK
  724.   for (i = 0; (i < size) && ((bufferIndex + i) < bufferSize)
  725.        && (buffer[i] != '\n'); ++i) {
  726.     lineBuffer[bufferIndex + i] = buffer[i];
  727.   }
  728.   bufferIndex += (useInWidth) ? inWidth : size;
  729.   return this;
  730. }
  731. ////
  732. // flush the output buffer and (possibly) add blank lines
  733. ////
  734. outputObject *fileOutput::flushBuffer(int noLines)
  735. {
  736.   if (fout) {
  737.     if (bufferIndex > 0) {
  738.       if (bufferIndex > bufferSize) bufferIndex = bufferSize;
  739.       fwrite(lineBuffer, 1, (unsigned int) bufferIndex, fout);
  740.     }
  741.     for (int i=0; i<noLines; ++i) fputc('\n', fout);
  742.   }
  743.   bufferIndex = 0;
  744.   for (int i=0; i<bufferSize; ++i) lineBuffer[i] = ' ';
  745.   return this;
  746. }
  747. ////
  748. // add a possible space
  749. ////
  750. outputObject *fileOutput::addSpace()
  751. {
  752.   if (bufferIndex <= 0) return this; // EOL counts
  753.   if (bufferIndex >= (bufferSize - 1)) flushBuffer(); // at end anyway
  754.   else if (lineBuffer[bufferIndex-1] == ' ') return this; // already a space
  755.   else ++bufferIndex; // already filled with blanks
  756.   return this;
  757. }
  758. ////
  759. // basic string output
  760. ////
  761. ////
  762. // output a string
  763. ////
  764. outputObject *stringOutput::outString(const char *inBuffer, int inSize,
  765.                       int useInWidth, int inWidth)
  766. {
  767.   if (inSize < 1) return this; // nothing to do
  768.   int i;
  769.   // how much more room do we need ?
  770.   int spaceNeeded = (useInWidth && (inWidth > inSize)) ? inWidth + 1
  771.     : inSize + 1;
  772.   ////
  773.   // do we have enough room ?
  774.   if (spaceNeeded > (mySize - lastPos)) { // must make more room
  775.     char *newContents = new char[mySize *= 2];
  776.     for (i=0; i<lastPos; ++i) newContents[i] = myContents[i];
  777.     delete myContents;
  778.     myContents = newContents;
  779.   }
  780.   ////
  781.   // copy over the input
  782.   for (i=0; i<inSize; ++i) myContents[lastPos++] = inBuffer[i];
  783.   if (useInWidth && (inWidth > inSize))
  784.     for (i=0; i<(inWidth - inSize); ++i) myContents[lastPos++] = ' ';
  785.   // terminate
  786.   myContents[lastPos] = 0;
  787.   return this;
  788. }
  789. ////
  790. // output to an array
  791. ////
  792. textObject *outputArray::newLine(int noLines)
  793. {
  794.   for (int i=0; i<noLines; ++i) outString("\n", 1);
  795.   return this;
  796. }
  797. ////
  798. // draw a string
  799. ////
  800. textObject *outputFile::drawString(const char *inStr, int size,
  801.                  int useInWidth, int inWidth)
  802. {
  803.   int useSize = (size >= 0) ? size : strlen(inStr);
  804.   outString(inStr, useSize, useInWidth, inWidth);
  805.   return this;
  806. }
  807. ////
  808. // output  new line(s)
  809. ////
  810. textObject *outputFile::newLine(int no_lines)
  811. {
  812.   flushBuffer(no_lines);
  813.   return this;
  814. }
  815. ////
  816. // new line output token
  817. ////
  818. // constructor to replace token
  819. ////
  820. outputNL::outputNL(outputToken *inToken, int inBefore)  : outputToken() 
  821. {
  822.   myNoLines = 1;
  823.   if (!inToken) return; // no links to make
  824.   myOwner = inToken->owner();
  825.   // if inToken is deletable, the outputNL takes its place;
  826.   if (inToken->deletable()) {   
  827.     myPrev = inToken->prev();
  828.     myNext = inToken->next();
  829.     inToken->unlink(); // so as not to screw up pointers when deleting
  830.     delete inToken;
  831.   } else {
  832.     if (inBefore) { // insert before
  833.       myNext = inToken;
  834.       myPrev = inToken->prev();
  835.     } else {  // insert afterwards
  836.       myNext = inToken->next();
  837.       myPrev = inToken;
  838.     }
  839.   }
  840.   // relink next and previous
  841.   if (myPrev) myPrev->setNext(this);
  842.   if (myNext) myNext->setPrev(this);
  843. }
  844. ////
  845. // simple constructor
  846. ////
  847. outputNL::outputNL(int inNoLines)  : outputToken() 
  848. {
  849.   myNoLines = inNoLines;
  850. }
  851. ////
  852. // output token
  853. ////
  854. // contructor (compiler problems if inlined ?)
  855. ////
  856.  outputToken::outputToken()
  857. {
  858.   myNext = NULL;
  859.   myPrev = NULL;
  860.   myWidth = 0;
  861.   myOwner = NULL;
  862. }
  863. ////
  864. // destructor
  865. ////
  866. outputToken::~outputToken()
  867. {
  868.   if (myOwner) {
  869.     if (this == myOwner->start()) { // realign start
  870.       myOwner->newStart(next());
  871.     }
  872.     if (this == myOwner->end()) { // realign end
  873.       myOwner->newEnd(prev());
  874.      }
  875.   }
  876.   if (myPrev) myPrev->setNext(next()); // relink
  877.   if (myNext) myNext->setPrev(prev());
  878. }
  879. ////
  880. // the String token
  881. ////
  882. // string constructor
  883. // if inSize will fit in the ptr/array union, use that
  884. // use default outputToken constructor (compiler problems ?)
  885. ////
  886. outputString::outputString(const char *inPtr, int inSize,
  887.                outputToken *inToken)
  888. {
  889.   myPrev = inToken;
  890.   if (((size = inSize) <= 0) || !inPtr) {   // nothing to do
  891.      size = 0;
  892.      return;
  893.   }
  894.   // else a legitimate string
  895.   int i;
  896.   if (size > sizeof(char*)) {
  897.      if (!(myUnion.bptr = new char[size])) {
  898.         fprintf(stderr, "couldn't make %d bytes for outputString\n", size);
  899.       size = 0;
  900.       return;
  901.     } else for (i=0; i<size; ++i) myUnion.bptr[i] = inPtr[i];
  902.   } else for (i=0; i<size; ++i) myUnion.barray[i] = inPtr[i];
  903. }
  904. ////
  905. // character constructor
  906. ////
  907. outputString::outputString(const char inChar, outputToken *inToken)
  908. : outputToken()
  909. {
  910.   size = 1;
  911.   myUnion.barray[0] = inChar; // save space
  912.   myPrev = inToken;
  913. }
  914. ////
  915. // destructor
  916. ////
  917. outputString::~outputString()
  918. {
  919.   if (size > sizeof(char*)) delete myUnion.bptr;
  920. }
  921. ////
  922. // draw it
  923. ////
  924. void outputString::draw(textObject *inText)
  925. {
  926.   inText->drawString(bptr(), size, 1, width(inText));
  927. }
  928. ////
  929. // width
  930. ////
  931. int outputString::width(textObject *inText)
  932. {
  933.   return (myWidth) ? myWidth : myWidth = inText->stringWidth(bptr(), size);
  934. }
  935. ////
  936. // single character class
  937. ////
  938. void outputChar::draw(textObject *inText)
  939. {
  940.   inText->drawString(&schar, 1, 1, width(inText));
  941. }
  942. int outputChar::width(textObject *inText)
  943. {
  944.   return (myWidth) ? myWidth : myWidth = inText->stringWidth(&schar, 1);
  945. }
  946. ////
  947. // space class
  948. ////
  949. void outputSpace::draw(textObject *inText)
  950. {
  951.   inText->outSpace();
  952. }
  953. ////
  954. // width
  955. ////
  956. int outputSpace::width(textObject *inText)
  957. {
  958.   return (myWidth) ? myWidth : myWidth = inText->stringWidth(" ", 1);
  959. }
  960. ////
  961. // token holders
  962. ////
  963. // constructor
  964. ////
  965. tokenHolder::tokenHolder() : outputToken()
  966. {
  967.   contents = lastToken = NULL;
  968.   myLinesBefore = myLinesAfter = 0;
  969. }
  970. ////
  971. // destructor
  972. ////
  973. tokenHolder::~tokenHolder() 
  974. {
  975.   outputToken *myToken;
  976.   while (myToken = contents) {
  977.     contents = contents->next();
  978.     delete myToken;
  979.   }
  980. }
  981. ////
  982. // add space (AT&T can't inline this)
  983. tokenHolder *tokenHolder::addSpace(int i)
  984. {
  985.   static char myBuffer[20];
  986.   sprintf(myBuffer, "%d", i); // for now
  987.   return addSpace(myBuffer);
  988. }
  989. ////
  990. // add space (AT&T can't inline this)
  991. tokenHolder *tokenHolder::addSpace(float x)
  992. {
  993.   static char myBuffer[20];
  994.   sprintf(myBuffer, "%f", x); // for now
  995.   return addSpace(myBuffer);
  996. }
  997. ////
  998. // how many new lines do we hold ?
  999. ////
  1000. int tokenHolder::noLines()
  1001. {
  1002.   int myNoLines = 0;
  1003.   for (outputToken *myToken = start(); myToken; myToken = myToken->next()) {
  1004.     myNoLines += myToken->noLines();
  1005.   }
  1006.   return myNoLines;
  1007. }
  1008. ////
  1009. // draw it
  1010. ////
  1011. void tokenHolder::draw(textObject *inText)
  1012. {
  1013.   if (!inText) return;
  1014.   inText->newLine(linesBefore());
  1015.   int myHeight = inText->pageHeight();
  1016.   for (outputToken *myToken = contents; myToken && ((!myHeight) ||
  1017.        (inText->vpos() < myHeight)); myToken = myToken->next()) {
  1018.     myToken->draw(inText);
  1019.   }
  1020.   inText->newLine(linesAfter());
  1021. }
  1022. ////
  1023. // width
  1024. ////
  1025. int tokenHolder::width(textObject *inText)
  1026. {
  1027.   int myWidth = 0;
  1028.   for (outputToken *myToken = contents; myToken; myToken = myToken->next())
  1029.     myWidth += myToken->width(inText);
  1030.   return myWidth;
  1031. }
  1032. ////
  1033. // add one output token
  1034. ////
  1035. tokenHolder *tokenHolder::addToken(outputToken *inToken, int addBreak)
  1036. {
  1037.   if (!inToken) return this;
  1038.   if (!contents || !lastToken) contents = lastToken = inToken;
  1039.   else lastToken->setNext(inToken);
  1040.   inToken->setPrev(lastToken);
  1041.   lastToken = inToken;
  1042.   inToken->setOwner(this);
  1043.   inToken->setNext(NULL);
  1044.   if (addBreak) addToken(new optionalBreak());
  1045.   return this;
  1046. }
  1047. ////
  1048. // short cut for adding an outputString
  1049. ////
  1050. tokenHolder *tokenHolder::addToken(const char *inString, int addBreak)
  1051. {
  1052.   for (int myLen = 0; inString[myLen]; ++myLen);
  1053.   if (!myLen) return this;
  1054.   // AT&T compiler seems to produce bad code if next 2 lines are combined
  1055.   outputToken *myToken = new outputString(inString, myLen); 
  1056.   addToken(myToken, addBreak);
  1057.   return this;
  1058. }
  1059. ////
  1060. // short cut for adding an outputChar
  1061. ////
  1062. tokenHolder *tokenHolder::addToken(char inChar, int addBreak)
  1063. {
  1064.   addToken(new outputChar(inChar), addBreak);
  1065.   return this;
  1066. }
  1067. ////
  1068. // reset the first output token
  1069. ////
  1070. void tokenHolder::newStart(outputToken *inToken)
  1071. {
  1072.   if (inToken) {
  1073.     inToken->setOwner(this);
  1074.     inToken->setPrev(NULL);
  1075.   }
  1076.   contents = inToken;
  1077.   if (!lastToken) lastToken = contents;
  1078.  
  1079. }
  1080. ////
  1081. // reset the last output token
  1082. ////
  1083. void tokenHolder::newEnd(outputToken *inToken)
  1084. {
  1085.   if (inToken) {
  1086.     inToken->setOwner(this);
  1087.     inToken->setNext(NULL);
  1088.   }
  1089.   lastToken = inToken;
  1090. }
  1091. ////
  1092. // how many tokens do we have
  1093. ////
  1094. int tokenHolder::noTokens()
  1095. {
  1096.   int i = 0;
  1097.   for (outputToken *myToken = contents; myToken; myToken = myToken->next())
  1098.     i += myToken->noTokens();
  1099.   return i;
  1100. }
  1101. ////
  1102. // make a kern
  1103. ////
  1104. textObject *textObject::kern(int inKern)
  1105. {
  1106.   if (inKern < 0) {
  1107.     if ((hpos() + inKern) <= 0) myHpos = leftMargin();
  1108.     else myHpos -= inKern;
  1109.   } else {
  1110.     if ((hpos() + inKern) >= pageWidth()) newLine();
  1111.     else myHpos += inKern;
  1112.   }
  1113.   return this;
  1114. }
  1115. ////
  1116. // the output file object
  1117. ////
  1118. // make a kern
  1119. ////
  1120. textObject *outputFile::kern(int inKern)
  1121. {
  1122.   if (inKern > 0) {
  1123.     bufferIndex += inKern;
  1124.   } else {
  1125.     bufferIndex = (bufferIndex >= inKern) ? bufferIndex - inKern : 0;
  1126.   }
  1127.   return this;
  1128. }
  1129. ////
  1130. // output kern token
  1131. ////
  1132. outputKern::outputKern(int inKern, outputToken *inToken)  : outputToken()
  1133. {
  1134.   myKern = inKern;
  1135.   if (!inToken) {
  1136.     myError("no inToken for outputKern!\n");
  1137.     return; // no links to make
  1138.   }
  1139.   myOwner = inToken->owner();
  1140.   if (!myOwner) {
  1141.     myError("no owner for outputKern!\n");
  1142.     return;
  1143.   }
  1144.   // the kern is inserted before the inToken
  1145.   myNext = inToken;
  1146.   myPrev = inToken->prev();
  1147.   myNext->setPrev(this);
  1148.   if (myPrev) {
  1149.     myPrev->setNext(this);
  1150.     if (inToken == myOwner->start())
  1151.       myError("corrupted start in outputKern! (inToken == start)\n");
  1152.   } else {
  1153.     if (inToken != myOwner->start())
  1154.       myError("corrupted start in outputKern! (inToken != start)\n");
  1155.     myOwner->newStart(this);
  1156.     if (this != myOwner->start()) myError("not equal to start!\n");
  1157.   }
  1158. }
  1159. void outputKern::draw(textObject *inText)
  1160. {
  1161.   inText->kern(myKern);
  1162. }
  1163.