~ubuntu-branches/ubuntu/vivid/ddrescueview/vivid-backports

« back to all changes in this revision

Viewing changes to source/Parser.pas

  • Committer: Package Import Robot
  • Author(s): Graham Inggs
  • Date: 2015-06-02 12:30:03 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20150602123003-vq5snysd2m5i20ek
Tags: 0.4~alpha2-1
* New upstream release
* Update d/rules, d/watch since upstream now ship an xz-compressed tarball
* Drop d/patches/linker-options.patch included upstream
* Drop manpage, desktop file and xpm icon since these files are now shipped
  by upstream, update d/ddrecueview.install, d/manpages accordingly
* Add build-depends on libgtk2.0-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
uses Classes, Shared;
26
26
 
27
27
type
28
 
 
29
 
  { TLogParser }
30
 
 
31
 
  TLogParser = class(TObservablePersistent)
 
28
  TSimpleParser = class(TObservablePersistent)
32
29
  private
33
30
    FLog : TLog;
34
31
    FRescueStatus : TRescueStatus;
35
32
    FComments : TStringList; // comment lines of logfile
 
33
    FVersion : String; // ddrescue version in first comment line / for future use?
36
34
    FLogStream : TStream;
37
35
    FFileName : String;
38
36
    procedure logMsg(msg : String);
39
37
    function isEmptyLine(strLine : String) : boolean;
40
38
    function isCommentLine(strLine : String) : boolean;
41
39
  public
 
40
    constructor Create;
42
41
    destructor Destroy; override;
43
42
    procedure OpenFile(filename : String);
44
43
    procedure CloseFile;
45
44
    procedure parse();
46
45
    function hasFile() : boolean;
 
46
    property rescueStatus : TRescueStatus read FRescueStatus;
47
47
    property log : TLog read FLog;
48
48
    property CommentLines : TStringList read FComments;
49
 
    property rescueStatus : TRescueStatus read FRescueStatus;
 
49
    property Version : String read FVersion;
50
50
    property LogFileName :String read FFileName;
51
51
  end;
52
52
 
 
53
  // This parser can also embed a domain log file parser
 
54
  TLogParser = class(TObservablePersistent)
 
55
  private
 
56
    FLogParser : TSimpleParser;
 
57
    FDomParser : TSimpleParser;
 
58
    FLog: TLog;
 
59
    FRescueStatus : TRescueStatus;
 
60
    FContiguous : Boolean;
 
61
    FHasFile : Boolean;
 
62
    function getLog(): TLog;
 
63
    procedure postParse();
 
64
    procedure setContiguous(mode: Boolean);
 
65
  public
 
66
    constructor Create;
 
67
    destructor Destroy; override;
 
68
    procedure OpenFile(filename : String);
 
69
    procedure OpenDomainFile(filename : String);
 
70
    procedure CloseFile;
 
71
    procedure parse();
 
72
    function hasFile() : boolean;
 
73
    function hasDomFile() : Boolean;
 
74
    property rescueStatus : TRescueStatus read FRescueStatus;
 
75
    property log : TLog read getLog;
 
76
    property CommentLines : TStringList read FLogParser.FComments;
 
77
    property Version : String read FLogParser.FVersion;
 
78
    property LogFileName : String read FLogParser.FFileName;
 
79
    property DomFileName : String read FDomParser.FFileName;
 
80
    property ContiguousDomain : Boolean read FContiguous write setContiguous;
 
81
  end;
53
82
 
54
83
implementation
55
 
uses SysUtils, GUI;
 
84
uses SysUtils, Math, GUI;
 
85
 
 
86
{ TLogParser }
 
87
 
 
88
constructor TLogParser.Create;
 
89
begin
 
90
  inherited;
 
91
  FRescueStatus := emptyRescueStatus;
 
92
  FLogParser:=TSimpleParser.Create;
 
93
  FDomParser:=TSimpleParser.Create;
 
94
end;
56
95
 
57
96
destructor TLogParser.Destroy;
58
97
begin
59
98
  CloseFile();
60
 
  inherited;
61
 
end;
62
 
 
63
 
procedure TLogParser.OpenFile(filename : String);
 
99
  FreeAndNil(FDomParser);
 
100
  FreeAndNil(FLogParser);
 
101
  inherited Destroy;
 
102
end;
 
103
 
 
104
procedure TLogParser.OpenFile(filename: String);
 
105
begin
 
106
  CloseFile;
 
107
  FLogParser.OpenFile(filename);
 
108
  postParse;
 
109
end;
 
110
 
 
111
procedure TLogParser.OpenDomainFile(filename: String);
 
112
begin
 
113
  FDomParser.OpenFile(filename);
 
114
  if FDomParser.hasFile and FContiguous then begin
 
115
    FHasFile:=false;
 
116
    FPONotifyObservers(self, ooDeleteItem, nil);
 
117
  end;
 
118
  postParse;
 
119
end;
 
120
 
 
121
procedure TLogParser.CloseFile;
 
122
begin
 
123
  FDomParser.CloseFile;
 
124
  FLogParser.CloseFile;
 
125
  FRescueStatus := emptyRescueStatus;
 
126
  SetLength(FLog, 0);
 
127
  FHasFile:=false;
 
128
  FPONotifyObservers(self, ooDeleteItem, nil);
 
129
end;
 
130
 
 
131
procedure TLogParser.postParse;
 
132
var
 
133
  ResLog, DomLog: TLog;
 
134
  i, iRes, iDom : Longint;
 
135
  newEntry : TLogEntry;
 
136
  logCurOffset : Int64;
 
137
begin
 
138
  if FDomParser.hasFile then begin // post-process the logs
 
139
    FRescueStatus:=emptyRescueStatus;
 
140
    ResLog:=FLogParser.log;
 
141
    DomLog:=FDomParser.log;
 
142
    //build log and rescuestatus
 
143
    i:=0; iRes:=0; iDom:=0; // current log indices
 
144
    logCurOffset:=0; // needed for contiguous mode, but also used for normal mode
 
145
    while iDom < Length(DomLog) do begin
 
146
      if (DomLog[iDom].status<>'+') then begin // outside domain entry
 
147
        if not FContiguous then begin
 
148
          newEntry.status:='d';
 
149
          newEntry.offset:=DomLog[iDom].offset;
 
150
          newEntry.length:=DomLog[iDom].length;
 
151
          inc(FRescueStatus.outsidedomain, DomLog[iDom].length);
 
152
          logCurOffset:=newEntry.offset+newEntry.length;
 
153
          if InRange(FLogParser.rescueStatus.pos, newEntry.offset, logCurOffset-1) then
 
154
             FRescueStatus.pos:=FLogParser.rescueStatus.pos;
 
155
          if Length(FLog) <= i then SetLength(FLog, i+1024);
 
156
          FLog[i]:=newEntry;
 
157
          Inc(i);
 
158
        end;
 
159
      end else begin // iterate over the ResEntries intersecting current DomEntry
 
160
        while iRes < Length(ResLog) do begin
 
161
          // increase iRes until intersecting dom block
 
162
          if ResLog[iRes].offset+ResLog[iRes].length <= DomLog[iDom].offset then begin
 
163
            Inc(iRes);
 
164
            Continue;
 
165
          end;
 
166
          // if intersecting current dom block, create new block
 
167
          newEntry:=IntersectEntries(ResLog[iRes], DomLog[iDom]);
 
168
          if newEntry.length > 0 then begin //can be 0 for rescue logfiles with gaps
 
169
            if InRange(FLogParser.rescueStatus.pos, newEntry.offset,
 
170
                       newEntry.offset+newEntry.length-1) then
 
171
              FRescueStatus.pos:=logCurOffset+FLogParser.rescueStatus.pos-newEntry.offset;
 
172
            newEntry.offset:=logCurOffset;
 
173
            logCurOffset+=newEntry.length;
 
174
            if Length(FLog) <= i then SetLength(FLog, i+1024);
 
175
            FLog[i]:=newEntry;
 
176
            Inc(i);
 
177
            case newEntry.status of
 
178
              '?' : inc(FRescueStatus.nontried, newEntry.length);
 
179
              '+' : inc(FRescueStatus.rescued, newEntry.length);
 
180
              '*' : inc(FRescueStatus.nontrimmed, newEntry.length);
 
181
              '/' : inc(FRescueStatus.nonscraped, newEntry.length);
 
182
              '-' : begin
 
183
                      inc(FRescueStatus.bad, newEntry.length);
 
184
                      inc(FRescueStatus.errors);
 
185
                    end;
 
186
            end;
 
187
          end;
 
188
          // ensure iRes is set to last intersecting entry
 
189
          if ResLog[iRes].offset+ResLog[iRes].length >=
 
190
             DomLog[iDom].offset+DomLog[iDom].length then break;
 
191
          Inc(iRes);
 
192
        end;
 
193
      end;
 
194
      Inc(iDom);
 
195
    end;
 
196
    SetLength(FLog, i);
 
197
    FRescueStatus.curOperation:=FLogParser.rescueStatus.curOperation;
 
198
    FRescueStatus.strCurOperation:=FLogParser.rescueStatus.strCurOperation;
 
199
    if i > 0 then begin  // calculate device's size
 
200
      FRescueStatus.devicesize:=FLog[i-1].offset + FLog[i-1].length;
 
201
    end;
 
202
    FRescueStatus.suggestedBlockSize:=FLogParser.rescueStatus.suggestedBlockSize;
 
203
  end else begin
 
204
    FRescueStatus:=FLogParser.rescueStatus;
 
205
  end;
 
206
  if (not FHasFile) and FLogParser.hasFile then begin  // just been opened
 
207
    FHasFile:=true;
 
208
    FPONotifyObservers(self, ooAddItem, nil);
 
209
  end else FPONotifyObservers(self, ooChange, nil)
 
210
end;
 
211
 
 
212
procedure TLogParser.setContiguous(mode: Boolean);
 
213
begin
 
214
  if mode <> FContiguous then begin
 
215
    FContiguous:=mode;
 
216
    if FDomParser.hasFile then begin
 
217
      FHasFile:=false;
 
218
      FPONotifyObservers(self, ooDeleteItem, nil);
 
219
      postParse;
 
220
    end;
 
221
  end;
 
222
end;
 
223
 
 
224
procedure TLogParser.parse;
 
225
begin
 
226
  if FLogParser.hasFile() then FLogParser.parse();
 
227
  if FDomParser.hasFile() then FDomParser.parse();
 
228
  postParse;
 
229
end;
 
230
 
 
231
function TLogParser.hasFile: boolean;
 
232
begin
 
233
  result:=FHasFile;
 
234
end;
 
235
 
 
236
function TLogParser.hasDomFile: Boolean;
 
237
begin
 
238
  result:=FHasFile and FDomParser.hasFile;
 
239
end;
 
240
 
 
241
function TLogParser.getLog: TLog;
 
242
begin
 
243
  if not FDomParser.hasFile then result:=FLogParser.log
 
244
  else result:=FLog;
 
245
end;
 
246
 
 
247
 
 
248
{ TSimpleParser }
 
249
 
 
250
constructor TSimpleParser.Create;
 
251
begin
 
252
  inherited;
 
253
  FRescueStatus := emptyRescueStatus;
 
254
end;
 
255
 
 
256
destructor TSimpleParser.Destroy;
 
257
begin
 
258
  CloseFile();
 
259
  inherited;
 
260
end;
 
261
 
 
262
procedure TSimpleParser.OpenFile(filename : String);
64
263
begin
65
264
  try
66
265
    CloseFile(); // close already open file
72
271
  except
73
272
    on E: Exception do begin
74
273
        CloseFile;
75
 
        logMsg('Error: '+E.Message);
 
274
        logMsg('Parser error: '+E.Message);
76
275
    end;
77
276
  end;
78
277
end;
79
278
 
80
 
procedure TLogParser.CloseFile;
 
279
procedure TSimpleParser.CloseFile;
81
280
begin
82
281
  FFileName := '';
83
282
  FreeAndNil(FComments);
88
287
end;
89
288
 
90
289
(* returns true if a line is empty *)
91
 
function TLogParser.isEmptyLine(strLine : String) : boolean;
 
290
function TSimpleParser.isEmptyLine(strLine : String) : boolean;
92
291
begin
93
292
  strLine := TrimLeft(strLine);
94
293
  result := (Length(strLine) = 0); // empty line
95
294
end;
96
295
 
97
296
(* returns true if a line is a comment line *)
98
 
function TLogParser.isCommentLine(strLine : String) : boolean;
 
297
function TSimpleParser.isCommentLine(strLine : String) : boolean;
99
298
begin
100
 
  result := (strLine[1] = '#'); // comment line
 
299
  result := (not isEmptyLine(strLine)) and (strLine[1] = '#'); // comment line
101
300
end;
102
301
 
103
 
 
104
302
(* add a log line to the application log *)
105
 
procedure TLogParser.logMsg(msg : String);
 
303
procedure TSimpleParser.logMsg(msg : String);
106
304
begin
107
305
  MainForm.DbgLog.Lines.Add(msg);
108
306
end;
109
307
 
110
 
(* Parse a log file.
 
308
(* Parse the log file.
111
309
   After parsing, the results are available in
112
 
   the properties log and rescueStatus *)
113
 
procedure TLogParser.parse();
 
310
   the properties log, rescueStatus and Comments *)
 
311
procedure TSimpleParser.parse();
114
312
var
115
313
  line : string;
116
314
  token : array[0..2] of string;
117
315
  i, logEntry, lineIdx, idx : Integer;
118
 
  logStrings : TStringList;
119
 
  comments_end : boolean;
 
316
  logStrings : TStringList = nil;
 
317
  comments_end, prevHadFile : boolean;
120
318
begin
121
 
  if not Assigned(FLogStream) then begin
 
319
  // make sure the file is open
 
320
  if not hasFile then begin
122
321
    logMsg('Parser: No log file opened.');
123
322
    FPONotifyObservers(self, ooChange, nil);
124
323
    exit;
125
324
  end;
126
325
 
 
326
  // read file contents into string list
127
327
  try
128
 
    FLogStream.Seek(0, soFromBeginning);
129
328
    logStrings := TStringList.Create;
 
329
    if FLogStream.Seek(0, soFromBeginning) <> 0 then
 
330
      raise Exception.Create('Seek error!');
130
331
    logStrings.LoadFromStream(FLogStream);
131
 
    logMsg('Parsing log file: ' +IntToStr(logStrings.Count) + ' lines.');
 
332
    if (FLogStream.Size = 0) or (logStrings.Count = 0) then begin
 
333
      logMsg('Parser: log file seems empty, trying to reopen.');
 
334
      FreeAndNil(FLogStream);
 
335
      FLogStream := TFileStream.Create(FFilename, fmOpenRead or fmShareDenyNone);
 
336
      FLogStream.Seek(0, soFromBeginning);
 
337
      logStrings.LoadFromStream(FLogStream);
 
338
    end;
 
339
    logMsg('Reading log file: ' +IntToStr(logStrings.Count) + ' lines.');
132
340
  except
133
341
    on E : Exception do begin
134
342
      logMsg('Error: Cannot read log file: '+E.Message+'('+E.ClassName+')');
 
343
      FreeAndNil(logStrings);
135
344
      FPONotifyObservers(self, ooChange, nil);
136
345
      exit;
137
346
    end;
138
347
  end;
139
348
 
 
349
  // initialize stuff to zero
140
350
  FRescueStatus := emptyRescueStatus;
141
 
  lineIdx := -1;
 
351
  FComments.Clear;
 
352
  FVersion:='';
 
353
  lineIdx := 0;
142
354
  logEntry := 0;
143
355
  comments_end := false;
 
356
  prevHadFile := Length(log) > 0;
144
357
 
 
358
  // parse the logfile lines into log array, rescueStatus and Comments.
145
359
  try
146
 
    repeat
147
 
      inc(lineIdx);
 
360
    while lineIdx < logStrings.Count do begin
148
361
      line:=logStrings[lineIdx];
149
362
      if isCommentLine(line) then begin
 
363
        // copy comment lines into FComments
150
364
        if comments_end or (pos('# current_pos', line) > 0) then
151
365
          comments_end:=true
152
366
        else FComments.Add(line);
153
367
        // process comment info
154
368
        if pos('Command line:', line) > 0 then begin
155
369
           repeat // not a loop, but goto replacement
156
 
               // try to find ddrescue's block size argument
157
 
               idx := pos(' -b', line);
158
 
               if idx <> 0 then idx := idx+3 // point to start of number
159
 
               else begin
160
 
                 idx := pos(' --block-size=', line);
161
 
                 if idx <> 0 then idx := idx+14 // point to start of number
162
 
                 else break; // argument not found, jump over the rest
163
 
               end;
164
 
               token[0] :=TrimLeft(Copy(line, idx, Length(line)));
165
 
               idx := pos(' ', token[0]); //space after argument
166
 
               if idx <> 0 then token[0] :=Copy(token[0], 1, idx-1);
167
 
               FRescueStatus.suggestedBlockSize:=StrToIntDef(token[0], 0);
 
370
             // try to find ddrescue's block size argument
 
371
             idx := pos(' -b', line);
 
372
             if idx <> 0 then idx := idx+3 // point to start of number
 
373
             else begin
 
374
               idx := pos(' --block-size=', line);
 
375
               if idx <> 0 then idx := idx+14 // point to start of number
 
376
               else break; // argument not found, jump over the rest
 
377
             end;
 
378
             token[0] :=TrimLeft(Copy(line, idx, Length(line)));
 
379
             idx := pos(' ', token[0]); //space after argument
 
380
             if idx <> 0 then token[0] :=Copy(token[0], 1, idx-1);
 
381
             FRescueStatus.suggestedBlockSize:=StrToIntDef(token[0], DEF_BSIZE);
168
382
           until true;
169
383
        end;
 
384
        idx:=pos('ddrescue version ', line);
 
385
        if idx > 0 then FVersion := Copy(line, idx+17, 1337);
170
386
      end else if not isEmptyLine(line) then begin
171
387
        // split line into maximum of 3 tokens
172
388
        line:=Trim(line);
182
398
        end;
183
399
        if (token[0] <> '') and (token[1] <> '') then begin
184
400
          if token[2] <> '' then begin // standard block line (3 tokens)
185
 
            if Length(FLog) = logEntry then SetLength(FLog, logEntry+256); // allocate more space
 
401
            // allocate more space if needed
 
402
            if Length(FLog) = logEntry then SetLength(FLog, logEntry+1024);
186
403
            FLog[logEntry].offset:=StrToInt64(token[0]);
187
404
            FLog[logEntry].length:=StrToInt64(token[1]);
188
405
            FLog[logEntry].status:=token[2][1];
191
408
              '+' : inc(FRescueStatus.rescued, FLog[logEntry].length);
192
409
              '*' : inc(FRescueStatus.nontrimmed, FLog[logEntry].length);
193
410
              '/' : inc(FRescueStatus.nonscraped, FLog[logEntry].length);
194
 
              '-' : inc(FRescueStatus.bad, FLog[logEntry].length);
 
411
              '-' : begin
 
412
                      inc(FRescueStatus.bad, FLog[logEntry].length);
 
413
                      inc(FRescueStatus.errors);
 
414
                    end;
195
415
            end;
196
 
            if FLog[logEntry].status in ['-', '*', '/'] then inc(FRescueStatus.errors);
197
416
            inc(logEntry);
198
 
          end else begin // found the status line (2 components)
 
417
          end else begin // found the status line (2 tokens)
199
418
            if FRescueStatus.pos = 0 then begin
200
 
              FRescueStatus.pos:=StrToInt64(token[0]);
 
419
              FRescueStatus.pos:=StrToInt64Def(token[0], 0);
201
420
              FRescueStatus.curOperation:=token[1][1];
202
421
              FRescueStatus.strCurOperation:=OperationToText(FRescueStatus.curOperation);
203
422
            end else begin
204
 
              logMsg('Parser: found more than one line with 2 tokens.');
 
423
              logMsg('Parser: Not enough tokens in line '+inttostr(lineIdx));
205
424
            end;
206
425
          end;
207
426
        end;
208
427
      end;
209
 
    until lineIdx = logStrings.Count-1;
 
428
      lineIdx+=1;
 
429
    end;
210
430
    SetLength(FLog, logEntry); // trim array to actually needed size
211
431
  except
212
 
    on E : Exception do logMsg('Error parsing log file: '+E.Message+'('+E.ClassName+')');
 
432
    on E : Exception do begin
 
433
      logMsg('Error parsing log file: '+E.Message+'('+E.ClassName+')');
 
434
      FRescueStatus := emptyRescueStatus; // some fail-safe values
 
435
      SetLength(FLog, 1);
 
436
      FLog[0].offset:=0;
 
437
      FLog[0].length:=DEF_BSIZE*(1 shl 18); // one block of 2^18 bad sectors
 
438
      FLog[0].status:='-';
 
439
      FRescueStatus.bad:=FLog[0].length;
 
440
      FRescueStatus.devicesize:=FLog[0].length;
 
441
    end;
213
442
  end;
214
443
  FreeAndNil(logStrings);
215
444
 
216
 
  if logEntry > 0 then begin
 
445
  // notify listeners
 
446
  if Length(FLog) > 0 then begin
217
447
    // calculate device's size from last block's offset and length
218
 
    FRescueStatus.devicesize:=FLog[logEntry-1].offset + FLog[logEntry-1].length;
219
 
  end else logMsg('Parser: No blocks in logfile!');
220
 
  // notify any observers of the changes
221
 
  FPONotifyObservers(self, ooChange, nil);
 
448
    FRescueStatus.devicesize:=FLog[Length(FLog)-1].offset + FLog[Length(FLog)-1].length;
 
449
    if prevHadFile then FPONotifyObservers(self, ooChange, nil)
 
450
      else FPONotifyObservers(self, ooAddItem, nil); // notify of new file
 
451
  end else begin
 
452
    logMsg('Parser: No blocks in logfile!');
 
453
    FPONotifyObservers(self, ooChange, nil); // notify any observers of the changes
 
454
  end;
222
455
end;
223
456
 
224
 
function TLogParser.hasFile: boolean;
 
457
function TSimpleParser.hasFile: boolean;
225
458
begin
226
 
  hasFile:=Length(Flog)<>0;
 
459
  hasFile:=Assigned(FLogStream);
227
460
end;
228
461
 
229
462
end.