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

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.4
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