File indexing completed on 2025-03-16 08:11:23
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 }