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