• Skip to content
  • Skip to link menu
KDE 4.0 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 (C) 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 }
00397 
00398 int HistoryScrollBuffer::bufferIndex(int lineNumber)
00399 {
00400     Q_ASSERT( lineNumber >= 0 );
00401     Q_ASSERT( lineNumber < _maxLineCount );
00402     Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head );
00403 
00404     if ( _usedLines == _maxLineCount )
00405     {
00406         return (_head+lineNumber+1) % _maxLineCount;
00407     }
00408     else
00409     {   
00410         return lineNumber;
00411     }
00412 }
00413 
00414 
00415 // History Scroll None //////////////////////////////////////
00416 
00417 HistoryScrollNone::HistoryScrollNone()
00418   : HistoryScroll(new HistoryTypeNone())
00419 {
00420 }
00421 
00422 HistoryScrollNone::~HistoryScrollNone()
00423 {
00424 }
00425 
00426 bool HistoryScrollNone::hasScroll()
00427 {
00428   return false;
00429 }
00430 
00431 int  HistoryScrollNone::getLines()
00432 {
00433   return 0;
00434 }
00435 
00436 int  HistoryScrollNone::getLineLen(int)
00437 {
00438   return 0;
00439 }
00440 
00441 bool HistoryScrollNone::isWrappedLine(int /*lineno*/)
00442 {
00443   return false;
00444 }
00445 
00446 void HistoryScrollNone::getCells(int, int, int, Character [])
00447 {
00448 }
00449 
00450 void HistoryScrollNone::addCells(const Character [], int)
00451 {
00452 }
00453 
00454 void HistoryScrollNone::addLine(bool)
00455 {
00456 }
00457 
00458 // History Scroll BlockArray //////////////////////////////////////
00459 
00460 HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size)
00461   : HistoryScroll(new HistoryTypeBlockArray(size))
00462 {
00463   m_blockArray.setHistorySize(size); // nb. of lines.
00464 }
00465 
00466 HistoryScrollBlockArray::~HistoryScrollBlockArray()
00467 {
00468 }
00469 
00470 int  HistoryScrollBlockArray::getLines()
00471 {
00472   return m_lineLengths.count();
00473 }
00474 
00475 int  HistoryScrollBlockArray::getLineLen(int lineno)
00476 {
00477     if ( m_lineLengths.contains(lineno) )
00478         return m_lineLengths[lineno];
00479     else
00480         return 0;
00481 }
00482 
00483 bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/)
00484 {
00485   return false;
00486 }
00487 
00488 void HistoryScrollBlockArray::getCells(int lineno, int colno,
00489                                        int count, Character res[])
00490 {
00491   if (!count) return;
00492 
00493   const Block *b = m_blockArray.at(lineno);
00494 
00495   if (!b) {
00496     memset(res, 0, count * sizeof(Character)); // still better than random data
00497     return;
00498   }
00499 
00500   assert(((colno + count) * sizeof(Character)) < ENTRIES);
00501   memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character));
00502 }
00503 
00504 void HistoryScrollBlockArray::addCells(const Character a[], int count)
00505 {
00506   Block *b = m_blockArray.lastBlock();
00507   
00508   if (!b) return;
00509 
00510   // put cells in block's data
00511   assert((count * sizeof(Character)) < ENTRIES);
00512 
00513   memset(b->data, 0, ENTRIES);
00514 
00515   memcpy(b->data, a, count * sizeof(Character));
00516   b->size = count * sizeof(Character);
00517 
00518   size_t res = m_blockArray.newBlock();
00519   assert (res > 0);
00520   Q_UNUSED( res );
00521 
00522   m_lineLengths.insert(m_blockArray.getCurrent(), count);
00523 }
00524 
00525 void HistoryScrollBlockArray::addLine(bool)
00526 {
00527 }
00528 
00530 // History Types
00532 
00533 HistoryType::HistoryType()
00534 {
00535 }
00536 
00537 HistoryType::~HistoryType()
00538 {
00539 }
00540 
00542 
00543 HistoryTypeNone::HistoryTypeNone()
00544 {
00545 }
00546 
00547 bool HistoryTypeNone::isEnabled() const
00548 {
00549   return false;
00550 }
00551 
00552 HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const
00553 {
00554   delete old;
00555   return new HistoryScrollNone();
00556 }
00557 
00558 int HistoryTypeNone::maximumLineCount() const
00559 {
00560   return 0;
00561 }
00562 
00564 
00565 HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size)
00566   : m_size(size)
00567 {
00568 }
00569 
00570 bool HistoryTypeBlockArray::isEnabled() const
00571 {
00572   return true;
00573 }
00574 
00575 int HistoryTypeBlockArray::maximumLineCount() const
00576 {
00577   return m_size;
00578 }
00579 
00580 HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const
00581 {
00582   delete old;
00583   return new HistoryScrollBlockArray(m_size);
00584 }
00585 
00586 
00588 
00589 HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines)
00590   : m_nbLines(nbLines)
00591 {
00592 }
00593 
00594 bool HistoryTypeBuffer::isEnabled() const
00595 {
00596   return true;
00597 }
00598 
00599 int HistoryTypeBuffer::maximumLineCount() const
00600 {
00601   return m_nbLines;
00602 }
00603 
00604 HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const
00605 {
00606   if (old)
00607   {
00608     HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old);
00609     if (oldBuffer)
00610     {
00611        oldBuffer->setMaxNbLines(m_nbLines);
00612        return oldBuffer;
00613     }
00614 
00615     HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines);
00616     int lines = old->getLines();
00617     int startLine = 0;
00618     if (lines > (int) m_nbLines)
00619        startLine = lines - m_nbLines;
00620 
00621     Character line[LINE_SIZE];
00622     for(int i = startLine; i < lines; i++)
00623     {
00624        int size = old->getLineLen(i);
00625        if (size > LINE_SIZE)
00626        {
00627           Character *tmp_line = new Character[size];
00628           old->getCells(i, 0, size, tmp_line);
00629           newScroll->addCells(tmp_line, size);
00630           newScroll->addLine(old->isWrappedLine(i));
00631           delete [] tmp_line;
00632        }
00633        else
00634        {
00635           old->getCells(i, 0, size, line);
00636           newScroll->addCells(line, size);
00637           newScroll->addLine(old->isWrappedLine(i));
00638        }
00639     }
00640     delete old;
00641     return newScroll;
00642   }
00643   return new HistoryScrollBuffer(m_nbLines);
00644 }
00645 
00647 
00648 HistoryTypeFile::HistoryTypeFile(const QString& fileName)
00649   : m_fileName(fileName)
00650 {
00651 }
00652 
00653 bool HistoryTypeFile::isEnabled() const
00654 {
00655   return true;
00656 }
00657 
00658 const QString& HistoryTypeFile::getFileName() const
00659 {
00660   return m_fileName;
00661 }
00662 
00663 HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const
00664 {
00665   if (dynamic_cast<HistoryFile *>(old)) 
00666      return old; // Unchanged.
00667 
00668   HistoryScroll *newScroll = new HistoryScrollFile(m_fileName);
00669 
00670   Character line[LINE_SIZE];
00671   int lines = (old != 0) ? old->getLines() : 0;
00672   for(int i = 0; i < lines; i++)
00673   {
00674      int size = old->getLineLen(i);
00675      if (size > LINE_SIZE)
00676      {
00677         Character *tmp_line = new Character[size];
00678         old->getCells(i, 0, size, tmp_line);
00679         newScroll->addCells(tmp_line, size);
00680         newScroll->addLine(old->isWrappedLine(i));
00681         delete [] tmp_line;
00682      }
00683      else
00684      {
00685         old->getCells(i, 0, size, line);
00686         newScroll->addCells(line, size);
00687         newScroll->addLine(old->isWrappedLine(i));
00688      }
00689   }
00690 
00691   delete old;
00692   return newScroll; 
00693 }
00694 
00695 int HistoryTypeFile::maximumLineCount() const
00696 {
00697   return 0;
00698 }

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