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

0001 /*
0002     This file is part of Konsole, an X terminal.
0003     SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
0004 
0005     Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 
0009     This program is distributed in the hope that it will be useful,
0010     but WITHOUT ANY WARRANTY; without even the implied warranty of
0011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012     GNU General Public License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program; if not, write to the Free Software
0016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017     02110-1301  USA.
0018 
0019 */
0020 
0021 #include <QDebug>
0022 
0023 // Own
0024 #include "BlockArray.h"
0025 
0026 // System
0027 #include <cstdio>
0028 #include <sys/mman.h>
0029 #include <sys/param.h>
0030 #include <unistd.h>
0031 
0032 using namespace Konsole;
0033 
0034 BlockArray::BlockArray()
0035     : size(0)
0036     , current(size_t(-1))
0037     , index(size_t(-1))
0038     , lastmap(nullptr)
0039     , lastmap_index(size_t(-1))
0040     , lastblock(nullptr)
0041     , ion(-1)
0042     , length(0)
0043 {
0044     // lastmap_index = index = current = size_t(-1);
0045     if (blocksize == 0) {
0046         blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize();
0047     }
0048 }
0049 
0050 BlockArray::~BlockArray()
0051 {
0052     setHistorySize(0);
0053     Q_ASSERT(!lastblock);
0054 }
0055 
0056 size_t BlockArray::append(Block *block)
0057 {
0058     if (!size) {
0059         return size_t(-1);
0060     }
0061 
0062     ++current;
0063     if (current >= size) {
0064         current = 0;
0065     }
0066 
0067     int rc;
0068     rc = lseek(ion, current * blocksize, SEEK_SET);
0069     if (rc < 0) {
0070         perror("HistoryBuffer::add.seek");
0071         setHistorySize(0);
0072         return size_t(-1);
0073     }
0074     rc = write(ion, block, blocksize);
0075     if (rc < 0) {
0076         perror("HistoryBuffer::add.write");
0077         setHistorySize(0);
0078         return size_t(-1);
0079     }
0080 
0081     length++;
0082     if (length > size) {
0083         length = size;
0084     }
0085 
0086     ++index;
0087 
0088     delete block;
0089     return current;
0090 }
0091 
0092 size_t BlockArray::newBlock()
0093 {
0094     if (!size) {
0095         return size_t(-1);
0096     }
0097     append(lastblock);
0098 
0099     lastblock = new Block();
0100     return index + 1;
0101 }
0102 
0103 Block *BlockArray::lastBlock() const
0104 {
0105     return lastblock;
0106 }
0107 
0108 bool BlockArray::has(size_t i) const
0109 {
0110     if (i == index + 1) {
0111         return true;
0112     }
0113 
0114     if (i > index) {
0115         return false;
0116     }
0117     if (index - i >= length) {
0118         return false;
0119     }
0120     return true;
0121 }
0122 
0123 const Block *BlockArray::at(size_t i)
0124 {
0125     if (i == index + 1) {
0126         return lastblock;
0127     }
0128 
0129     if (i == lastmap_index) {
0130         return lastmap;
0131     }
0132 
0133     if (i > index) {
0134         qDebug() << "BlockArray::at() i > index\n";
0135         return nullptr;
0136     }
0137 
0138     size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ;
0139 
0140     Q_ASSERT(j < size);
0141     unmap();
0142 
0143     Block *block = (Block *)mmap(nullptr, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize);
0144 
0145     if (block == (Block *)-1) {
0146         perror("mmap");
0147         return nullptr;
0148     }
0149 
0150     lastmap = block;
0151     lastmap_index = i;
0152 
0153     return block;
0154 }
0155 
0156 void BlockArray::unmap()
0157 {
0158     if (lastmap) {
0159         int res = munmap((char *)lastmap, blocksize);
0160         if (res < 0) {
0161             perror("munmap");
0162         }
0163     }
0164     lastmap = nullptr;
0165     lastmap_index = size_t(-1);
0166 }
0167 
0168 bool BlockArray::setSize(size_t newsize)
0169 {
0170     return setHistorySize(newsize * 1024 / blocksize);
0171 }
0172 
0173 bool BlockArray::setHistorySize(size_t newsize)
0174 {
0175     if (size == newsize) {
0176         return false;
0177     }
0178 
0179     unmap();
0180 
0181     if (!newsize) {
0182         delete lastblock;
0183         lastblock = nullptr;
0184         if (ion >= 0) {
0185             close(ion);
0186         }
0187         ion = -1;
0188         current = size_t(-1);
0189         return true;
0190     }
0191 
0192     if (!size) {
0193         FILE *tmp = tmpfile();
0194         if (!tmp) {
0195             perror("konsole: cannot open temp file.\n");
0196         } else {
0197             ion = dup(fileno(tmp));
0198             if (ion < 0) {
0199                 perror("konsole: cannot dup temp file.\n");
0200                 fclose(tmp);
0201             }
0202         }
0203         if (ion < 0) {
0204             return false;
0205         }
0206 
0207         Q_ASSERT(!lastblock);
0208 
0209         lastblock = new Block();
0210         size = newsize;
0211         return false;
0212     }
0213 
0214     if (newsize > size) {
0215         increaseBuffer();
0216         size = newsize;
0217         return false;
0218     } else {
0219         decreaseBuffer(newsize);
0220         ftruncate(ion, length * blocksize);
0221         size = newsize;
0222 
0223         return true;
0224     }
0225 }
0226 
0227 void BlockArray::moveBlock(FILE *fion, int cursor, int newpos, char *buffer2)
0228 {
0229     int res = fseek(fion, cursor * blocksize, SEEK_SET);
0230     if (res) {
0231         perror("fseek");
0232     }
0233     res = fread(buffer2, blocksize, 1, fion);
0234     if (res != 1) {
0235         perror("fread");
0236     }
0237 
0238     res = fseek(fion, newpos * blocksize, SEEK_SET);
0239     if (res) {
0240         perror("fseek");
0241     }
0242     res = fwrite(buffer2, blocksize, 1, fion);
0243     if (res != 1) {
0244         perror("fwrite");
0245     }
0246     //    printf("moving block %d to %d\n", cursor, newpos);
0247 }
0248 
0249 void BlockArray::decreaseBuffer(size_t newsize)
0250 {
0251     if (index < newsize) { // still fits in whole
0252         return;
0253     }
0254 
0255     int offset = (current - (newsize - 1) + size) % size;
0256 
0257     if (!offset) {
0258         return;
0259     }
0260 
0261     // The Block constructor could do somthing in future...
0262     char *buffer1 = new char[blocksize];
0263 
0264     FILE *fion = fdopen(dup(ion), "w+b");
0265     if (!fion) {
0266         delete[] buffer1;
0267         perror("fdopen/dup");
0268         return;
0269     }
0270 
0271     int firstblock;
0272     if (current <= newsize) {
0273         firstblock = current + 1;
0274     } else {
0275         firstblock = 0;
0276     }
0277 
0278     size_t oldpos;
0279     for (size_t i = 0, cursor = firstblock; i < newsize; i++) {
0280         oldpos = (size + cursor + offset) % size;
0281         moveBlock(fion, oldpos, cursor, buffer1);
0282         if (oldpos < newsize) {
0283             cursor = oldpos;
0284         } else {
0285             cursor++;
0286         }
0287     }
0288 
0289     current = newsize - 1;
0290     length = newsize;
0291 
0292     delete[] buffer1;
0293 
0294     fclose(fion);
0295 }
0296 
0297 void BlockArray::increaseBuffer()
0298 {
0299     if (index < size) { // not even wrapped once
0300         return;
0301     }
0302 
0303     int offset = (current + size + 1) % size;
0304     if (!offset) { // no moving needed
0305         return;
0306     }
0307 
0308     // The Block constructor could do somthing in future...
0309     char *buffer1 = new char[blocksize];
0310     char *buffer2 = new char[blocksize];
0311 
0312     int runs = 1;
0313     int bpr = size; // blocks per run
0314 
0315     if (size % offset == 0) {
0316         bpr = size / offset;
0317         runs = offset;
0318     }
0319 
0320     FILE *fion = fdopen(dup(ion), "w+b");
0321     if (!fion) {
0322         perror("fdopen/dup");
0323         delete[] buffer1;
0324         delete[] buffer2;
0325         return;
0326     }
0327 
0328     int res;
0329     for (int i = 0; i < runs; i++) {
0330         // free one block in chain
0331         int firstblock = (offset + i) % size;
0332         res = fseek(fion, firstblock * blocksize, SEEK_SET);
0333         if (res) {
0334             perror("fseek");
0335         }
0336         res = fread(buffer1, blocksize, 1, fion);
0337         if (res != 1) {
0338             perror("fread");
0339         }
0340         int newpos = 0;
0341         for (int j = 1, cursor = firstblock; j < bpr; j++) {
0342             cursor = (cursor + offset) % size;
0343             newpos = (cursor - offset + size) % size;
0344             moveBlock(fion, cursor, newpos, buffer2);
0345         }
0346         res = fseek(fion, i * blocksize, SEEK_SET);
0347         if (res) {
0348             perror("fseek");
0349         }
0350         res = fwrite(buffer1, blocksize, 1, fion);
0351         if (res != 1) {
0352             perror("fwrite");
0353         }
0354     }
0355     current = size - 1;
0356     length = size;
0357 
0358     delete[] buffer1;
0359     delete[] buffer2;
0360 
0361     fclose(fion);
0362 }