• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Konsole

History.cpp

Go to the documentation of this file.
00001 /*
00002     This file is part of Konsole, an X terminal.
00003     Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
00004 
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program; if not, write to the Free Software
00017     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00018     02110-1301  USA.
00019 */
00020 
00021 // Own
00022 #include "History.h"
00023 
00024 // System
00025 #include <iostream>
00026 #include <stdlib.h>
00027 #include <assert.h>
00028 #include <stdio.h>
00029 #include <sys/types.h>
00030 #include <sys/mman.h>
00031 #include <unistd.h>
00032 #include <errno.h>
00033 
00034 // KDE
00035 #include <kde_file.h>
00036 #include <kdebug.h>
00037 
00038 // Reasonable line size
00039 #define LINE_SIZE    1024
00040 
00041 using namespace Konsole;
00042 
00043 /*
00044    An arbitrary long scroll.
00045 
00046    One can modify the scroll only by adding either cells
00047    or newlines, but access it randomly.
00048 
00049    The model is that of an arbitrary wide typewriter scroll
00050    in that the scroll is a serie of lines and each line is
00051    a serie of cells with no overwriting permitted.
00052 
00053    The implementation provides arbitrary length and numbers
00054    of cells and line/column indexed read access to the scroll
00055    at constant costs.
00056 
00057 KDE4: Can we use QTemporaryFile here, instead of KTempFile?
00058 
00059 FIXME: some complain about the history buffer comsuming the
00060        memory of their machines. This problem is critical
00061        since the history does not behave gracefully in cases
00062        where the memory is used up completely.
00063 
00064        I put in a workaround that should handle it problem
00065        now gracefully. I'm not satisfied with the solution.
00066 
00067 FIXME: Terminating the history is not properly indicated
00068        in the menu. We should throw a signal.
00069 
00070 FIXME: There is noticeable decrease in speed, also. Perhaps,
00071        there whole feature needs to be revisited therefore.
00072        Disadvantage of a more elaborated, say block-oriented
00073        scheme with wrap around would be it's complexity.
00074 */
00075 
00076 //FIXME: tempory replacement for tmpfile
00077 //       this is here one for debugging purpose.
00078 
00079 //#define tmpfile xTmpFile
00080 
00081 // History File ///////////////////////////////////////////
00082 
00083 /*
00084   A Row(X) data type which allows adding elements to the end.
00085 */
00086 
00087 HistoryFile::HistoryFile()
00088   : ion(-1),
00089     length(0),
00090     fileMap(0)
00091 {
00092   if (tmpFile.open())
00093   { 
00094     tmpFile.setAutoRemove(true);
00095     ion = tmpFile.handle();
00096   }
00097 }
00098 
00099 HistoryFile::~HistoryFile()
00100 {
00101     if (fileMap)
00102         unmap();
00103 }
00104 
00105 //TODO:  Mapping the entire file in will cause problems if the history file becomes exceedingly large,
00106 //(ie. larger than available memory).  HistoryFile::map() should only map in sections of the file at a time,
00107 //to avoid this.
00108 void HistoryFile::map()
00109 {
00110     assert( fileMap == 0 );
00111 
00112     fileMap = (char*)mmap( 0 , length , PROT_READ , MAP_PRIVATE , ion , 0 );
00113 
00114     //if mmap'ing fails, fall back to the read-lseek combination
00115     if ( fileMap == MAP_FAILED )
00116     {
00117             readWriteBalance = 0; 
00118             fileMap = 0;
00119             kDebug() << k_funcinfo << ": mmap'ing history failed.  errno = " << errno;
00120     }
00121 }
00122 
00123 void HistoryFile::unmap()
00124 {
00125     int result = munmap( fileMap , length );
00126     assert( result == 0 );
00127 
00128     fileMap = 0;
00129 }
00130 
00131 bool HistoryFile::isMapped()
00132 {
00133     return (fileMap != 0);
00134 }
00135 
00136 void HistoryFile::add(const unsigned char* bytes, int len)
00137 {
00138   if ( fileMap )
00139           unmap();
00140         
00141   readWriteBalance++;
00142 
00143   int rc = 0;
00144 
00145   rc = KDE_lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; }
00146   rc = write(ion,bytes,len);       if (rc < 0) { perror("HistoryFile::add.write"); return; }
00147   length += rc;
00148 }
00149 
00150 void HistoryFile::get(unsigned char* bytes, int len, int loc)
00151 {
00152   //count number of get() calls vs. number of add() calls.  
00153   //If there are many more get() calls compared with add() 
00154   //calls (decided by using MAP_THRESHOLD) then mmap the log
00155   //file to improve performance.
00156   readWriteBalance--;
00157   if ( !fileMap && readWriteBalance < MAP_THRESHOLD )
00158           map();
00159 
00160   if ( fileMap )
00161   {
00162     for (int i=0;i<len;i++)
00163             bytes[i]=fileMap[loc+i];
00164   }
00165   else
00166   {    
00167       int rc = 0;
00168 
00169       if (loc < 0 || len < 0 || loc + len > length)
00170         fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc);
00171       rc = KDE_lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; }
00172       rc = read(ion,bytes,len);     if (rc < 0) { perror("HistoryFile::get.read"); return; }
00173   }
00174 }
00175 
00176 int HistoryFile::len()
00177 {
00178   return length;
00179 }
00180 
00181 
00182 // History Scroll abstract base class //////////////////////////////////////
00183 
00184 
00185 HistoryScroll::HistoryScroll(HistoryType* t)
00186   : m_histType(t)
00187 {
00188 }
00189 
00190 HistoryScroll::~HistoryScroll()
00191 {
00192   delete m_histType;
00193 }
00194 
00195 bool HistoryScroll::hasScroll()
00196 {
00197   return true;
00198 }
00199 
00200 // History Scroll File //////////////////////////////////////
00201 
00202 /* 
00203    The history scroll makes a Row(Row(Cell)) from
00204    two history buffers. The index buffer contains
00205    start of line positions which refere to the cells
00206    buffer.
00207 
00208    Note that index[0] addresses the second line
00209    (line #1), while the first line (line #0) starts
00210    at 0 in cells.
00211 */
00212 
00213 HistoryScrollFile::HistoryScrollFile(const QString &logFileName)
00214   : HistoryScroll(new HistoryTypeFile(logFileName)),
00215   m_logFileName(logFileName)
00216 {
00217 }
00218 
00219 HistoryScrollFile::~HistoryScrollFile()
00220 {
00221 }
00222  
00223 int HistoryScrollFile::getLines()
00224 {
00225   return index.len() / sizeof(int);
00226 }
00227 
00228 int HistoryScrollFile::getLineLen(int lineno)
00229 {
00230   return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character);
00231 }
00232 
00233 bool HistoryScrollFile::isWrappedLine(int lineno)
00234 {
00235   if (lineno>=0 && lineno <= getLines()) {
00236     unsigned char flag;
00237     lineflags.get((unsigned char*)&flag,sizeof(unsigned char),(lineno)*sizeof(unsigned char));
00238     return flag;
00239   }
00240   return false;
00241 }
00242 
00243 int HistoryScrollFile::startOfLine(int lineno)
00244 {
00245   if (lineno <= 0) return 0;
00246   if (lineno <= getLines())
00247     { 
00248     
00249     if (!index.isMapped())
00250             index.map();
00251     
00252     int res;
00253     index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int));
00254     return res;
00255     }
00256   return cells.len();
00257 }
00258 
00259 void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[])
00260 {
00261   cells.get((unsigned char*)res,count*sizeof(Character),startOfLine(lineno)+colno*sizeof(Character));
00262 }
00263 
00264 void HistoryScrollFile::addCells(const Character text[], int count)
00265 {
00266   cells.add((unsigned char*)text,count*sizeof(Character));
00267 }
00268 
00269 void HistoryScrollFile::addLine(bool previousWrapped)
00270 {
00271   if (index.isMapped())
00272           index.unmap();
00273 
00274   int locn = cells.len();
00275   index.add((unsigned char*)&locn,sizeof(int));
00276   unsigned char flags = previousWrapped ? 0x01 : 0x00;
00277   lineflags.add((unsigned char*)&flags,sizeof(unsigned char));
00278 }
00279 
00280 
00281 // History Scroll Buffer //////////////////////////////////////
00282 HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount)
00283   : HistoryScroll(new HistoryTypeBuffer(maxLineCount))
00284    ,_historyBuffer()
00285    ,_maxLineCount(0)
00286    ,_usedLines(0)
00287    ,_head(0)
00288 {
00289   setMaxNbLines(maxLineCount);
00290 }
00291 
00292 HistoryScrollBuffer::~HistoryScrollBuffer()
00293 {
00294     delete[] _historyBuffer;
00295 }
00296 
00297 void HistoryScrollBuffer::addCellsVector(const QVector<Character>& cells)
00298 {
00299     _head++;
00300     if ( _usedLines < _maxLineCount )
00301         _usedLines++;
00302 
00303     if ( _head >= _maxLineCount )
00304     {
00305         _head = 0;
00306     }
00307 
00308     _historyBuffer[bufferIndex(_usedLines-1)] = cells;
00309     _wrappedLine[bufferIndex(_usedLines-1)] = false;
00310 }
00311 void HistoryScrollBuffer::addCells(const Character a[], int count)
00312 {
00313   HistoryLine newLine(count);
00314   qCopy(a,a+count,newLine.begin());
00315 
00316   addCellsVector(newLine);
00317 }
00318 
00319 void HistoryScrollBuffer::addLine(bool previousWrapped)
00320 {
00321     _wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped;
00322 }
00323 
00324 int HistoryScrollBuffer::getLines()
00325 {
00326     return _usedLines;
00327 }
00328 
00329 int HistoryScrollBuffer::getLineLen(int lineNumber)
00330 {
00331   Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
00332 
00333   if ( lineNumber < _usedLines )
00334   {
00335     return _historyBuffer[bufferIndex(lineNumber)].size();
00336   }
00337   else
00338   {
00339     return 0;
00340   }
00341 }
00342 
00343 bool HistoryScrollBuffer::isWrappedLine(int lineNumber)
00344 {
00345   Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
00346     
00347   if (lineNumber < _usedLines)
00348   {
00349     //kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)];
00350     return _wrappedLine[bufferIndex(lineNumber)];
00351   }
00352   else
00353     return false;
00354 }
00355 
00356 void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, Character* buffer)
00357 {
00358   if ( count == 0 ) return;
00359 
00360   Q_ASSERT( lineNumber < _maxLineCount );
00361 
00362   if (lineNumber >= _usedLines) 
00363   {
00364     memset(buffer, 0, count * sizeof(Character));
00365     return;
00366   }
00367   
00368   const HistoryLine& line = _historyBuffer[bufferIndex(lineNumber)];
00369 
00370   //kDebug() << "startCol " << startColumn;
00371   //kDebug() << "line.size() " << line.size();
00372   //kDebug() << "count " << count;
00373 
00374   Q_ASSERT( startColumn <= line.size() - count );
00375     
00376   memcpy(buffer, line.constData() + startColumn , count * sizeof(Character));
00377 }
00378 
00379 void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount)
00380 {
00381     HistoryLine* oldBuffer = _historyBuffer;
00382     HistoryLine* newBuffer = new HistoryLine[lineCount];
00383     
00384     for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ )
00385     {
00386         newBuffer[i] = oldBuffer[bufferIndex(i)];
00387     }
00388     
00389     _usedLines = qMin(_usedLines,(int)lineCount);
00390     _maxLineCount = lineCount;
00391     _head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1;
00392 
00393     _historyBuffer = newBuffer;
00394     delete[] oldBuffer;
00395 
00396     _wrappedLine.resize(lineCount);
00397     dynamic_cast<HistoryTypeBuffer*>(m_histType)->m_nbLines = lineCount;
00398 }
00399 
00400 int HistoryScrollBuffer::bufferIndex(int lineNumber)
00401 {
00402     Q_ASSERT( lineNumber >= 0 );
00403     Q_ASSERT( lineNumber < _maxLineCount );
00404     Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head );
00405 
00406     if ( _usedLines == _maxLineCount )
00407     {
00408         return (_head+lineNumber+1) % _maxLineCount;
00409     }
00410     else
00411     {   
00412         return lineNumber;
00413     }
00414 }
00415 
00416 
00417 // History Scroll None //////////////////////////////////////
00418 
00419 HistoryScrollNone::HistoryScrollNone()
00420   : HistoryScroll(new HistoryTypeNone())
00421 {
00422 }
00423 
00424 HistoryScrollNone::~HistoryScrollNone()
00425 {
00426 }
00427 
00428 bool HistoryScrollNone::hasScroll()
00429 {
00430   return false;
00431 }
00432 
00433 int  HistoryScrollNone::getLines()
00434 {
00435   return 0;
00436 }
00437 
00438 int  HistoryScrollNone::getLineLen(int)
00439 {
00440   return 0;
00441 }
00442 
00443 bool HistoryScrollNone::isWrappedLine(int /*lineno*/)
00444 {
00445   return false;
00446 }
00447 
00448 void HistoryScrollNone::getCells(int, int, int, Character [])
00449 {
00450 }
00451 
00452 void HistoryScrollNone::addCells(const Character [], int)
00453 {
00454 }
00455 
00456 void HistoryScrollNone::addLine(bool)
00457 {
00458 }
00459 
00460 // History Scroll BlockArray //////////////////////////////////////
00461 
00462 HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size)
00463   : HistoryScroll(new HistoryTypeBlockArray(size))
00464 {
00465   m_blockArray.setHistorySize(size); // nb. of lines.
00466 }
00467 
00468 HistoryScrollBlockArray::~HistoryScrollBlockArray()
00469 {
00470 }
00471 
00472 int  HistoryScrollBlockArray::getLines()
00473 {
00474   return m_lineLengths.count();
00475 }
00476 
00477 int  HistoryScrollBlockArray::getLineLen(int lineno)
00478 {
00479     if ( m_lineLengths.contains(lineno) )
00480         return m_lineLengths[lineno];
00481     else
00482         return 0;
00483 }
00484 
00485 bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/)
00486 {
00487   return false;
00488 }
00489 
00490 void HistoryScrollBlockArray::getCells(int lineno, int colno,
00491                                        int count, Character res[])
00492 {
00493   if (!count) return;
00494 
00495   const Block *b = m_blockArray.at(lineno);
00496 
00497   if (!b) {
00498     memset(res, 0, count * sizeof(Character)); // still better than random data
00499     return;
00500   }
00501 
00502   assert(((colno + count) * sizeof(Character)) < ENTRIES);
00503   memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character));
00504 }
00505 
00506 void HistoryScrollBlockArray::addCells(const Character a[], int count)
00507 {
00508   Block *b = m_blockArray.lastBlock();
00509   
00510   if (!b) return;
00511 
00512   // put cells in block's data
00513   assert((count * sizeof(Character)) < ENTRIES);
00514 
00515   memset(b->data, 0, ENTRIES);
00516 
00517   memcpy(b->data, a, count * sizeof(Character));
00518   b->size = count * sizeof(Character);
00519 
00520   size_t res = m_blockArray.newBlock();
00521   assert (res > 0);
00522   Q_UNUSED( res );
00523 
00524   m_lineLengths.insert(m_blockArray.getCurrent(), count);
00525 }
00526 
00527 void HistoryScrollBlockArray::addLine(bool)
00528 {
00529 }
00530 
00532 // History Types
00534 
00535 HistoryType::HistoryType()
00536 {
00537 }
00538 
00539 HistoryType::~HistoryType()
00540 {
00541 }
00542 
00544 
00545 HistoryTypeNone::HistoryTypeNone()
00546 {
00547 }
00548 
00549 bool HistoryTypeNone::isEnabled() const
00550 {
00551   return false;
00552 }
00553 
00554 HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const
00555 {
00556   delete old;
00557   return new HistoryScrollNone();
00558 }
00559 
00560 int HistoryTypeNone::maximumLineCount() const
00561 {
00562   return 0;
00563 }
00564 
00566 
00567 HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size)
00568   : m_size(size)
00569 {
00570 }
00571 
00572 bool HistoryTypeBlockArray::isEnabled() const
00573 {
00574   return true;
00575 }
00576 
00577 int HistoryTypeBlockArray::maximumLineCount() const
00578 {
00579   return m_size;
00580 }
00581 
00582 HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const
00583 {
00584   delete old;
00585   return new HistoryScrollBlockArray(m_size);
00586 }
00587 
00588 
00590 
00591 HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines)
00592   : m_nbLines(nbLines)
00593 {
00594 }
00595 
00596 bool HistoryTypeBuffer::isEnabled() const
00597 {
00598   return true;
00599 }
00600 
00601 int HistoryTypeBuffer::maximumLineCount() const
00602 {
00603   return m_nbLines;
00604 }
00605 
00606 HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const
00607 {
00608   if (old)
00609   {
00610     HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old);
00611     if (oldBuffer)
00612     {
00613        oldBuffer->setMaxNbLines(m_nbLines);
00614        return oldBuffer;
00615     }
00616 
00617     HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines);
00618     int lines = old->getLines();
00619     int startLine = 0;
00620     if (lines > (int) m_nbLines)
00621        startLine = lines - m_nbLines;
00622 
00623     Character line[LINE_SIZE];
00624     for(int i = startLine; i < lines; i++)
00625     {
00626        int size = old->getLineLen(i);
00627        if (size > LINE_SIZE)
00628        {
00629           Character *tmp_line = new Character[size];
00630           old->getCells(i, 0, size, tmp_line);
00631           newScroll->addCells(tmp_line, size);
00632           newScroll->addLine(old->isWrappedLine(i));
00633           delete [] tmp_line;
00634        }
00635        else
00636        {
00637           old->getCells(i, 0, size, line);
00638           newScroll->addCells(line, size);
00639           newScroll->addLine(old->isWrappedLine(i));
00640        }
00641     }
00642     delete old;
00643     return newScroll;
00644   }
00645   return new HistoryScrollBuffer(m_nbLines);
00646 }
00647 
00649 
00650 HistoryTypeFile::HistoryTypeFile(const QString& fileName)
00651   : m_fileName(fileName)
00652 {
00653 }
00654 
00655 bool HistoryTypeFile::isEnabled() const
00656 {
00657   return true;
00658 }
00659 
00660 const QString& HistoryTypeFile::getFileName() const
00661 {
00662   return m_fileName;
00663 }
00664 
00665 HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const
00666 {
00667   if (dynamic_cast<HistoryFile *>(old)) 
00668      return old; // Unchanged.
00669 
00670   HistoryScroll *newScroll = new HistoryScrollFile(m_fileName);
00671 
00672   Character line[LINE_SIZE];
00673   int lines = (old != 0) ? old->getLines() : 0;
00674   for(int i = 0; i < lines; i++)
00675   {
00676      int size = old->getLineLen(i);
00677      if (size > LINE_SIZE)
00678      {
00679         Character *tmp_line = new Character[size];
00680         old->getCells(i, 0, size, tmp_line);
00681         newScroll->addCells(tmp_line, size);
00682         newScroll->addLine(old->isWrappedLine(i));
00683         delete [] tmp_line;
00684      }
00685      else
00686      {
00687         old->getCells(i, 0, size, line);
00688         newScroll->addCells(line, size);
00689         newScroll->addLine(old->isWrappedLine(i));
00690      }
00691   }
00692 
00693   delete old;
00694   return newScroll; 
00695 }
00696 
00697 int HistoryTypeFile::maximumLineCount() const
00698 {
00699   return 0;
00700 }

Konsole

Skip menu "Konsole"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • Konsole
  • Libraries
  •   libkonq
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal