You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
7.4 KiB
335 lines
7.4 KiB
/* |
|
This file is part of Konsole, an X terminal. |
|
Copyright 2000 by Stephan Kulow <coolo@kde.org> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program; if not, write to the Free Software |
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
02110-1301 USA. |
|
*/ |
|
|
|
// Own |
|
#include "BlockArray.h" |
|
|
|
// System |
|
#include <assert.h> |
|
#include <sys/mman.h> |
|
#include <sys/param.h> |
|
#include <unistd.h> |
|
#include <stdio.h> |
|
|
|
// KDE |
|
#include <kde_file.h> |
|
#include <kdebug.h> |
|
|
|
using namespace Konsole; |
|
|
|
static int blocksize = 0; |
|
|
|
BlockArray::BlockArray() |
|
: size(0), |
|
current(size_t(-1)), |
|
index(size_t(-1)), |
|
lastmap(0), |
|
lastmap_index(size_t(-1)), |
|
lastblock(0), ion(-1), |
|
length(0) |
|
{ |
|
// lastmap_index = index = current = size_t(-1); |
|
if (blocksize == 0) |
|
blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize(); |
|
|
|
} |
|
|
|
BlockArray::~BlockArray() |
|
{ |
|
setHistorySize(0); |
|
assert(!lastblock); |
|
} |
|
|
|
size_t BlockArray::append(Block *block) |
|
{ |
|
if (!size) |
|
return size_t(-1); |
|
|
|
++current; |
|
if (current >= size) current = 0; |
|
|
|
int rc; |
|
rc = KDE_lseek(ion, current * blocksize, SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setHistorySize(0); return size_t(-1); } |
|
rc = write(ion, block, blocksize); if (rc < 0) { perror("HistoryBuffer::add.write"); setHistorySize(0); return size_t(-1); } |
|
|
|
length++; |
|
if (length > size) length = size; |
|
|
|
++index; |
|
|
|
delete block; |
|
return current; |
|
} |
|
|
|
size_t BlockArray::newBlock() |
|
{ |
|
if (!size) |
|
return size_t(-1); |
|
append(lastblock); |
|
|
|
lastblock = new Block(); |
|
return index + 1; |
|
} |
|
|
|
Block *BlockArray::lastBlock() const |
|
{ |
|
return lastblock; |
|
} |
|
|
|
bool BlockArray::has(size_t i) const |
|
{ |
|
if (i == index + 1) |
|
return true; |
|
|
|
if (i > index) |
|
return false; |
|
if (index - i >= length) |
|
return false; |
|
return true; |
|
} |
|
|
|
const Block* BlockArray::at(size_t i) |
|
{ |
|
if (i == index + 1) |
|
return lastblock; |
|
|
|
if (i == lastmap_index) |
|
return lastmap; |
|
|
|
if (i > index) { |
|
kDebug(1211) << "BlockArray::at() i > index\n"; |
|
return 0; |
|
} |
|
|
|
// if (index - i >= length) { |
|
// kDebug(1211) << "BlockArray::at() index - i >= length\n"; |
|
// return 0; |
|
// } |
|
|
|
size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ; |
|
|
|
assert(j < size); |
|
unmap(); |
|
|
|
Block *block = (Block*)mmap(0, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize); |
|
|
|
if (block == (Block*)-1) { perror("mmap"); return 0; } |
|
|
|
lastmap = block; |
|
lastmap_index = i; |
|
|
|
return block; |
|
} |
|
|
|
void BlockArray::unmap() |
|
{ |
|
if (lastmap) { |
|
int res = munmap((char*)lastmap, blocksize); |
|
if (res < 0) perror("munmap"); |
|
} |
|
lastmap = 0; |
|
lastmap_index = size_t(-1); |
|
} |
|
|
|
bool BlockArray::setSize(size_t newsize) |
|
{ |
|
return setHistorySize(newsize * 1024 / blocksize); |
|
} |
|
|
|
bool BlockArray::setHistorySize(size_t newsize) |
|
{ |
|
// kDebug(1211) << "setHistorySize " << size << " " << newsize; |
|
|
|
if (size == newsize) |
|
return false; |
|
|
|
unmap(); |
|
|
|
if (!newsize) { |
|
delete lastblock; |
|
lastblock = 0; |
|
if (ion >= 0) close(ion); |
|
ion = -1; |
|
current = size_t(-1); |
|
return true; |
|
} |
|
|
|
if (!size) { |
|
FILE* tmp = tmpfile(); |
|
if (!tmp) { |
|
perror("konsole: cannot open temp file.\n"); |
|
} else { |
|
ion = dup(fileno(tmp)); |
|
if (ion<0) { |
|
perror("konsole: cannot dup temp file.\n"); |
|
fclose(tmp); |
|
} |
|
} |
|
if (ion < 0) |
|
return false; |
|
|
|
assert(!lastblock); |
|
|
|
lastblock = new Block(); |
|
size = newsize; |
|
return false; |
|
} |
|
|
|
if (newsize > size) { |
|
increaseBuffer(); |
|
size = newsize; |
|
return false; |
|
} else { |
|
decreaseBuffer(newsize); |
|
ftruncate(ion, length*blocksize); |
|
size = newsize; |
|
|
|
return true; |
|
} |
|
} |
|
|
|
void moveBlock(FILE *fion, int cursor, int newpos, char *buffer2) |
|
{ |
|
int res = KDE_fseek(fion, cursor * blocksize, SEEK_SET); |
|
if (res) |
|
perror("fseek"); |
|
res = fread(buffer2, blocksize, 1, fion); |
|
if (res != 1) |
|
perror("fread"); |
|
|
|
res = KDE_fseek(fion, newpos * blocksize, SEEK_SET); |
|
if (res) |
|
perror("fseek"); |
|
res = fwrite(buffer2, blocksize, 1, fion); |
|
if (res != 1) |
|
perror("fwrite"); |
|
// printf("moving block %d to %d\n", cursor, newpos); |
|
} |
|
|
|
void BlockArray::decreaseBuffer(size_t newsize) |
|
{ |
|
if (index < newsize) // still fits in whole |
|
return; |
|
|
|
int offset = (current - (newsize - 1) + size) % size; |
|
|
|
if (!offset) |
|
return; |
|
|
|
// The Block constructor could do somthing in future... |
|
char *buffer1 = new char[blocksize]; |
|
|
|
FILE *fion = fdopen(dup(ion), "w+b"); |
|
if (!fion) { |
|
delete [] buffer1; |
|
perror("fdopen/dup"); |
|
return; |
|
} |
|
|
|
int firstblock; |
|
if (current <= newsize) { |
|
firstblock = current + 1; |
|
} else { |
|
firstblock = 0; |
|
} |
|
|
|
size_t oldpos; |
|
for (size_t i = 0, cursor=firstblock; i < newsize; i++) { |
|
oldpos = (size + cursor + offset) % size; |
|
moveBlock(fion, oldpos, cursor, buffer1); |
|
if (oldpos < newsize) { |
|
cursor = oldpos; |
|
} else |
|
cursor++; |
|
} |
|
|
|
current = newsize - 1; |
|
length = newsize; |
|
|
|
delete [] buffer1; |
|
|
|
fclose(fion); |
|
|
|
} |
|
|
|
void BlockArray::increaseBuffer() |
|
{ |
|
if (index < size) // not even wrapped once |
|
return; |
|
|
|
int offset = (current + size + 1) % size; |
|
if (!offset) // no moving needed |
|
return; |
|
|
|
// The Block constructor could do somthing in future... |
|
char *buffer1 = new char[blocksize]; |
|
char *buffer2 = new char[blocksize]; |
|
|
|
int runs = 1; |
|
int bpr = size; // blocks per run |
|
|
|
if (size % offset == 0) { |
|
bpr = size / offset; |
|
runs = offset; |
|
} |
|
|
|
FILE *fion = fdopen(dup(ion), "w+b"); |
|
if (!fion) { |
|
perror("fdopen/dup"); |
|
delete [] buffer1; |
|
delete [] buffer2; |
|
return; |
|
} |
|
|
|
int res; |
|
for (int i = 0; i < runs; i++) |
|
{ |
|
// free one block in chain |
|
int firstblock = (offset + i) % size; |
|
res = KDE_fseek(fion, firstblock * blocksize, SEEK_SET); |
|
if (res) |
|
perror("fseek"); |
|
res = fread(buffer1, blocksize, 1, fion); |
|
if (res != 1) |
|
perror("fread"); |
|
int newpos = 0; |
|
for (int j = 1, cursor=firstblock; j < bpr; j++) |
|
{ |
|
cursor = (cursor + offset) % size; |
|
newpos = (cursor - offset + size) % size; |
|
moveBlock(fion, cursor, newpos, buffer2); |
|
} |
|
res = KDE_fseek(fion, i * blocksize, SEEK_SET); |
|
if (res) |
|
perror("fseek"); |
|
res = fwrite(buffer1, blocksize, 1, fion); |
|
if (res != 1) |
|
perror("fwrite"); |
|
} |
|
current = size - 1; |
|
length = size; |
|
|
|
delete [] buffer1; |
|
delete [] buffer2; |
|
|
|
fclose(fion); |
|
|
|
} |
|
|
|
|