File indexing completed on 2025-01-19 04:23:24

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