2
This file is part of Konsole, an X terminal.
3
Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
5
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31
#include <sys/types.h>
37
// Reasonable line size
38
#define LINE_SIZE 1024
40
using namespace Konsole;
43
An arbitrary long scroll.
45
One can modify the scroll only by adding either cells
46
or newlines, but access it randomly.
48
The model is that of an arbitrary wide typewriter scroll
49
in that the scroll is a serie of lines and each line is
50
a serie of cells with no overwriting permitted.
52
The implementation provides arbitrary length and numbers
53
of cells and line/column indexed read access to the scroll
56
KDE4: Can we use QTemporaryFile here, instead of KTempFile?
58
FIXME: some complain about the history buffer comsuming the
59
memory of their machines. This problem is critical
60
since the history does not behave gracefully in cases
61
where the memory is used up completely.
63
I put in a workaround that should handle it problem
64
now gracefully. I'm not satisfied with the solution.
66
FIXME: Terminating the history is not properly indicated
67
in the menu. We should throw a signal.
69
FIXME: There is noticeable decrease in speed, also. Perhaps,
70
there whole feature needs to be revisited therefore.
71
Disadvantage of a more elaborated, say block-oriented
72
scheme with wrap around would be it's complexity.
75
//FIXME: tempory replacement for tmpfile
76
// this is here one for debugging purpose.
78
//#define tmpfile xTmpFile
80
// History File ///////////////////////////////////////////
83
A Row(X) data type which allows adding elements to the end.
86
HistoryFile::HistoryFile()
93
tmpFile.setAutoRemove(true);
94
ion = tmpFile.handle();
98
HistoryFile::~HistoryFile()
104
//TODO: Mapping the entire file in will cause problems if the history file becomes exceedingly large,
105
//(ie. larger than available memory). HistoryFile::map() should only map in sections of the file at a time,
107
void HistoryFile::map()
109
assert( fileMap == 0 );
111
fileMap = (char*)mmap( 0 , length , PROT_READ , MAP_PRIVATE , ion , 0 );
113
//if mmap'ing fails, fall back to the read-lseek combination
114
if ( fileMap == MAP_FAILED )
116
readWriteBalance = 0;
118
qDebug() << ": mmap'ing history failed. errno = " << errno;
122
void HistoryFile::unmap()
124
int result = munmap( fileMap , length );
125
assert( result == 0 );
130
bool HistoryFile::isMapped()
132
return (fileMap != 0);
135
void HistoryFile::add(const unsigned char* bytes, int len)
144
rc = lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; }
145
rc = write(ion,bytes,len); if (rc < 0) { perror("HistoryFile::add.write"); return; }
149
void HistoryFile::get(unsigned char* bytes, int len, int loc)
151
//count number of get() calls vs. number of add() calls.
152
//If there are many more get() calls compared with add()
153
//calls (decided by using MAP_THRESHOLD) then mmap the log
154
//file to improve performance.
156
if ( !fileMap && readWriteBalance < MAP_THRESHOLD )
161
for (int i=0;i<len;i++)
162
bytes[i]=fileMap[loc+i];
168
if (loc < 0 || len < 0 || loc + len > length)
169
fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc);
170
rc = lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; }
171
rc = read(ion,bytes,len); if (rc < 0) { perror("HistoryFile::get.read"); return; }
175
int HistoryFile::len()
181
// History Scroll abstract base class //////////////////////////////////////
184
HistoryScroll::HistoryScroll(HistoryType* t)
189
HistoryScroll::~HistoryScroll()
194
bool HistoryScroll::hasScroll()
199
// History Scroll File //////////////////////////////////////
202
The history scroll makes a Row(Row(Cell)) from
203
two history buffers. The index buffer contains
204
start of line positions which refere to the cells
207
Note that index[0] addresses the second line
208
(line #1), while the first line (line #0) starts
212
HistoryScrollFile::HistoryScrollFile(const QString &logFileName)
213
: HistoryScroll(new HistoryTypeFile(logFileName)),
214
m_logFileName(logFileName)
218
HistoryScrollFile::~HistoryScrollFile()
222
int HistoryScrollFile::getLines()
224
return index.len() / sizeof(int);
227
int HistoryScrollFile::getLineLen(int lineno)
229
return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character);
232
bool HistoryScrollFile::isWrappedLine(int lineno)
234
if (lineno>=0 && lineno <= getLines()) {
236
lineflags.get((unsigned char*)&flag,sizeof(unsigned char),(lineno)*sizeof(unsigned char));
242
int HistoryScrollFile::startOfLine(int lineno)
244
if (lineno <= 0) return 0;
245
if (lineno <= getLines())
248
if (!index.isMapped())
252
index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int));
258
void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[])
260
cells.get((unsigned char*)res,count*sizeof(Character),startOfLine(lineno)+colno*sizeof(Character));
263
void HistoryScrollFile::addCells(const Character text[], int count)
265
cells.add((unsigned char*)text,count*sizeof(Character));
268
void HistoryScrollFile::addLine(bool previousWrapped)
270
if (index.isMapped())
273
int locn = cells.len();
274
index.add((unsigned char*)&locn,sizeof(int));
275
unsigned char flags = previousWrapped ? 0x01 : 0x00;
276
lineflags.add((unsigned char*)&flags,sizeof(unsigned char));
280
// History Scroll Buffer //////////////////////////////////////
281
HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount)
282
: HistoryScroll(new HistoryTypeBuffer(maxLineCount))
288
setMaxNbLines(maxLineCount);
291
HistoryScrollBuffer::~HistoryScrollBuffer()
293
delete[] _historyBuffer;
296
void HistoryScrollBuffer::addCellsVector(const QVector<Character>& cells)
299
if ( _usedLines < _maxLineCount )
302
if ( _head >= _maxLineCount )
307
_historyBuffer[bufferIndex(_usedLines-1)] = cells;
308
_wrappedLine[bufferIndex(_usedLines-1)] = false;
310
void HistoryScrollBuffer::addCells(const Character a[], int count)
312
HistoryLine newLine(count);
313
qCopy(a,a+count,newLine.begin());
315
addCellsVector(newLine);
318
void HistoryScrollBuffer::addLine(bool previousWrapped)
320
_wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped;
323
int HistoryScrollBuffer::getLines()
328
int HistoryScrollBuffer::getLineLen(int lineNumber)
330
Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
332
if ( lineNumber < _usedLines )
334
return _historyBuffer[bufferIndex(lineNumber)].size();
342
bool HistoryScrollBuffer::isWrappedLine(int lineNumber)
344
Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
346
if (lineNumber < _usedLines)
348
//kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)];
349
return _wrappedLine[bufferIndex(lineNumber)];
355
void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, Character* buffer)
357
if ( count == 0 ) return;
359
Q_ASSERT( lineNumber < _maxLineCount );
361
if (lineNumber >= _usedLines)
363
memset(buffer, 0, count * sizeof(Character));
367
const HistoryLine& line = _historyBuffer[bufferIndex(lineNumber)];
369
//kDebug() << "startCol " << startColumn;
370
//kDebug() << "line.size() " << line.size();
371
//kDebug() << "count " << count;
373
Q_ASSERT( startColumn <= line.size() - count );
375
memcpy(buffer, line.constData() + startColumn , count * sizeof(Character));
378
void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount)
380
HistoryLine* oldBuffer = _historyBuffer;
381
HistoryLine* newBuffer = new HistoryLine[lineCount];
383
for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ )
385
newBuffer[i] = oldBuffer[bufferIndex(i)];
388
_usedLines = qMin(_usedLines,(int)lineCount);
389
_maxLineCount = lineCount;
390
_head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1;
392
_historyBuffer = newBuffer;
395
_wrappedLine.resize(lineCount);
398
int HistoryScrollBuffer::bufferIndex(int lineNumber)
400
Q_ASSERT( lineNumber >= 0 );
401
Q_ASSERT( lineNumber < _maxLineCount );
402
Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head );
404
if ( _usedLines == _maxLineCount )
406
return (_head+lineNumber+1) % _maxLineCount;
415
// History Scroll None //////////////////////////////////////
417
HistoryScrollNone::HistoryScrollNone()
418
: HistoryScroll(new HistoryTypeNone())
422
HistoryScrollNone::~HistoryScrollNone()
426
bool HistoryScrollNone::hasScroll()
431
int HistoryScrollNone::getLines()
436
int HistoryScrollNone::getLineLen(int)
441
bool HistoryScrollNone::isWrappedLine(int /*lineno*/)
446
void HistoryScrollNone::getCells(int, int, int, Character [])
450
void HistoryScrollNone::addCells(const Character [], int)
454
void HistoryScrollNone::addLine(bool)
458
// History Scroll BlockArray //////////////////////////////////////
460
HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size)
461
: HistoryScroll(new HistoryTypeBlockArray(size))
463
m_blockArray.setHistorySize(size); // nb. of lines.
466
HistoryScrollBlockArray::~HistoryScrollBlockArray()
470
int HistoryScrollBlockArray::getLines()
472
return m_lineLengths.count();
475
int HistoryScrollBlockArray::getLineLen(int lineno)
477
if ( m_lineLengths.contains(lineno) )
478
return m_lineLengths[lineno];
483
bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/)
488
void HistoryScrollBlockArray::getCells(int lineno, int colno,
489
int count, Character res[])
493
const Block *b = m_blockArray.at(lineno);
496
memset(res, 0, count * sizeof(Character)); // still better than random data
500
assert(((colno + count) * sizeof(Character)) < ENTRIES);
501
memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character));
504
void HistoryScrollBlockArray::addCells(const Character a[], int count)
506
Block *b = m_blockArray.lastBlock();
510
// put cells in block's data
511
assert((count * sizeof(Character)) < ENTRIES);
513
memset(b->data, 0, ENTRIES);
515
memcpy(b->data, a, count * sizeof(Character));
516
b->size = count * sizeof(Character);
518
size_t res = m_blockArray.newBlock();
522
m_lineLengths.insert(m_blockArray.getCurrent(), count);
525
void HistoryScrollBlockArray::addLine(bool)
529
//////////////////////////////////////////////////////////////////////
531
//////////////////////////////////////////////////////////////////////
533
HistoryType::HistoryType()
537
HistoryType::~HistoryType()
541
//////////////////////////////
543
HistoryTypeNone::HistoryTypeNone()
547
bool HistoryTypeNone::isEnabled() const
552
HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const
555
return new HistoryScrollNone();
558
int HistoryTypeNone::maximumLineCount() const
563
//////////////////////////////
565
HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size)
570
bool HistoryTypeBlockArray::isEnabled() const
575
int HistoryTypeBlockArray::maximumLineCount() const
580
HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const
583
return new HistoryScrollBlockArray(m_size);
587
//////////////////////////////
589
HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines)
594
bool HistoryTypeBuffer::isEnabled() const
599
int HistoryTypeBuffer::maximumLineCount() const
604
HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const
608
HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old);
611
oldBuffer->setMaxNbLines(m_nbLines);
615
HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines);
616
int lines = old->getLines();
618
if (lines > (int) m_nbLines)
619
startLine = lines - m_nbLines;
621
Character line[LINE_SIZE];
622
for(int i = startLine; i < lines; i++)
624
int size = old->getLineLen(i);
625
if (size > LINE_SIZE)
627
Character *tmp_line = new Character[size];
628
old->getCells(i, 0, size, tmp_line);
629
newScroll->addCells(tmp_line, size);
630
newScroll->addLine(old->isWrappedLine(i));
635
old->getCells(i, 0, size, line);
636
newScroll->addCells(line, size);
637
newScroll->addLine(old->isWrappedLine(i));
643
return new HistoryScrollBuffer(m_nbLines);
646
//////////////////////////////
648
HistoryTypeFile::HistoryTypeFile(const QString& fileName)
649
: m_fileName(fileName)
653
bool HistoryTypeFile::isEnabled() const
658
const QString& HistoryTypeFile::getFileName() const
663
HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const
665
if (dynamic_cast<HistoryFile *>(old))
666
return old; // Unchanged.
668
HistoryScroll *newScroll = new HistoryScrollFile(m_fileName);
670
Character line[LINE_SIZE];
671
int lines = (old != 0) ? old->getLines() : 0;
672
for(int i = 0; i < lines; i++)
674
int size = old->getLineLen(i);
675
if (size > LINE_SIZE)
677
Character *tmp_line = new Character[size];
678
old->getCells(i, 0, size, tmp_line);
679
newScroll->addCells(tmp_line, size);
680
newScroll->addLine(old->isWrappedLine(i));
685
old->getCells(i, 0, size, line);
686
newScroll->addCells(line, size);
687
newScroll->addLine(old->isWrappedLine(i));
695
int HistoryTypeFile::maximumLineCount() const