File indexing completed on 2024-05-19 05:28:16

0001 /*
0002     This file is part of Konsole, an X terminal.
0003     SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 
0007     This program is distributed in the hope that it will be useful,
0008     but WITHOUT ANY WARRANTY; without even the implied warranty of
0009     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0010     GNU General Public License for more details.
0011 
0012     You should have received a copy of the GNU General Public License
0013     along with this program; if not, write to the Free Software
0014     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0015     02110-1301  USA.
0016 */
0017 
0018 // Own
0019 #include "History.h"
0020 
0021 // System
0022 #include <algorithm>
0023 #include <cerrno>
0024 #include <cstdio>
0025 #include <cstdlib>
0026 #include <iostream>
0027 #include <sys/mman.h>
0028 #include <sys/types.h>
0029 #include <unistd.h>
0030 
0031 #include <QtDebug>
0032 
0033 // KDE
0034 // #include <kde_file.h>
0035 // #include <kdebug.h>
0036 
0037 // Reasonable line size
0038 #define LINE_SIZE 1024
0039 #define KDE_lseek lseek
0040 
0041 using namespace Konsole;
0042 
0043 /*
0044    An arbitrary long scroll.
0045 
0046    One can modify the scroll only by adding either cells
0047    or newlines, but access it randomly.
0048 
0049    The model is that of an arbitrary wide typewriter scroll
0050    in that the scroll is a serie of lines and each line is
0051    a serie of cells with no overwriting permitted.
0052 
0053    The implementation provides arbitrary length and numbers
0054    of cells and line/column indexed read access to the scroll
0055    at constant costs.
0056 
0057 KDE4: Can we use QTemporaryFile here, instead of KTempFile?
0058 
0059 FIXME: some complain about the history buffer comsuming the
0060        memory of their machines. This problem is critical
0061        since the history does not behave gracefully in cases
0062        where the memory is used up completely.
0063 
0064        I put in a workaround that should handle it problem
0065        now gracefully. I'm not satisfied with the solution.
0066 
0067 FIXME: Terminating the history is not properly indicated
0068        in the menu. We should throw a signal.
0069 
0070 FIXME: There is noticeable decrease in speed, also. Perhaps,
0071        there whole feature needs to be revisited therefore.
0072        Disadvantage of a more elaborated, say block-oriented
0073        scheme with wrap around would be it's complexity.
0074 */
0075 
0076 // FIXME: tempory replacement for tmpfile
0077 //        this is here one for debugging purpose.
0078 
0079 // #define tmpfile xTmpFile
0080 
0081 // History File ///////////////////////////////////////////
0082 
0083 /*
0084   A Row(X) data type which allows adding elements to the end.
0085 */
0086 
0087 HistoryFile::HistoryFile()
0088     : ion(-1)
0089     , length(0)
0090     , fileMap(nullptr)
0091     , readWriteBalance(0)
0092 {
0093     if (tmpFile.open()) {
0094         tmpFile.setAutoRemove(true);
0095         ion = tmpFile.handle();
0096     }
0097 }
0098 
0099 HistoryFile::~HistoryFile()
0100 {
0101     if (fileMap)
0102         unmap();
0103 }
0104 
0105 // TODO:  Mapping the entire file in will cause problems if the history file becomes exceedingly large,
0106 //(ie. larger than available memory).  HistoryFile::map() should only map in sections of the file at a time,
0107 // to avoid this.
0108 void HistoryFile::map()
0109 {
0110     Q_ASSERT(fileMap == nullptr);
0111 
0112     fileMap = (char *)mmap(nullptr, length, PROT_READ, MAP_PRIVATE, ion, 0);
0113 
0114     // if mmap'ing fails, fall back to the read-lseek combination
0115     if (fileMap == MAP_FAILED) {
0116         readWriteBalance = 0;
0117         fileMap = nullptr;
0118         // qDebug() << __FILE__ << __LINE__ << ": mmap'ing history failed.  errno = " << errno;
0119     }
0120 }
0121 
0122 void HistoryFile::unmap()
0123 {
0124     int result = munmap(fileMap, length);
0125     Q_ASSERT(result == 0);
0126     Q_UNUSED(result);
0127 
0128     fileMap = nullptr;
0129 }
0130 
0131 bool HistoryFile::isMapped() const
0132 {
0133     return (fileMap != nullptr);
0134 }
0135 
0136 void HistoryFile::add(const unsigned char *bytes, int len)
0137 {
0138     if (fileMap)
0139         unmap();
0140 
0141     readWriteBalance++;
0142 
0143     int rc = 0;
0144 
0145     rc = KDE_lseek(ion, length, SEEK_SET);
0146     if (rc < 0) {
0147         perror("HistoryFile::add.seek");
0148         return;
0149     }
0150     rc = write(ion, bytes, len);
0151     if (rc < 0) {
0152         perror("HistoryFile::add.write");
0153         return;
0154     }
0155     length += rc;
0156 }
0157 
0158 void HistoryFile::get(unsigned char *bytes, int len, int loc)
0159 {
0160     // count number of get() calls vs. number of add() calls.
0161     // If there are many more get() calls compared with add()
0162     // calls (decided by using MAP_THRESHOLD) then mmap the log
0163     // file to improve performance.
0164     readWriteBalance--;
0165     if (!fileMap && readWriteBalance < MAP_THRESHOLD)
0166         map();
0167 
0168     if (fileMap) {
0169         for (int i = 0; i < len; i++)
0170             bytes[i] = fileMap[loc + i];
0171     } else {
0172         int rc = 0;
0173 
0174         if (loc < 0 || len < 0 || loc + len > length)
0175             fprintf(stderr, "getHist(...,%d,%d): invalid args.\n", len, loc);
0176         rc = KDE_lseek(ion, loc, SEEK_SET);
0177         if (rc < 0) {
0178             perror("HistoryFile::get.seek");
0179             return;
0180         }
0181         rc = read(ion, bytes, len);
0182         if (rc < 0) {
0183             perror("HistoryFile::get.read");
0184             return;
0185         }
0186     }
0187 }
0188 
0189 int HistoryFile::len()
0190 {
0191     return length;
0192 }
0193 
0194 // History Scroll abstract base class //////////////////////////////////////
0195 
0196 HistoryScroll::HistoryScroll(HistoryType *t)
0197     : m_histType(t)
0198 {
0199 }
0200 
0201 HistoryScroll::~HistoryScroll()
0202 {
0203     delete m_histType;
0204 }
0205 
0206 bool HistoryScroll::hasScroll()
0207 {
0208     return true;
0209 }
0210 
0211 // History Scroll File //////////////////////////////////////
0212 
0213 /*
0214    The history scroll makes a Row(Row(Cell)) from
0215    two history buffers. The index buffer contains
0216    start of line positions which refere to the cells
0217    buffer.
0218 
0219    Note that index[0] addresses the second line
0220    (line #1), while the first line (line #0) starts
0221    at 0 in cells.
0222 */
0223 
0224 HistoryScrollFile::HistoryScrollFile(const QString &logFileName)
0225     : HistoryScroll(new HistoryTypeFile(logFileName))
0226     , m_logFileName(logFileName)
0227 {
0228 }
0229 
0230 HistoryScrollFile::~HistoryScrollFile()
0231 {
0232 }
0233 
0234 int HistoryScrollFile::getLines()
0235 {
0236     return index.len() / sizeof(int);
0237 }
0238 
0239 int HistoryScrollFile::getLineLen(int lineno)
0240 {
0241     return (startOfLine(lineno + 1) - startOfLine(lineno)) / sizeof(Character);
0242 }
0243 
0244 bool HistoryScrollFile::isWrappedLine(int lineno)
0245 {
0246     if (lineno >= 0 && lineno <= getLines()) {
0247         unsigned char flag;
0248         lineflags.get((unsigned char *)&flag, sizeof(unsigned char), (lineno) * sizeof(unsigned char));
0249         return flag;
0250     }
0251     return false;
0252 }
0253 
0254 int HistoryScrollFile::startOfLine(int lineno)
0255 {
0256     if (lineno <= 0)
0257         return 0;
0258     if (lineno <= getLines()) {
0259         if (!index.isMapped())
0260             index.map();
0261 
0262         int res;
0263         index.get((unsigned char *)&res, sizeof(int), (lineno - 1) * sizeof(int));
0264         return res;
0265     }
0266     return cells.len();
0267 }
0268 
0269 void HistoryScrollFile::getCells(int lineno, int colno, int count, std::span<Character> res)
0270 {
0271     cells.get((unsigned char *)res.data(), count * sizeof(Character), startOfLine(lineno) + colno * sizeof(Character));
0272 }
0273 
0274 void HistoryScrollFile::addCells(std::span<const Character> text, int count)
0275 {
0276     cells.add((unsigned char *)text.data(), count * sizeof(Character));
0277 }
0278 
0279 void HistoryScrollFile::addLine(bool previousWrapped)
0280 {
0281     if (index.isMapped())
0282         index.unmap();
0283 
0284     int locn = cells.len();
0285     index.add((unsigned char *)&locn, sizeof(int));
0286     unsigned char flags = previousWrapped ? 0x01 : 0x00;
0287     lineflags.add((unsigned char *)&flags, sizeof(unsigned char));
0288 }
0289 
0290 // History Scroll Buffer //////////////////////////////////////
0291 HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount)
0292     : HistoryScroll(new HistoryTypeBuffer(maxLineCount))
0293     , _historyBuffer()
0294     , _maxLineCount(0)
0295     , _usedLines(0)
0296     , _head(0)
0297 {
0298     setMaxNbLines(maxLineCount);
0299 }
0300 
0301 HistoryScrollBuffer::~HistoryScrollBuffer()
0302 {
0303     delete[] _historyBuffer;
0304 }
0305 
0306 void HistoryScrollBuffer::addCellsVector(const QVector<Character> &cells)
0307 {
0308     _head++;
0309     if (_usedLines < _maxLineCount)
0310         _usedLines++;
0311 
0312     if (_head >= _maxLineCount) {
0313         _head = 0;
0314     }
0315 
0316     _historyBuffer[bufferIndex(_usedLines - 1)] = cells;
0317     _wrappedLine[bufferIndex(_usedLines - 1)] = false;
0318 }
0319 void HistoryScrollBuffer::addCells(std::span<const Character> a, int count)
0320 {
0321     HistoryLine newLine(count);
0322     std::copy(a.data(), a.subspan(count).data(), newLine.begin());
0323 
0324     addCellsVector(newLine);
0325 }
0326 
0327 void HistoryScrollBuffer::addLine(bool previousWrapped)
0328 {
0329     _wrappedLine[bufferIndex(_usedLines - 1)] = previousWrapped;
0330 }
0331 
0332 int HistoryScrollBuffer::getLines()
0333 {
0334     return _usedLines;
0335 }
0336 
0337 int HistoryScrollBuffer::getLineLen(int lineNumber)
0338 {
0339     Q_ASSERT(lineNumber >= 0 && lineNumber < _maxLineCount);
0340 
0341     if (lineNumber < _usedLines) {
0342         return _historyBuffer[bufferIndex(lineNumber)].size();
0343     } else {
0344         return 0;
0345     }
0346 }
0347 
0348 bool HistoryScrollBuffer::isWrappedLine(int lineNumber)
0349 {
0350     Q_ASSERT(lineNumber >= 0 && lineNumber < _maxLineCount);
0351 
0352     if (lineNumber < _usedLines) {
0353         // kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)];
0354         return _wrappedLine[bufferIndex(lineNumber)];
0355     } else
0356         return false;
0357 }
0358 
0359 void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, std::span<Character> buffer)
0360 {
0361     if (count == 0)
0362         return;
0363 
0364     Q_ASSERT(lineNumber < _maxLineCount);
0365 
0366     if (lineNumber >= _usedLines) {
0367         memset(static_cast<void *>(buffer.data()), 0, count * sizeof(Character));
0368         return;
0369     }
0370 
0371     const HistoryLine &line = _historyBuffer[bufferIndex(lineNumber)];
0372 
0373     // kDebug() << "startCol " << startColumn;
0374     // kDebug() << "line.size() " << line.size();
0375     // kDebug() << "count " << count;
0376 
0377     Q_ASSERT(startColumn <= line.size() - count);
0378 
0379     memcpy(buffer.data(), line.constData() + startColumn, count * sizeof(Character));
0380 }
0381 
0382 void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount)
0383 {
0384     HistoryLine *oldBuffer = _historyBuffer;
0385     HistoryLine *newBuffer = new HistoryLine[lineCount];
0386 
0387     for (int i = 0; i < qMin(_usedLines, (int)lineCount); i++) {
0388         newBuffer[i] = oldBuffer[bufferIndex(i)];
0389     }
0390 
0391     _usedLines = qMin(_usedLines, (int)lineCount);
0392     _maxLineCount = lineCount;
0393     _head = (_usedLines == _maxLineCount) ? 0 : _usedLines - 1;
0394 
0395     _historyBuffer = newBuffer;
0396     delete[] oldBuffer;
0397 
0398     _wrappedLine.resize(lineCount);
0399     dynamic_cast<HistoryTypeBuffer *>(m_histType)->m_nbLines = lineCount;
0400 }
0401 
0402 int HistoryScrollBuffer::bufferIndex(int lineNumber) const
0403 {
0404     Q_ASSERT(lineNumber >= 0);
0405     Q_ASSERT(lineNumber < _maxLineCount);
0406     Q_ASSERT((_usedLines == _maxLineCount) || lineNumber <= _head);
0407 
0408     if (_usedLines == _maxLineCount) {
0409         return (_head + lineNumber + 1) % _maxLineCount;
0410     } else {
0411         return lineNumber;
0412     }
0413 }
0414 
0415 // History Scroll None //////////////////////////////////////
0416 
0417 HistoryScrollNone::HistoryScrollNone()
0418     : HistoryScroll(new HistoryTypeNone())
0419 {
0420 }
0421 
0422 HistoryScrollNone::~HistoryScrollNone()
0423 {
0424 }
0425 
0426 bool HistoryScrollNone::hasScroll()
0427 {
0428     return false;
0429 }
0430 
0431 int HistoryScrollNone::getLines()
0432 {
0433     return 0;
0434 }
0435 
0436 int HistoryScrollNone::getLineLen(int)
0437 {
0438     return 0;
0439 }
0440 
0441 bool HistoryScrollNone::isWrappedLine(int /*lineno*/)
0442 {
0443     return false;
0444 }
0445 
0446 void HistoryScrollNone::getCells(int, int, int, std::span<Character>)
0447 {
0448 }
0449 
0450 void HistoryScrollNone::addCells(std::span<const Character>, int)
0451 {
0452 }
0453 
0454 void HistoryScrollNone::addLine(bool)
0455 {
0456 }
0457 
0458 // History Scroll BlockArray //////////////////////////////////////
0459 
0460 HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size)
0461     : HistoryScroll(new HistoryTypeBlockArray(size))
0462 {
0463     m_blockArray.setHistorySize(size); // nb. of lines.
0464 }
0465 
0466 HistoryScrollBlockArray::~HistoryScrollBlockArray()
0467 {
0468 }
0469 
0470 int HistoryScrollBlockArray::getLines()
0471 {
0472     return m_lineLengths.size();
0473 }
0474 
0475 int HistoryScrollBlockArray::getLineLen(int lineno)
0476 {
0477     if (m_lineLengths.contains(lineno))
0478         return m_lineLengths[lineno];
0479     else
0480         return 0;
0481 }
0482 
0483 bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/)
0484 {
0485     return false;
0486 }
0487 
0488 void HistoryScrollBlockArray::getCells(int lineno, int colno, int count, std::span<Character> res)
0489 {
0490     if (!count)
0491         return;
0492 
0493     const Block *b = m_blockArray.at(lineno);
0494 
0495     if (!b) {
0496         memset(static_cast<void *>(res.data()), 0, count * sizeof(Character)); // still better than random data
0497         return;
0498     }
0499 
0500     Q_ASSERT(((colno + count) * sizeof(Character)) < ENTRIES);
0501     memcpy(res.data(), b->data + (colno * sizeof(Character)), count * sizeof(Character));
0502 }
0503 
0504 void HistoryScrollBlockArray::addCells(std::span<const Character> a, int count)
0505 {
0506     Block *b = m_blockArray.lastBlock();
0507 
0508     if (!b)
0509         return;
0510 
0511     // put cells in block's data
0512     Q_ASSERT((count * sizeof(Character)) < ENTRIES);
0513 
0514     memset(b->data, 0, sizeof(b->data));
0515 
0516     memcpy(b->data, a.data(), count * sizeof(Character));
0517     b->size = count * sizeof(Character);
0518 
0519     size_t res = m_blockArray.newBlock();
0520     Q_ASSERT(res > 0);
0521     Q_UNUSED(res);
0522 
0523     m_lineLengths.insert(m_blockArray.getCurrent(), count);
0524 }
0525 
0526 void HistoryScrollBlockArray::addLine(bool)
0527 {
0528 }
0529 
0530 ////////////////////////////////////////////////////////////////
0531 // Compact History Scroll //////////////////////////////////////
0532 ////////////////////////////////////////////////////////////////
0533 void *CompactHistoryBlock::allocate(size_t length)
0534 {
0535     Q_ASSERT(length > 0);
0536     if (tail - blockStart + length > blockLength)
0537         return nullptr;
0538 
0539     void *block = tail;
0540     tail += length;
0541     // kDebug() << "allocated " << length << " bytes at address " << block;
0542     allocCount++;
0543     return block;
0544 }
0545 
0546 void CompactHistoryBlock::deallocate()
0547 {
0548     allocCount--;
0549     Q_ASSERT(allocCount >= 0);
0550 }
0551 
0552 void *CompactHistoryBlockList::allocate(size_t size)
0553 {
0554     CompactHistoryBlock *block;
0555     if (list.isEmpty() || list.last()->remaining() < size) {
0556         block = new CompactHistoryBlock();
0557         list.append(block);
0558         // kDebug() << "new block created, remaining " << block->remaining() << "number of blocks=" << list.size();
0559     } else {
0560         block = list.last();
0561         // kDebug() << "old block used, remaining " << block->remaining();
0562     }
0563     return block->allocate(size);
0564 }
0565 
0566 void CompactHistoryBlockList::deallocate(void *ptr)
0567 {
0568     Q_ASSERT(!list.isEmpty());
0569 
0570     int i = 0;
0571     CompactHistoryBlock *block = list.at(i);
0572     while (i < list.size() && !block->contains(ptr)) {
0573         i++;
0574         block = list.at(i);
0575     }
0576 
0577     Q_ASSERT(i < list.size());
0578 
0579     block->deallocate();
0580 
0581     if (!block->isInUse()) {
0582         list.removeAt(i);
0583         delete block;
0584         // kDebug() << "block deleted, new size = " << list.size();
0585     }
0586 }
0587 
0588 CompactHistoryBlockList::~CompactHistoryBlockList()
0589 {
0590     qDeleteAll(list.begin(), list.end());
0591     list.clear();
0592 }
0593 
0594 void *CompactHistoryLine::operator new(size_t size, CompactHistoryBlockList &blockList)
0595 {
0596     return blockList.allocate(size);
0597 }
0598 
0599 CompactHistoryLine::CompactHistoryLine(const TextLine &line, CompactHistoryBlockList &bList)
0600     : blockList(bList)
0601     , formatLength(0)
0602 {
0603     length = line.size();
0604 
0605     if (!line.empty()) {
0606         formatLength = 1;
0607         int k = 1;
0608 
0609         // count number of different formats in this text line
0610         Character c = line[0];
0611         while (k < length) {
0612             if (!(line[k].equalsFormat(c))) {
0613                 formatLength++; // format change detected
0614                 c = line[k];
0615             }
0616             k++;
0617         }
0618 
0619         // kDebug() << "number of different formats in string: " << formatLength;
0620         formatArray = (CharacterFormat *)blockList.allocate(sizeof(CharacterFormat) * formatLength);
0621         Q_ASSERT(formatArray != nullptr);
0622         text = (quint16 *)blockList.allocate(sizeof(quint16) * line.size());
0623         Q_ASSERT(text != nullptr);
0624 
0625         length = line.size();
0626         wrapped = false;
0627 
0628         // record formats and their positions in the format array
0629         c = line[0];
0630         formatArray[0].setFormat(c);
0631         formatArray[0].startPos = 0; // there's always at least 1 format (for the entire line, unless a change happens)
0632 
0633         k = 1; // look for possible format changes
0634         int j = 1;
0635         while (k < length && j < formatLength) {
0636             if (!(line[k].equalsFormat(c))) {
0637                 c = line[k];
0638                 formatArray[j].setFormat(c);
0639                 formatArray[j].startPos = k;
0640                 // kDebug() << "format entry " << j << " at pos " << formatArray[j].startPos << " " << &(formatArray[j].startPos) ;
0641                 j++;
0642             }
0643             k++;
0644         }
0645 
0646         // copy character values
0647         for (int i = 0; i < line.size(); i++) {
0648             text[i] = line[i].character.unicode();
0649             // kDebug() << "char " << i << " at mem " << &(text[i]);
0650         }
0651     }
0652     // kDebug() << "line created, length " << length << " at " << &(length);
0653 }
0654 
0655 CompactHistoryLine::~CompactHistoryLine()
0656 {
0657     // kDebug() << "~CHL";
0658     if (length > 0) {
0659         blockList.deallocate(text);
0660         blockList.deallocate(formatArray);
0661     }
0662     blockList.deallocate(this);
0663 }
0664 
0665 void CompactHistoryLine::getCharacter(int index, Character &r)
0666 {
0667     Q_ASSERT(index < length);
0668     int formatPos = 0;
0669     while ((formatPos + 1) < formatLength && index >= formatArray[formatPos + 1].startPos)
0670         formatPos++;
0671 
0672     r.character = text[index];
0673     r.rendition = formatArray[formatPos].rendition;
0674     r.foregroundColor = formatArray[formatPos].fgColor;
0675     r.backgroundColor = formatArray[formatPos].bgColor;
0676 }
0677 
0678 void CompactHistoryLine::getCharacters(Character *array, int length, int startColumn)
0679 {
0680     Q_ASSERT(startColumn >= 0 && length >= 0);
0681     Q_ASSERT(startColumn + length <= (int)getLength());
0682 
0683     for (int i = startColumn; i < length + startColumn; i++) {
0684         getCharacter(i, array[i - startColumn]);
0685     }
0686 }
0687 
0688 CompactHistoryScroll::CompactHistoryScroll(unsigned int maxLineCount)
0689     : HistoryScroll(new CompactHistoryType(maxLineCount))
0690     , lines()
0691     , blockList()
0692 {
0693     // kDebug() << "scroll of length " << maxLineCount << " created";
0694     setMaxNbLines(maxLineCount);
0695 }
0696 
0697 CompactHistoryScroll::~CompactHistoryScroll()
0698 {
0699     qDeleteAll(lines.begin(), lines.end());
0700     lines.clear();
0701 }
0702 
0703 void CompactHistoryScroll::addCellsVector(const TextLine &cells)
0704 {
0705     CompactHistoryLine *line;
0706     line = new (blockList) CompactHistoryLine(cells, blockList);
0707 
0708     if (lines.size() > (int)_maxLineCount) {
0709         delete lines.takeAt(0);
0710     }
0711     lines.append(line);
0712 }
0713 
0714 void CompactHistoryScroll::addCells(std::span<const Character> a, int count)
0715 {
0716     TextLine newLine(count);
0717     std::copy(a.data(), a.subspan(count).data(), newLine.begin());
0718     addCellsVector(newLine);
0719 }
0720 
0721 void CompactHistoryScroll::addLine(bool previousWrapped)
0722 {
0723     CompactHistoryLine *line = lines.last();
0724     // kDebug() << "last line at address " << line;
0725     line->setWrapped(previousWrapped);
0726 }
0727 
0728 int CompactHistoryScroll::getLines()
0729 {
0730     return lines.size();
0731 }
0732 
0733 int CompactHistoryScroll::getLineLen(int lineNumber)
0734 {
0735     Q_ASSERT(lineNumber >= 0 && lineNumber < lines.size());
0736     CompactHistoryLine *line = lines[lineNumber];
0737     // kDebug() << "request for line at address " << line;
0738     return line->getLength();
0739 }
0740 
0741 void CompactHistoryScroll::getCells(int lineNumber, int startColumn, int count, std::span<Character> buffer)
0742 {
0743     if (count == 0)
0744         return;
0745     Q_ASSERT(lineNumber < lines.size());
0746     CompactHistoryLine *line = lines[lineNumber];
0747     Q_ASSERT(startColumn >= 0);
0748     Q_ASSERT((unsigned int)startColumn <= line->getLength() - count);
0749     line->getCharacters(buffer.data(), count, startColumn);
0750 }
0751 
0752 void CompactHistoryScroll::setMaxNbLines(unsigned int lineCount)
0753 {
0754     _maxLineCount = lineCount;
0755 
0756     while (lines.size() > (int)lineCount) {
0757         delete lines.takeAt(0);
0758     }
0759     // kDebug() << "set max lines to: " << _maxLineCount;
0760 }
0761 
0762 bool CompactHistoryScroll::isWrappedLine(int lineNumber)
0763 {
0764     Q_ASSERT(lineNumber < lines.size());
0765     return lines[lineNumber]->isWrapped();
0766 }
0767 
0768 //////////////////////////////////////////////////////////////////////
0769 // History Types
0770 //////////////////////////////////////////////////////////////////////
0771 
0772 HistoryType::HistoryType()
0773 {
0774 }
0775 
0776 HistoryType::~HistoryType()
0777 {
0778 }
0779 
0780 //////////////////////////////
0781 
0782 HistoryTypeNone::HistoryTypeNone()
0783 {
0784 }
0785 
0786 bool HistoryTypeNone::isEnabled() const
0787 {
0788     return false;
0789 }
0790 
0791 std::unique_ptr<HistoryScroll> HistoryTypeNone::scroll(std::unique_ptr<HistoryScroll> &&) const
0792 {
0793     return std::make_unique<HistoryScrollNone>();
0794 }
0795 
0796 int HistoryTypeNone::maximumLineCount() const
0797 {
0798     return 0;
0799 }
0800 
0801 //////////////////////////////
0802 
0803 HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size)
0804     : m_size(size)
0805 {
0806 }
0807 
0808 bool HistoryTypeBlockArray::isEnabled() const
0809 {
0810     return true;
0811 }
0812 
0813 int HistoryTypeBlockArray::maximumLineCount() const
0814 {
0815     return m_size;
0816 }
0817 
0818 std::unique_ptr<HistoryScroll> HistoryTypeBlockArray::scroll(std::unique_ptr<HistoryScroll> &&) const
0819 {
0820     return std::make_unique<HistoryScrollBlockArray>(m_size);
0821 }
0822 
0823 //////////////////////////////
0824 
0825 HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines)
0826     : m_nbLines(nbLines)
0827 {
0828 }
0829 
0830 bool HistoryTypeBuffer::isEnabled() const
0831 {
0832     return true;
0833 }
0834 
0835 int HistoryTypeBuffer::maximumLineCount() const
0836 {
0837     return m_nbLines;
0838 }
0839 
0840 std::unique_ptr<HistoryScroll> HistoryTypeBuffer::scroll(std::unique_ptr<HistoryScroll> &&old) const
0841 {
0842     if (old) {
0843         HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer *>(old.get());
0844         if (oldBuffer) {
0845             oldBuffer->setMaxNbLines(m_nbLines);
0846             return old;
0847         }
0848 
0849         auto newScroll = std::make_unique<HistoryScrollBuffer>(m_nbLines);
0850         int lines = old->getLines();
0851         int startLine = 0;
0852         if (lines > (int)m_nbLines)
0853             startLine = lines - m_nbLines;
0854 
0855         Character line[LINE_SIZE];
0856         for (int i = startLine; i < lines; i++) {
0857             int size = old->getLineLen(i);
0858             if (size > LINE_SIZE) {
0859                 auto tmp_line = std::vector<Character>(size);
0860                 old->getCells(i, 0, size, tmp_line);
0861                 newScroll->addCells(tmp_line, size);
0862                 newScroll->addLine(old->isWrappedLine(i));
0863             } else {
0864                 old->getCells(i, 0, size, line);
0865                 newScroll->addCells(line, size);
0866                 newScroll->addLine(old->isWrappedLine(i));
0867             }
0868         }
0869         return newScroll;
0870     }
0871     return std::make_unique<HistoryScrollBuffer>(m_nbLines);
0872 }
0873 
0874 //////////////////////////////
0875 
0876 HistoryTypeFile::HistoryTypeFile(const QString &fileName)
0877     : m_fileName(fileName)
0878 {
0879 }
0880 
0881 bool HistoryTypeFile::isEnabled() const
0882 {
0883     return true;
0884 }
0885 
0886 const QString &HistoryTypeFile::getFileName() const
0887 {
0888     return m_fileName;
0889 }
0890 
0891 std::unique_ptr<HistoryScroll> HistoryTypeFile::scroll(std::unique_ptr<HistoryScroll> &&old) const
0892 {
0893     if (dynamic_cast<HistoryFile *>(old.get()))
0894         return old; // Unchanged.
0895 
0896     auto newScroll = std::make_unique<HistoryScrollFile>(m_fileName);
0897 
0898     Character line[LINE_SIZE];
0899     int lines = (old != nullptr) ? old->getLines() : 0;
0900     for (int i = 0; i < lines; i++) {
0901         int size = old->getLineLen(i);
0902         if (size > LINE_SIZE) {
0903             auto tmp_line = std::vector<Character>(size);
0904             old->getCells(i, 0, size, tmp_line);
0905             newScroll->addCells(tmp_line, size);
0906             newScroll->addLine(old->isWrappedLine(i));
0907         } else {
0908             old->getCells(i, 0, size, line);
0909             newScroll->addCells(line, size);
0910             newScroll->addLine(old->isWrappedLine(i));
0911         }
0912     }
0913 
0914     return newScroll;
0915 }
0916 
0917 int HistoryTypeFile::maximumLineCount() const
0918 {
0919     return 0;
0920 }
0921 
0922 //////////////////////////////
0923 
0924 CompactHistoryType::CompactHistoryType(unsigned int nbLines)
0925     : m_nbLines(nbLines)
0926 {
0927 }
0928 
0929 bool CompactHistoryType::isEnabled() const
0930 {
0931     return true;
0932 }
0933 
0934 int CompactHistoryType::maximumLineCount() const
0935 {
0936     return m_nbLines;
0937 }
0938 
0939 std::unique_ptr<HistoryScroll> CompactHistoryType::scroll(std::unique_ptr<HistoryScroll> &&old) const
0940 {
0941     if (old) {
0942         CompactHistoryScroll *oldBuffer = dynamic_cast<CompactHistoryScroll *>(old.get());
0943         if (oldBuffer) {
0944             oldBuffer->setMaxNbLines(m_nbLines);
0945             return old;
0946         }
0947     }
0948     return std::make_unique<CompactHistoryScroll>(m_nbLines);
0949 }