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