source: ogBrowser-Git/qtermwidget/src/History.cpp

qndtest
Last change on this file was 050d67a, checked in by adelcastillo <adelcastillo@…>, 15 years ago

Ahora el browser tiene consola en vez del output.
Pasado todo el sistema de compilacion a cmake.

git-svn-id: https://opengnsys.es/svn/trunk@408 a21b9725-9963-47de-94b9-378ad31fedc9

  • Property mode set to 100644
File size: 15.4 KB
Line 
1/*
2    This file is part of Konsole, an X terminal.
3    Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
4
5    Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301  USA.
21*/
22
23// Own
24#include "History.h"
25
26// System
27#include <iostream>
28#include <stdlib.h>
29#include <assert.h>
30#include <stdio.h>
31#include <sys/types.h>
32#include <sys/mman.h>
33#include <unistd.h>
34#include <errno.h>
35
36
37// Reasonable line size
38#define LINE_SIZE       1024
39
40using namespace Konsole;
41
42/*
43   An arbitrary long scroll.
44
45   One can modify the scroll only by adding either cells
46   or newlines, but access it randomly.
47
48   The model is that of an arbitrary wide typewriter scroll
49   in that the scroll is a serie of lines and each line is
50   a serie of cells with no overwriting permitted.
51
52   The implementation provides arbitrary length and numbers
53   of cells and line/column indexed read access to the scroll
54   at constant costs.
55
56KDE4: Can we use QTemporaryFile here, instead of KTempFile?
57
58FIXME: some complain about the history buffer comsuming the
59       memory of their machines. This problem is critical
60       since the history does not behave gracefully in cases
61       where the memory is used up completely.
62
63       I put in a workaround that should handle it problem
64       now gracefully. I'm not satisfied with the solution.
65
66FIXME: Terminating the history is not properly indicated
67       in the menu. We should throw a signal.
68
69FIXME: There is noticeable decrease in speed, also. Perhaps,
70       there whole feature needs to be revisited therefore.
71       Disadvantage of a more elaborated, say block-oriented
72       scheme with wrap around would be it's complexity.
73*/
74
75//FIXME: tempory replacement for tmpfile
76//       this is here one for debugging purpose.
77
78//#define tmpfile xTmpFile
79
80// History File ///////////////////////////////////////////
81
82/*
83  A Row(X) data type which allows adding elements to the end.
84*/
85
86HistoryFile::HistoryFile()
87  : ion(-1),
88    length(0),
89        fileMap(0)
90{
91  if (tmpFile.open())
92  { 
93    tmpFile.setAutoRemove(true);
94    ion = tmpFile.handle();
95  }
96}
97
98HistoryFile::~HistoryFile()
99{
100        if (fileMap)
101                unmap();
102}
103
104//TODO:  Mapping the entire file in will cause problems if the history file becomes exceedingly large,
105//(ie. larger than available memory).  HistoryFile::map() should only map in sections of the file at a time,
106//to avoid this.
107void HistoryFile::map()
108{
109        assert( fileMap == 0 );
110
111        fileMap = (char*)mmap( 0 , length , PROT_READ , MAP_PRIVATE , ion , 0 );
112
113    //if mmap'ing fails, fall back to the read-lseek combination
114    if ( fileMap == MAP_FAILED )
115    {
116            readWriteBalance = 0; 
117            fileMap = 0;
118            qDebug() << ": mmap'ing history failed.  errno = " << errno;
119    }
120}
121
122void HistoryFile::unmap()
123{
124        int result = munmap( fileMap , length );
125        assert( result == 0 );
126
127        fileMap = 0;
128}
129
130bool HistoryFile::isMapped()
131{
132        return (fileMap != 0);
133}
134
135void HistoryFile::add(const unsigned char* bytes, int len)
136{
137  if ( fileMap )
138                  unmap();
139               
140  readWriteBalance++;
141
142  int rc = 0;
143
144  rc = lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; }
145  rc = write(ion,bytes,len);       if (rc < 0) { perror("HistoryFile::add.write"); return; }
146  length += rc;
147}
148
149void HistoryFile::get(unsigned char* bytes, int len, int loc)
150{
151  //count number of get() calls vs. number of add() calls. 
152  //If there are many more get() calls compared with add()
153  //calls (decided by using MAP_THRESHOLD) then mmap the log
154  //file to improve performance.
155  readWriteBalance--;
156  if ( !fileMap && readWriteBalance < MAP_THRESHOLD )
157                  map();
158
159  if ( fileMap )
160  {
161        for (int i=0;i<len;i++)
162                        bytes[i]=fileMap[loc+i];
163  }
164  else
165  {     
166        int rc = 0;
167
168        if (loc < 0 || len < 0 || loc + len > length)
169        fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc);
170        rc = lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; }
171        rc = read(ion,bytes,len);     if (rc < 0) { perror("HistoryFile::get.read"); return; }
172  }
173}
174
175int HistoryFile::len()
176{
177  return length;
178}
179
180
181// History Scroll abstract base class //////////////////////////////////////
182
183
184HistoryScroll::HistoryScroll(HistoryType* t)
185  : m_histType(t)
186{
187}
188
189HistoryScroll::~HistoryScroll()
190{
191  delete m_histType;
192}
193
194bool HistoryScroll::hasScroll()
195{
196  return true;
197}
198
199// History Scroll File //////////////////////////////////////
200
201/*
202   The history scroll makes a Row(Row(Cell)) from
203   two history buffers. The index buffer contains
204   start of line positions which refere to the cells
205   buffer.
206
207   Note that index[0] addresses the second line
208   (line #1), while the first line (line #0) starts
209   at 0 in cells.
210*/
211
212HistoryScrollFile::HistoryScrollFile(const QString &logFileName)
213  : HistoryScroll(new HistoryTypeFile(logFileName)),
214  m_logFileName(logFileName)
215{
216}
217
218HistoryScrollFile::~HistoryScrollFile()
219{
220}
221 
222int HistoryScrollFile::getLines()
223{
224  return index.len() / sizeof(int);
225}
226
227int HistoryScrollFile::getLineLen(int lineno)
228{
229  return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character);
230}
231
232bool HistoryScrollFile::isWrappedLine(int lineno)
233{
234  if (lineno>=0 && lineno <= getLines()) {
235    unsigned char flag;
236    lineflags.get((unsigned char*)&flag,sizeof(unsigned char),(lineno)*sizeof(unsigned char));
237    return flag;
238  }
239  return false;
240}
241
242int HistoryScrollFile::startOfLine(int lineno)
243{
244  if (lineno <= 0) return 0;
245  if (lineno <= getLines())
246    { 
247       
248        if (!index.isMapped())
249                        index.map();
250       
251        int res;
252    index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int));
253    return res;
254    }
255  return cells.len();
256}
257
258void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[])
259{
260  cells.get((unsigned char*)res,count*sizeof(Character),startOfLine(lineno)+colno*sizeof(Character));
261}
262
263void HistoryScrollFile::addCells(const Character text[], int count)
264{
265  cells.add((unsigned char*)text,count*sizeof(Character));
266}
267
268void HistoryScrollFile::addLine(bool previousWrapped)
269{
270  if (index.isMapped())
271                  index.unmap();
272
273  int locn = cells.len();
274  index.add((unsigned char*)&locn,sizeof(int));
275  unsigned char flags = previousWrapped ? 0x01 : 0x00;
276  lineflags.add((unsigned char*)&flags,sizeof(unsigned char));
277}
278
279
280// History Scroll Buffer //////////////////////////////////////
281HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount)
282  : HistoryScroll(new HistoryTypeBuffer(maxLineCount))
283   ,_historyBuffer()
284   ,_maxLineCount(0)
285   ,_usedLines(0)
286   ,_head(0)
287{
288  setMaxNbLines(maxLineCount);
289}
290
291HistoryScrollBuffer::~HistoryScrollBuffer()
292{
293    delete[] _historyBuffer;
294}
295
296void HistoryScrollBuffer::addCellsVector(const QVector<Character>& cells)
297{
298    _head++;
299    if ( _usedLines < _maxLineCount )
300        _usedLines++;
301
302    if ( _head >= _maxLineCount )
303    {
304        _head = 0;
305    }
306
307    _historyBuffer[bufferIndex(_usedLines-1)] = cells;
308    _wrappedLine[bufferIndex(_usedLines-1)] = false;
309}
310void HistoryScrollBuffer::addCells(const Character a[], int count)
311{
312  HistoryLine newLine(count);
313  qCopy(a,a+count,newLine.begin());
314
315  addCellsVector(newLine);
316}
317
318void HistoryScrollBuffer::addLine(bool previousWrapped)
319{
320    _wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped;
321}
322
323int HistoryScrollBuffer::getLines()
324{
325    return _usedLines;
326}
327
328int HistoryScrollBuffer::getLineLen(int lineNumber)
329{
330  Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
331
332  if ( lineNumber < _usedLines )
333  {
334    return _historyBuffer[bufferIndex(lineNumber)].size();
335  }
336  else
337  {
338    return 0;
339  }
340}
341
342bool HistoryScrollBuffer::isWrappedLine(int lineNumber)
343{
344  Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
345   
346  if (lineNumber < _usedLines)
347  {
348    //kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)];
349    return _wrappedLine[bufferIndex(lineNumber)];
350  }
351  else
352    return false;
353}
354
355void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, Character* buffer)
356{
357  if ( count == 0 ) return;
358
359  Q_ASSERT( lineNumber < _maxLineCount );
360
361  if (lineNumber >= _usedLines) 
362  {
363    memset(buffer, 0, count * sizeof(Character));
364    return;
365  }
366 
367  const HistoryLine& line = _historyBuffer[bufferIndex(lineNumber)];
368
369  //kDebug() << "startCol " << startColumn;
370  //kDebug() << "line.size() " << line.size();
371  //kDebug() << "count " << count;
372
373  Q_ASSERT( startColumn <= line.size() - count );
374   
375  memcpy(buffer, line.constData() + startColumn , count * sizeof(Character));
376}
377
378void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount)
379{
380    HistoryLine* oldBuffer = _historyBuffer;
381    HistoryLine* newBuffer = new HistoryLine[lineCount];
382   
383    for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ )
384    {
385        newBuffer[i] = oldBuffer[bufferIndex(i)];
386    }
387   
388    _usedLines = qMin(_usedLines,(int)lineCount);
389    _maxLineCount = lineCount;
390    _head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1;
391
392    _historyBuffer = newBuffer;
393    delete[] oldBuffer;
394
395    _wrappedLine.resize(lineCount);
396}
397
398int HistoryScrollBuffer::bufferIndex(int lineNumber)
399{
400    Q_ASSERT( lineNumber >= 0 );
401    Q_ASSERT( lineNumber < _maxLineCount );
402    Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head );
403
404    if ( _usedLines == _maxLineCount )
405    {
406        return (_head+lineNumber+1) % _maxLineCount;
407    }
408    else
409    {   
410        return lineNumber;
411    }
412}
413
414
415// History Scroll None //////////////////////////////////////
416
417HistoryScrollNone::HistoryScrollNone()
418  : HistoryScroll(new HistoryTypeNone())
419{
420}
421
422HistoryScrollNone::~HistoryScrollNone()
423{
424}
425
426bool HistoryScrollNone::hasScroll()
427{
428  return false;
429}
430
431int  HistoryScrollNone::getLines()
432{
433  return 0;
434}
435
436int  HistoryScrollNone::getLineLen(int)
437{
438  return 0;
439}
440
441bool HistoryScrollNone::isWrappedLine(int /*lineno*/)
442{
443  return false;
444}
445
446void HistoryScrollNone::getCells(int, int, int, Character [])
447{
448}
449
450void HistoryScrollNone::addCells(const Character [], int)
451{
452}
453
454void HistoryScrollNone::addLine(bool)
455{
456}
457
458// History Scroll BlockArray //////////////////////////////////////
459
460HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size)
461  : HistoryScroll(new HistoryTypeBlockArray(size))
462{
463  m_blockArray.setHistorySize(size); // nb. of lines.
464}
465
466HistoryScrollBlockArray::~HistoryScrollBlockArray()
467{
468}
469
470int  HistoryScrollBlockArray::getLines()
471{
472  return m_lineLengths.count();
473}
474
475int  HistoryScrollBlockArray::getLineLen(int lineno)
476{
477    if ( m_lineLengths.contains(lineno) )
478        return m_lineLengths[lineno];
479    else
480        return 0;
481}
482
483bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/)
484{
485  return false;
486}
487
488void HistoryScrollBlockArray::getCells(int lineno, int colno,
489                                       int count, Character res[])
490{
491  if (!count) return;
492
493  const Block *b = m_blockArray.at(lineno);
494
495  if (!b) {
496    memset(res, 0, count * sizeof(Character)); // still better than random data
497    return;
498  }
499
500  assert(((colno + count) * sizeof(Character)) < ENTRIES);
501  memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character));
502}
503
504void HistoryScrollBlockArray::addCells(const Character a[], int count)
505{
506  Block *b = m_blockArray.lastBlock();
507 
508  if (!b) return;
509
510  // put cells in block's data
511  assert((count * sizeof(Character)) < ENTRIES);
512
513  memset(b->data, 0, ENTRIES);
514
515  memcpy(b->data, a, count * sizeof(Character));
516  b->size = count * sizeof(Character);
517
518  size_t res = m_blockArray.newBlock();
519  assert (res > 0);
520  Q_UNUSED( res );
521
522  m_lineLengths.insert(m_blockArray.getCurrent(), count);
523}
524
525void HistoryScrollBlockArray::addLine(bool)
526{
527}
528
529//////////////////////////////////////////////////////////////////////
530// History Types
531//////////////////////////////////////////////////////////////////////
532
533HistoryType::HistoryType()
534{
535}
536
537HistoryType::~HistoryType()
538{
539}
540
541//////////////////////////////
542
543HistoryTypeNone::HistoryTypeNone()
544{
545}
546
547bool HistoryTypeNone::isEnabled() const
548{
549  return false;
550}
551
552HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const
553{
554  delete old;
555  return new HistoryScrollNone();
556}
557
558int HistoryTypeNone::maximumLineCount() const
559{
560  return 0;
561}
562
563//////////////////////////////
564
565HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size)
566  : m_size(size)
567{
568}
569
570bool HistoryTypeBlockArray::isEnabled() const
571{
572  return true;
573}
574
575int HistoryTypeBlockArray::maximumLineCount() const
576{
577  return m_size;
578}
579
580HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const
581{
582  delete old;
583  return new HistoryScrollBlockArray(m_size);
584}
585
586
587//////////////////////////////
588
589HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines)
590  : m_nbLines(nbLines)
591{
592}
593
594bool HistoryTypeBuffer::isEnabled() const
595{
596  return true;
597}
598
599int HistoryTypeBuffer::maximumLineCount() const
600{
601  return m_nbLines;
602}
603
604HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const
605{
606  if (old)
607  {
608    HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old);
609    if (oldBuffer)
610    {
611       oldBuffer->setMaxNbLines(m_nbLines);
612       return oldBuffer;
613    }
614
615    HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines);
616    int lines = old->getLines();
617    int startLine = 0;
618    if (lines > (int) m_nbLines)
619       startLine = lines - m_nbLines;
620
621    Character line[LINE_SIZE];
622    for(int i = startLine; i < lines; i++)
623    {
624       int size = old->getLineLen(i);
625       if (size > LINE_SIZE)
626       {
627          Character *tmp_line = new Character[size];
628          old->getCells(i, 0, size, tmp_line);
629          newScroll->addCells(tmp_line, size);
630          newScroll->addLine(old->isWrappedLine(i));
631          delete [] tmp_line;
632       }
633       else
634       {
635          old->getCells(i, 0, size, line);
636          newScroll->addCells(line, size);
637          newScroll->addLine(old->isWrappedLine(i));
638       }
639    }
640    delete old;
641    return newScroll;
642  }
643  return new HistoryScrollBuffer(m_nbLines);
644}
645
646//////////////////////////////
647
648HistoryTypeFile::HistoryTypeFile(const QString& fileName)
649  : m_fileName(fileName)
650{
651}
652
653bool HistoryTypeFile::isEnabled() const
654{
655  return true;
656}
657
658const QString& HistoryTypeFile::getFileName() const
659{
660  return m_fileName;
661}
662
663HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const
664{
665  if (dynamic_cast<HistoryFile *>(old)) 
666     return old; // Unchanged.
667
668  HistoryScroll *newScroll = new HistoryScrollFile(m_fileName);
669
670  Character line[LINE_SIZE];
671  int lines = (old != 0) ? old->getLines() : 0;
672  for(int i = 0; i < lines; i++)
673  {
674     int size = old->getLineLen(i);
675     if (size > LINE_SIZE)
676     {
677        Character *tmp_line = new Character[size];
678        old->getCells(i, 0, size, tmp_line);
679        newScroll->addCells(tmp_line, size);
680        newScroll->addLine(old->isWrappedLine(i));
681        delete [] tmp_line;
682     }
683     else
684     {
685        old->getCells(i, 0, size, line);
686        newScroll->addCells(line, size);
687        newScroll->addLine(old->isWrappedLine(i));
688     }
689  }
690
691  delete old;
692  return newScroll; 
693}
694
695int HistoryTypeFile::maximumLineCount() const
696{
697  return 0;
698}
Note: See TracBrowser for help on using the repository browser.