Subversion Repositories oidplus

Rev

Rev 745 | Rev 747 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
733 daniel-mar 1
program OIDPLUS;
2
 
3
(************************************************)
4
(* OIDPLUS.PAS                                  *)
5
(* Author:   Daniel Marschall                   *)
744 daniel-mar 6
(* Revision: 2022-02-15                         *)
733 daniel-mar 7
(* License:  Apache 2.0                         *)
8
(* This file contains:                          *)
9
(* - "OIDplus for DOS" program                  *)
10
(************************************************)
11
 
741 daniel-mar 12
(* IMPORTANT:                                                  *)
13
(* When you compile this code with Turbo Pascal 7.01,          *)
14
(* it won't run on fast PCs (Runtime Error 200).               *)
15
(* The built EXE file needs to be patched.                     *)
16
(* The program "PatchCRT" by Kennedy Software                  *)
17
(* WON'T work because it somehow breaks our "_Pause" function. *)
18
(* Instead, use the tool "TPPATCH" by Andreas Bauer.           *)
19
 
733 daniel-mar 20
uses
746 daniel-mar 21
  Dos, Crt, Drivers, StrList, VtsFuncs, VtsCui, OidFile, OidUtils;
733 daniel-mar 22
 
23
const
744 daniel-mar 24
  VERSIONINFO            = 'Revision: 2022-02-15';
735 daniel-mar 25
  DEFAULT_STATUSBAR      = '(C)2020-2022 ViaThinkSoft. Licensed under the terms of the Apache 2.0 license.';
740 daniel-mar 26
  TITLEBAR_LEFT_TEXT     = 'OIDplus';
733 daniel-mar 27
  DISKIO_SOUND_DEBUGGING = false;
735 daniel-mar 28
  DISKIO_SOUND_DELAY     = 500;
29
  ASNEDIT_LINES          = 10;
30
  DESCEDIT_LINES         = 10;
31
  DESCEDIT_PADDING       = 3;
32
  ACTIONMENU_SIZE        = 5;
33
  MAINMENU_WIDTH         = 15;
34
  MAINMENU_HEIGHT        = 3;
35
  MAINMENU_ALLOW_ESC     = false;
740 daniel-mar 36
  TREEVIEW_INDENT        = 0;
37
  TREEVIEW_INCLUDE_DESC  = true;
38
  TREEVIEW_WIDTH         = 80;
744 daniel-mar 39
  OID_EXTENSION          = '.OID';
40
  TREEVIEW_FILENAME      = 'OIDTREE.TXT';
733 daniel-mar 41
 
743 daniel-mar 42
procedure _Pause;
733 daniel-mar 43
begin
743 daniel-mar 44
  DrawStatusBar('Press any key to continue');
746 daniel-mar 45
  CursorOn;
743 daniel-mar 46
  ReadKey;
746 daniel-mar 47
  CursorOff;
743 daniel-mar 48
  DrawStatusBar(DEFAULT_STATUSBAR);
49
end;
50
 
51
function _WriteOidFile(filename: string; oid: POid; ShowErrorMessage: boolean): boolean;
52
var
53
  res: boolean;
54
begin
733 daniel-mar 55
  DrawStatusBar('Write file ' + filename + '...');
743 daniel-mar 56
  res := WriteOidFile(filename, oid);
733 daniel-mar 57
  if DISKIO_SOUND_DEBUGGING then
58
  begin
59
    Sound(70);
60
    Delay(DISKIO_SOUND_DELAY - 10);
61
    NoSound;
62
    Delay(10);
63
  end;
743 daniel-mar 64
  DrawStatusBar(DEFAULT_STATUSBAR);
733 daniel-mar 65
 
743 daniel-mar 66
  _WriteOidFile := res;
67
 
68
  if ShowErrorMessage and not res then
69
  begin
70
    ShowMessage('Cannot write to file ' + filename, 'ERROR', true);
71
    _Pause;
72
  end;
733 daniel-mar 73
end;
74
 
743 daniel-mar 75
function _ReadOidFile(filename: string; oid: POid; ShowErrorMessage: boolean): boolean;
76
var
77
  res: boolean;
733 daniel-mar 78
begin
79
  DrawStatusBar('Read file ' + filename + '...');
743 daniel-mar 80
  res := ReadOidFile(filename, oid);
733 daniel-mar 81
  if DISKIO_SOUND_DEBUGGING then
82
  begin
83
    Sound(50);
84
    Delay(DISKIO_SOUND_DELAY - 10);
85
    NoSound;
86
    Delay(10);
87
  end;
88
  DrawStatusBar(DEFAULT_STATUSBAR);
89
 
743 daniel-mar 90
  _ReadOidFile := res;
91
 
92
  if ShowErrorMessage and not res then
93
  begin
94
    ShowMessage('Cannot read file ' + filename, 'ERROR', true);
95
    _Pause;
96
  end;
733 daniel-mar 97
end;
98
 
740 daniel-mar 99
function _ShowASNIds(childOID: POID): string;
733 daniel-mar 100
var
101
  j, jmax: integer;
102
  sTmp: string;
103
begin
104
  sTmp := '';
735 daniel-mar 105
  jmax := ListCount(childOID^.ASNIds)-1;
733 daniel-mar 106
  for j := 0 to jmax do
107
  begin
108
    if j = 0 then sTmp := sTmp + ' (';
735 daniel-mar 109
    sTmp := sTmp + ListGetElement(childOID^.ASNIds, j);
733 daniel-mar 110
    if j = jmax then
111
      sTmp := sTmp + ')'
112
    else
113
      sTmp := sTmp + ', ';
114
  end;
115
  _ShowASNIds := sTmp;
116
end;
117
 
118
function AsnAlreadyExisting(oid: POID; asnid: string): boolean;
119
begin
741 daniel-mar 120
  AsnAlreadyExisting := ListContains(oid^.AsnIds, asnid);
733 daniel-mar 121
end;
122
 
123
function AsnEditor(oid: POID): boolean;
124
var
125
  asnList: PStringList;
126
  i: integer;
127
  x, y, w, h: integer;
128
  res: integer;
129
  sInput: string;
735 daniel-mar 130
  menuIdNew, menuIdSave, menuIdExit: integer;
733 daniel-mar 131
begin
132
  AsnEditor := false;
133
 
134
  repeat
735 daniel-mar 135
    CreateList(asnList);
733 daniel-mar 136
 
137
    for i := 0 to ListCount(oid^.ASNIds)-1 do
138
    begin
139
      ListAppend(asnList, ListGetElement(oid^.ASNIDs, i));
140
    end;
735 daniel-mar 141
    menuIdNew  := ListAppend(asnList, '<NEW>');
142
    menuIdSave := ListAppend(asnList, '<SAVE>');
143
    menuIdExit := ListAppend(asnList, '<CANCEL>');
733 daniel-mar 144
 
145
    DrawStatusBar(DEFAULT_STATUSBAR);
146
    x := SINGLE_LINE_BOX_PADDING;
147
    y := ScreenHeight div 2 - ASNEDIT_LINES div 2;
148
    w := ScreenWidth - (SINGLE_LINE_BOX_PADDING-1)*2;
149
    h := ASNEDIT_LINES;
150
    res := DrawSelectionList(x, y, w, h,
151
                             asnList, true,
152
                             'EDIT ASN.1 IDENTIFIERS',
153
                             2);
735 daniel-mar 154
    FreeList(asnList);
733 daniel-mar 155
 
156
    (* Change double-border to thin-border *)
157
    DrawThinBorder(x-1, y-1, w+2, h+2);
158
    GoToXY(x+1, y-1);
159
    Write('EDIT ASN.1 IDENTIFIERS');
160
 
161
    if res = -1 then
162
    begin
163
      exit;
164
    end
735 daniel-mar 165
    else if res = menuIdNew then
733 daniel-mar 166
    begin
167
      (* "NEW" item was selected *)
168
      sInput := '';
746 daniel-mar 169
      CursorOn;
733 daniel-mar 170
      repeat
171
        if QueryVal(sInput,
172
                    SINGLE_LINE_BOX_PADDING_INNER,
173
                    ScreenHeight div 2,
174
                    ScreenWidth - (SINGLE_LINE_BOX_PADDING_INNER-1)*2,
175
                    1,
176
                    'ADD SINGLE ASN.1 ID',
177
                    2) then
178
        begin
179
          if sInput = '' then continue;
180
          if not ASN1IDValid(sInput) then
181
          begin
182
            ShowMessage('Invalid ASN1.ID! (Require -, a..z, A..Z, 0..9, begin with a-z)', 'ERROR', true);
183
            _Pause;
184
          end
185
          else if AsnAlreadyExisting(oid, sInput) then
186
          begin
187
            ShowMessage('ASN.1 identifier is already existing on this arc', 'ERROR', true);
188
            _Pause;
189
          end
190
          else
191
          begin
192
            ListAppend(oid^.ASNIDs, sInput);
193
            break;
194
          end;
195
        end
196
        else break;
197
      until false;
746 daniel-mar 198
      CursorOff;
733 daniel-mar 199
    end
735 daniel-mar 200
    else if res = menuIdSave then
733 daniel-mar 201
    begin
202
      (* "SAVE" item was selected *)
203
      AsnEditor := true;
204
      Exit;
205
    end
735 daniel-mar 206
    else if res = menuIdExit then
733 daniel-mar 207
    begin
208
      (* "CANCEL" item was selected *)
209
      AsnEditor := false;
210
      Exit;
211
    end
212
    else
213
    begin
214
      DrawStatusBar('Note: Remove the text to delete the ASN.1 identifier');
215
      sInput := ListGetElement(oid^.ASNIDs, res);
746 daniel-mar 216
          CursorOn;
733 daniel-mar 217
      repeat
218
        if QueryVal(sInput,
219
                    SINGLE_LINE_BOX_PADDING_INNER,
220
                    ScreenHeight div 2,
221
                    ScreenWidth - (SINGLE_LINE_BOX_PADDING_INNER-1)*2,
222
                    1,
223
                    'EDIT SINGLE ASN.1 ID',
224
                    2) then
225
        begin
226
          if sInput = '' then
227
          begin
228
            (* Empty input = Delete ASN.1 ID *)
737 daniel-mar 229
            ListDeleteElementByIndex(oid^.ASNIDs, res);
733 daniel-mar 230
            break;
231
          end
232
          else if not ASN1IDValid(sInput) then
233
          begin
234
            ShowMessage('Invalid ASN1.ID! (Require -, a..z, A..Z, 0..9, begin with a-z)', 'ERROR', true);
235
            _Pause;
236
          end
237
          else if AsnAlreadyExisting(oid, sInput) and
238
              not (ListGetElement(oid^.ASNIDs, res) = sInput) then
239
          begin
240
            ShowMessage('ASN.1 identifier is already existing on this arc', 'ERROR', true);
241
            _Pause;
242
          end
243
          else
244
          begin
245
            ListSetElement(oid^.ASNIDs, res, sInput);
246
            break;
247
          end;
248
        end
249
        else break;
250
      until false;
746 daniel-mar 251
          CursorOff;
733 daniel-mar 252
    end;
253
  until false;
254
end;
255
 
256
function DescEditor(oid: POID): boolean;
257
var
258
  sInput: string;
259
begin
260
  DescEditor := false;
261
 
262
  DrawStatusBar('Note: Press Ctrl+Return for a line-break.');
263
  sInput := oid^.description;
746 daniel-mar 264
  CursorOn;
733 daniel-mar 265
  if QueryVal(sInput,
266
              DESCEDIT_PADDING,
267
              ScreenHeight div 2 - DESCEDIT_LINES div 2,
268
              ScreenWidth - (DESCEDIT_PADDING-1)*2,
269
              DESCEDIT_LINES,
270
              'EDIT DESCRIPTION',
271
              2) then
272
  begin
273
    oid^.description := sInput;
740 daniel-mar 274
    DescEditor := true; (* request caller to save <oid> *)
733 daniel-mar 275
  end;
746 daniel-mar 276
  CursorOff;
733 daniel-mar 277
end;
278
 
279
function NextPossibleFileID: string;
280
var
281
  DirInfo: SearchRec;
282
  list: PStringList;
283
  iId: LongInt;
284
  sId: string;
285
begin
286
  (* Put all found files into a list *)
735 daniel-mar 287
  CreateList(list);
744 daniel-mar 288
  FindFirst(RepeatStr('?',8)+OID_EXTENSION, Archive, DirInfo);
733 daniel-mar 289
  while DosError = 0 do
290
  begin
291
    sId := Copy(DirInfo.Name, 1, 8);
292
    ListAppend(list, sId);
293
    FindNext(DirInfo);
294
  end;
295
 
296
  (* Search for the first non existing item in the list *)
297
  sId := '';
298
  for iId := 0 to 99999999 do
299
  begin
300
    sId := ZeroPad(iId, 8);
301
    if not ListContains(list, sId) then break;
302
  end;
303
  NextPossibleFileId := sId;
304
  FreeList(list);
305
end;
306
 
307
function NumIdAlreadyExisting(parentOID: POID; sInput: string): boolean;
308
var
309
  searchDotNotation: string;
310
  sTmp: string;
311
  i: integer;
312
begin
313
  if parentOID^.DotNotation = '' then
314
    searchDotNotation := sInput
315
  else
316
    searchDotNotation := parentOID^.DotNotation + '.' + sInput;
317
  for i := 0 to ListCount(parentOID^.SubIds)-1 do
318
  begin
319
    sTmp := ListGetElement(parentOID^.SubIds, i);
735 daniel-mar 320
    if DotNotationPart(sTmp) = searchDotNotation then
733 daniel-mar 321
    begin
322
      NumIdAlreadyExisting := true;
323
      exit;
324
    end;
325
  end;
326
  NumIdAlreadyExisting := false;
327
end;
328
 
329
function NumIdEditor(oid: POID; parentOID: POID): boolean;
330
var
331
  sInput: string;
332
begin
333
  NumIdEditor := false;
334
  sInput := '';
335
 
746 daniel-mar 336
  CursorOn;
733 daniel-mar 337
  repeat
338
    if QueryVal(sInput,
339
                SINGLE_LINE_BOX_PADDING_INNER,
340
                ScreenHeight div 2,
341
                ScreenWidth - (SINGLE_LINE_BOX_PADDING_INNER-1)*2,
342
                1,
343
                'ENTER NUMERIC ID',
344
                2) then
345
    begin
346
      if sInput = '' then continue;
739 daniel-mar 347
      if not IsPositiveInteger(sInput) then
733 daniel-mar 348
      begin
349
        ShowMessage('Invalid numeric ID (must be a positive integer)', 'ERROR', true);
350
        _Pause;
351
      end
352
      else if (parentOID^.DotNotation='') and (StrToInt(sInput) > 2) then
353
      begin
354
        ShowMessage('Invalid numeric ID (root arc can only be 0, 1, or 2)', 'ERROR', true);
355
        _Pause;
356
      end
357
      else if ((parentOID^.DotNotation='0') or (parentOID^.DotNotation='1')) and (StrToInt(sInput) > 39) then
358
      begin
359
        ShowMessage('Invalid numeric ID (root 0 and 1 must have sub-arc of 0..39)', 'ERROR', true);
360
        _Pause;
361
      end
362
      else if NumIdAlreadyExisting(parentOID, sInput) then
363
      begin
364
        ShowMessage('This numeric ID is already used in this arc', 'ERROR', true);
365
        _Pause;
366
      end
367
      else
368
      begin
369
        if parentOID^.DotNotation = '' then
370
          oid^.DotNotation := sInput
371
        else
372
          oid^.DotNotation := parentOID^.DotNotation + '.' + sInput;
740 daniel-mar 373
        NumIdEditor := true; (* request caller to save <oid> *)
746 daniel-mar 374
        Break;
733 daniel-mar 375
      end;
376
    end
377
    else
378
    begin
746 daniel-mar 379
      Break;
733 daniel-mar 380
    end;
381
  until false;
746 daniel-mar 382
  CursorOff;
733 daniel-mar 383
end;
384
 
385
function NewOidEditor(oid: POID): boolean;
386
var
387
  newfilename: string;
735 daniel-mar 388
  newOID: POID;
733 daniel-mar 389
begin
390
  NewOidEditor := false;
391
 
735 daniel-mar 392
  CreateOidDef(newOID);
393
  newOID^.FileId := NextPossibleFileID;
745 daniel-mar 394
  newOID^.ParentFileId := oid^.FileId;
395
  newOID^.ParentDotNotation := oid^.DotNotation;
735 daniel-mar 396
  if NumIdEditor(newOID, oid) and
397
     AsnEditor(newOID) and
398
     DescEditor(newOID) then
734 daniel-mar 399
  begin
744 daniel-mar 400
    newfilename := newOID^.FileId + OID_EXTENSION;
743 daniel-mar 401
    if _WriteOidFile(newfilename, newOID, true) then
402
    begin
403
      (* Add link to original file and enable the saving of it *)
404
      ListAppend(oid^.SubIds, newOID^.FileId + newOID^.DotNotation);
405
      NewOidEditor := true; (* request caller to save <oid> *)
406
    end;
734 daniel-mar 407
  end;
735 daniel-mar 408
  FreeOidDef(newOID);
733 daniel-mar 409
end;
410
 
411
procedure DeleteChildrenRecursive(oid: POID);
412
var
413
  i: integer;
735 daniel-mar 414
  childOID: POID;
733 daniel-mar 415
  filenameChild: string;
416
begin
417
  for i := 0 to ListCount(oid^.SubIds)-1 do
418
  begin
744 daniel-mar 419
    filenameChild := FileIdPart(ListGetElement(oid^.SubIds, i)) + OID_EXTENSION;
740 daniel-mar 420
    if FileExists(filenameChild) then
421
    begin
422
      CreateOidDef(childOID);
744 daniel-mar 423
      if _ReadOidFile(filenameChild, childOID, false) and
745 daniel-mar 424
         (childOID^.ParentFileId = oid^.FileId) and
425
         (childOID^.ParentDotNotation = oid^.DotNotation) then
743 daniel-mar 426
      begin
427
        DeleteChildrenRecursive(childOID);
428
      end;
740 daniel-mar 429
      FreeOidDef(childOID);
430
      DeleteFile(filenameChild);
431
    end;
733 daniel-mar 432
  end;
433
  ListClear(oid^.SubIds);
434
end;
435
 
436
procedure DeleteOidRecursive(selfOID: POID);
437
var
438
  i: integer;
735 daniel-mar 439
  parentOID: POID;
733 daniel-mar 440
  filenameSelf, filenameParent: string;
441
begin
442
  (* Remove all children and their files recursively *)
443
  DeleteChildrenRecursive(selfOID);
444
 
445
  (* Remove forward reference in parent OID *)
743 daniel-mar 446
  (* (this is the most important part)      *)
745 daniel-mar 447
  filenameParent := selfOID^.ParentFileId + OID_EXTENSION;
740 daniel-mar 448
  if FileExists(filenameParent) then
733 daniel-mar 449
  begin
740 daniel-mar 450
    CreateOidDef(parentOID);
743 daniel-mar 451
    if _ReadOidFile(filenameParent, parentOID, true) then
740 daniel-mar 452
    begin
743 daniel-mar 453
      if ListDeleteElementByValue(parentOID^.SubIds, selfOID^.FileId + selfOID^.DotNotation) then
454
      begin
455
        _WriteOidFile(filenameParent, parentOID, true);
456
      end;
740 daniel-mar 457
    end;
458
    FreeOidDef(parentOID);
733 daniel-mar 459
  end;
460
 
461
  (* Delete own file *)
744 daniel-mar 462
  filenameSelf := selfOID^.FileId + OID_EXTENSION;
740 daniel-mar 463
  if FileExists(filenameSelf) then
464
  begin
465
    DeleteFile(filenameSelf);
466
  end;
733 daniel-mar 467
end;
468
 
735 daniel-mar 469
function _DeleteConfirmation: boolean;
470
var
471
  sc: Char;
472
begin
473
  repeat
474
    ShowMessage('Are you sure you want to delete this OID? (Y/N)', 'DELETE OID', true);
737 daniel-mar 475
    DrawStatusBar('Y=Yes, N=No');
735 daniel-mar 476
 
746 daniel-mar 477
    CursorOn;
735 daniel-mar 478
    sc := ReadKey;
746 daniel-mar 479
    CursorOff;
735 daniel-mar 480
    if sc = #0 then
481
    begin
482
      (* Extended key. Nothing we care about. *)
483
      ReadKey;
484
      continue;
485
    end;
486
 
487
    if UpCase(sc) = 'Y' then
488
    begin
489
      _DeleteConfirmation := true;
490
      break;
737 daniel-mar 491
    end
492
    else if UpCase(sc) = 'N' then
735 daniel-mar 493
    begin
494
      _DeleteConfirmation := false;
495
      break;
496
    end;
497
  until false;
498
end;
499
 
737 daniel-mar 500
procedure _DrawOidTitleBar(filename: string; oid: POID);
501
begin
502
  if oid^.DotNotation = '' then
738 daniel-mar 503
    DrawTitleBar('OID ROOT', TITLEBAR_LEFT_TEXT, filename)
737 daniel-mar 504
  else
738 daniel-mar 505
    DrawTitleBar('OID ' + oid^.DotNotation, TITLEBAR_LEFT_TEXT, filename);
737 daniel-mar 506
end;
507
 
733 daniel-mar 508
procedure DisplayOIDFile(filename: string);
509
var
510
  isRoot: boolean;
740 daniel-mar 511
  oid, tmpOID: POID;
733 daniel-mar 512
  i, menuX, menuY: integer;
513
  linesLeft, linesRequired: integer;
514
  sTmp, subfile: string;
515
  subsel, subfiles: PStringList;
516
  subselres: integer;
735 daniel-mar 517
  exitRequest: boolean;
518
  menuIdExit, menuIdAsnEdit, menuIdDescEdit, menuIdAdd, menuIdDelete: integer;
733 daniel-mar 519
begin
735 daniel-mar 520
  exitRequest := false;
733 daniel-mar 521
  repeat
743 daniel-mar 522
    CreateOidDef(oid);
523
 
524
    if not _ReadOidFile(filename, oid, true) then
740 daniel-mar 525
    begin
743 daniel-mar 526
      FreeOidDef(oid);
740 daniel-mar 527
      exit;
528
    end;
529
 
733 daniel-mar 530
    (* Print OID information *)
531
 
532
    ClrScr;
737 daniel-mar 533
    _DrawOidTitleBar(filename, oid);
733 daniel-mar 534
    DrawStatusBar(DEFAULT_STATUSBAR);
535
    GotoXY(1,2);
536
 
735 daniel-mar 537
    if oid^.DotNotation <> '' then
733 daniel-mar 538
    begin
539
      WriteLn('Dot-Notation:');
735 daniel-mar 540
      WriteLn(oid^.DotNotation);
733 daniel-mar 541
      WriteLn('');
542
    end;
543
 
735 daniel-mar 544
    if Trim(oid^.Description) <> '' then
733 daniel-mar 545
    begin
546
      WriteLn('Description:');
735 daniel-mar 547
      WriteLn(oid^.Description);
733 daniel-mar 548
      WriteLn('');
549
    end;
550
 
551
    menuX := WhereX + 1;
735 daniel-mar 552
    menuY := ScreenHeight - ACTIONMENU_SIZE - 1;
733 daniel-mar 553
 
735 daniel-mar 554
    if ListCount(oid^.ASNIDs) > 0 then
733 daniel-mar 555
    begin
556
      linesLeft := menuY - WhereY - 1;
735 daniel-mar 557
      linesRequired := 1 + ListCount(oid^.ASNIds);
733 daniel-mar 558
 
559
      if LinesLeft < LinesRequired then
560
      begin
561
        (* Compact display of ASN.1 identifiers *)
562
        Write('ASN.1-Identifiers: ');
735 daniel-mar 563
        for i := 0 to ListCount(oid^.ASNIds)-1 do
733 daniel-mar 564
        begin
565
          if i > 0 then Write(', ');
735 daniel-mar 566
          Write(ListGetElement(oid^.ASNIds, i));
733 daniel-mar 567
        end;
568
        WriteLn('');
569
      end
570
      else
571
      begin
572
        (* Long display of ASN.1 identifiers *)
573
        WriteLn('ASN.1-Identifiers:');
735 daniel-mar 574
        for i := 0 to ListCount(oid^.ASNIds)-1 do
733 daniel-mar 575
        begin
735 daniel-mar 576
          WriteLn('- '+ListGetElement(oid^.ASNIds, i));
733 daniel-mar 577
        end;
578
        WriteLn('');
579
      end;
580
    end;
581
 
582
    (* Now prepare the menu entries *)
583
 
744 daniel-mar 584
    CreateList(subsel);   (* Contains the human-readable OID name *)
737 daniel-mar 585
    CreateList(subfiles); (* Contains the file name               *)
733 daniel-mar 586
 
745 daniel-mar 587
    if oid^.ParentFileId = '' then
733 daniel-mar 588
    begin
589
      isRoot := true;
590
    end
591
    else
592
    begin
745 daniel-mar 593
      isRoot := oid^.ParentDotNotation = oid^.DotNotation;
733 daniel-mar 594
    end;
595
 
745 daniel-mar 596
    if (oid^.ParentFileId <> '') and not isRoot then
733 daniel-mar 597
    begin
745 daniel-mar 598
      subfile := oid^.ParentFileId + OID_EXTENSION;
740 daniel-mar 599
      if FileExists(subfile) then
600
      begin
601
        CreateOidDef(tmpOID);
744 daniel-mar 602
        if not _ReadOidFile(subfile, tmpOID, true) then
603
        begin
745 daniel-mar 604
          ListAppend(subsel, 'Go to parent ' + oid^.ParentDotNotation + ' (READ ERROR)');
744 daniel-mar 605
          ListAppend(subfiles, 'ERROR: '+subfile+' Read error or file invalid');
606
        end
743 daniel-mar 607
        else
744 daniel-mar 608
        begin
745 daniel-mar 609
          ListAppend(subsel, 'Go to parent ' + oid^.ParentDotNotation + _ShowASNIds(tmpOID));
744 daniel-mar 610
          ListAppend(subfiles, subfile);
611
        end;
740 daniel-mar 612
        FreeOidDef(tmpOID);
613
      end
614
      else
615
      begin
745 daniel-mar 616
        ListAppend(subsel, 'Go to parent ' + oid^.ParentDotNotation + ' (FILE NOT FOUND)');
744 daniel-mar 617
        ListAppend(subfiles, 'ERROR: File '+subfile+' was not found');
740 daniel-mar 618
      end;
733 daniel-mar 619
    end;
620
 
621
    if isRoot then
622
    begin
735 daniel-mar 623
      menuIdExit := ListAppend(subsel, 'Back to main menu');
624
      ListAppend(subfiles, '');
625
    end
626
    else menuIdExit := -99;
733 daniel-mar 627
 
735 daniel-mar 628
    for i := 0 to ListCount(oid^.SubIds)-1 do
733 daniel-mar 629
    begin
735 daniel-mar 630
      sTmp := ListGetElement(oid^.SubIds, i);
744 daniel-mar 631
      subfile := FileIdPart(sTmp) + OID_EXTENSION;
740 daniel-mar 632
      if FileExists(subfile) then
633
      begin
634
        CreateOidDef(tmpOID);
744 daniel-mar 635
        if not _ReadOidFile(subfile, tmpOID, true) then
636
        begin
637
          ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + ' (READ ERROR)');
638
          ListAppend(subfiles, 'ERROR: Read error at file '+subfile+', or file is invalid.');
639
        end
745 daniel-mar 640
        else if (tmpOID^.ParentFileId <> oid^.FileId) or
641
                (tmpOID^.ParentDotNotation <> oid^.DotNotation) then
744 daniel-mar 642
        begin
643
          ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + ' (BAD BACKREF)');
644
          ListAppend(subfiles, 'ERROR: File '+subfile+' has a wrong back-reference.');
645
        end
743 daniel-mar 646
        else
744 daniel-mar 647
        begin
648
          ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + _ShowASNIds(tmpOID));
649
          ListAppend(subfiles, subfile);
650
        end;
740 daniel-mar 651
        FreeOidDef(tmpOID);
652
      end
653
      else
654
      begin
655
        ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + ' (FILE NOT FOUND)');
744 daniel-mar 656
        ListAppend(subfiles, 'ERROR: File '+subfile+' was not found');
740 daniel-mar 657
      end;
733 daniel-mar 658
    end;
659
 
735 daniel-mar 660
    if oid^.DotNotation <> '' then
733 daniel-mar 661
    begin
735 daniel-mar 662
      menuIdAsnEdit := ListAppend(subsel, 'Edit ASN.1 identifiers');
663
      ListAppend(subfiles, '');
664
    end
665
    else menuIdAsnEdit := -99;
733 daniel-mar 666
 
735 daniel-mar 667
    menuIdDescEdit := ListAppend(subsel, 'Edit description');
668
    ListAppend(subfiles, '');
733 daniel-mar 669
 
735 daniel-mar 670
    menuIdAdd := ListAppend(subsel, 'Add child');
671
    ListAppend(subfiles, '');
733 daniel-mar 672
 
673
    if not isRoot then
674
    begin
735 daniel-mar 675
      menuIdDelete := ListAppend(subsel, 'Delete OID');
676
      ListAppend(subfiles, '');
677
    end
678
    else menuIdDelete := -99;
733 daniel-mar 679
 
734 daniel-mar 680
    (* Show menu *)
681
 
733 daniel-mar 682
    subselres := DrawSelectionList(menuX, menuY,
683
                                   ScreenWidth-2,
735 daniel-mar 684
                                   ACTIONMENU_SIZE,
733 daniel-mar 685
                                   subsel,
686
                                   true,
687
                                   'SELECT ACTION',
688
                                   1);
734 daniel-mar 689
 
690
    (* Process user selection *)
691
 
733 daniel-mar 692
    if subselres = -1 then
693
    begin
735 daniel-mar 694
      exitRequest := true;
733 daniel-mar 695
    end
735 daniel-mar 696
    else if subselres = menuIdAsnEdit then
733 daniel-mar 697
    begin
735 daniel-mar 698
      if AsnEditor(oid) then
743 daniel-mar 699
        _WriteOidFile(filename, oid, true);
735 daniel-mar 700
    end
701
    else if subselres = menuIdDescEdit then
702
    begin
703
      if DescEditor(oid) then
743 daniel-mar 704
        _WriteOidFile(filename, oid, true);
735 daniel-mar 705
    end
706
    else if subselres = menuIdAdd then
707
    begin
708
      if NewOidEditor(oid) then
743 daniel-mar 709
        _WriteOidFile(filename, oid, true);
735 daniel-mar 710
    end
711
    else if subselres = menuIdDelete then
712
    begin
713
      if _DeleteConfirmation then
733 daniel-mar 714
      begin
745 daniel-mar 715
        sTmp := oid^.ParentFileId + OID_EXTENSION;
735 daniel-mar 716
        DeleteOidRecursive(oid);
740 daniel-mar 717
        if FileExists(sTmp) then
718
        begin
719
          filename := sTmp;
720
        end
721
        else
722
        begin
744 daniel-mar 723
          ShowMessage('Parent file ' + sTmp + ' was not found', 'ERROR', true);
740 daniel-mar 724
          _Pause;
725
          exitRequest := true;
726
        end;
733 daniel-mar 727
      end;
735 daniel-mar 728
    end
729
    else if subselres = menuIdExit then
730
    begin
731
      exitRequest := true;
732
    end
733
    else
734
    begin
735
      (* Normal OID *)
744 daniel-mar 736
      (* Above we already checked if the files are valild and existing *)
740 daniel-mar 737
      sTmp := ListGetElement(subfiles, subselres);
744 daniel-mar 738
      if Copy(sTmp, 1, Length('ERROR: ')) = 'ERROR: ' then
740 daniel-mar 739
      begin
744 daniel-mar 740
        Delete(sTmp, 1, Length('ERROR: '));
741
        ShowMessage(sTmp, 'ERROR', true);
742
        _Pause;
740 daniel-mar 743
      end
744
      else
745
      begin
744 daniel-mar 746
        filename := sTmp;
740 daniel-mar 747
      end;
733 daniel-mar 748
    end;
749
    FreeList(subsel);
750
    FreeList(subfiles);
751
 
735 daniel-mar 752
    FreeOidDef(oid);
753
  until exitRequest;
733 daniel-mar 754
end;
755
 
743 daniel-mar 756
function CreateRootOIDFile(filename: string; ShowErrorMessage: boolean): boolean;
733 daniel-mar 757
var
735 daniel-mar 758
  oid: POID;
733 daniel-mar 759
begin
735 daniel-mar 760
  CreateOidDef(oid);
745 daniel-mar 761
  oid^.Description  := 'This is the root of the OID tree.' +#13#10 +
762
                       #13#10 +
763
                       'Valid subsequent arcs are per definition:' + #13#10 +
764
                       '- 0 (itu-t)' + #13#10 +
765
                       '- 1 (iso)' + #13#10 +
766
                       '- 2 (joint-iso-itu-t)';
767
  oid^.FileId       := ZeroPad(0, 8);
768
  oid^.DotNotation  := '';
769
  oid^.ParentFileId := ZeroPad(0, 8);
770
  oid^.ParentDotNotation := '';
743 daniel-mar 771
  CreateRootOIDFile := _WriteOidFile(filename, oid, ShowErrorMessage);
735 daniel-mar 772
  FreeOidDef(oid);
733 daniel-mar 773
end;
774
 
742 daniel-mar 775
function _GetRootFile(ShowErrorMessage: boolean): string;
735 daniel-mar 776
var
742 daniel-mar 777
  rootFile: string;
733 daniel-mar 778
begin
744 daniel-mar 779
  rootFile := ZeroPad(0, 8) + OID_EXTENSION;
743 daniel-mar 780
  _GetRootFile := rootFile;
742 daniel-mar 781
  if not FileExists(rootFile) then
733 daniel-mar 782
  begin
743 daniel-mar 783
    if not CreateRootOIDFile(rootFile, ShowErrorMessage) then
742 daniel-mar 784
    begin
743 daniel-mar 785
      _GetRootFile := '';
742 daniel-mar 786
    end;
787
  end;
733 daniel-mar 788
end;
789
 
740 daniel-mar 790
procedure OP_ManageOIDs;
742 daniel-mar 791
var
792
  rootfile: string;
740 daniel-mar 793
begin
794
  ClrScr;
795
  DrawTitleBar('Manage Object Identifiers', TITLEBAR_LEFT_TEXT, '');
796
  DrawStatusBar('Loading data... please wait...');
797
 
742 daniel-mar 798
  (* This will try creating a new root file if it does not exist *)
799
  rootfile := _GetRootFile(true);
800
  if rootfile = '' then Exit;
801
 
802
  DisplayOIDFile(rootfile);
740 daniel-mar 803
end;
804
 
733 daniel-mar 805
procedure OP_ManageRAs;
806
begin
807
  ClrScr;
738 daniel-mar 808
  DrawTitleBar('Manage Registration Authorities', TITLEBAR_LEFT_TEXT, '');
733 daniel-mar 809
  DrawStatusBar('');
810
 
811
  (* TODO: Implement "Manage RAs" feature *)
812
end;
813
 
814
procedure OP_ReturnToMSDOS;
815
begin
816
  ClrScr;
743 daniel-mar 817
  TextBackground(Black);
818
  TextColor(LightGray);
746 daniel-mar 819
  ClrScr; (*Important, so that the DOS command prompt is also LightGray *)
820
 
741 daniel-mar 821
  WriteLn('Thank you for using OIDplus for DOS.');
743 daniel-mar 822
  WriteLn('');
733 daniel-mar 823
end;
824
 
741 daniel-mar 825
function _GetTreeViewLine(oid: POID; indent: integer): string;
740 daniel-mar 826
var
827
  i: integer;
828
  sTmp: string;
829
begin
830
  (* Build line *)
831
  sTmp := RepeatStr(' ', indent*TREEVIEW_INDENT);
832
  if oid^.DotNotation = '' then
833
    sTmp := sTmp + 'Object Identifiers'
834
  else
835
    sTmp := sTmp + oid^.DotNotation;
836
  sTmp := sTmp + _ShowAsnIds(oid);
837
  if TREEVIEW_INCLUDE_DESC then
838
  begin
839
    if Trim(oid^.Description) <> '' then
840
    begin
841
      sTmp := sTmp + ': ' + oid^.Description;
842
    end;
843
  end;
844
  for i := 1 to Length(sTmp) do
845
  begin
846
    if (sTmp[i]=#13) or (sTmp[i]=#10) then sTmp[i] := ' ';
847
  end;
744 daniel-mar 848
  sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
741 daniel-mar 849
  _GetTreeViewLine := sTmp;
740 daniel-mar 850
end;
851
 
852
procedure _RecTreeExport(oid: POID; var F: Text; indent: integer);
853
var
854
  i: integer;
855
  sTmp: string;
856
  suboid: POID;
857
  childFilename: string;
858
begin
741 daniel-mar 859
  sTmp := _GetTreeViewLine(oid, indent);
740 daniel-mar 860
  sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
861
  WriteLn(F, sTmp);
862
 
863
  (* Recursively call children *)
864
  for i := 0 to ListCount(oid^.SubIds)-1 do
865
  begin
866
    sTmp := ListGetElement(oid^.SubIds, i);
867
    CreateOidDef(suboid);
744 daniel-mar 868
    childFilename := FileIdPart(sTmp) + OID_EXTENSION;
743 daniel-mar 869
    if not FileExists(childFilename) then
740 daniel-mar 870
    begin
744 daniel-mar 871
      sTmp := 'ERROR: MISSING ' + childFilename + ' (SHALL CONTAIN ' + DotNotationPart(sTmp) + ')!';
743 daniel-mar 872
      sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
873
      WriteLn(F, sTmp);
740 daniel-mar 874
    end
743 daniel-mar 875
    else if not _ReadOidFile(childFilename, suboid, false) then
740 daniel-mar 876
    begin
744 daniel-mar 877
      sTmp := 'ERROR: READ ERROR AT ' + childFilename + ' (SHALL CONTAIN ' + DotNotationPart(sTmp) + ')!';
740 daniel-mar 878
      sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
879
      WriteLn(F, sTmp);
743 daniel-mar 880
    end
745 daniel-mar 881
    else if (suboid^.ParentFileId <> oid^.FileId) or
882
            (suboid^.ParentDotNotation <> oid^.DotNotation) then
744 daniel-mar 883
    begin
884
      (* This can happen if a file is missing, and then another OID gets this filename since the number seems to be free *)
885
      sTmp := 'ERROR: BAD BACKREF AT ' + childFilename + ' (SHALL CONTAIN ' + DotNotationPart(sTmp) + ')!';
886
      sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
887
      WriteLn(F, sTmp);
888
    end
743 daniel-mar 889
    else
890
    begin
891
      _RecTreeExport(suboid, F, indent+1);
892
      FreeOidDef(suboid);
893
    end
740 daniel-mar 894
  end;
895
end;
896
 
897
procedure OP_TreeView;
898
var
899
  F: Text;
900
  rootoid: POID;
742 daniel-mar 901
  rootfile: string;
743 daniel-mar 902
  res: boolean;
740 daniel-mar 903
begin
904
  ClrScr;
905
  DrawTitleBar('TreeView Export', TITLEBAR_LEFT_TEXT, '');
906
  DrawStatusBar('Exporting data... please wait...');
907
 
742 daniel-mar 908
  (* This will try creating a new root file if it does not exist *)
743 daniel-mar 909
  rootfile := _GetRootFile(true);
910
  if rootfile = '' then
911
  begin
912
    DrawStatusBar(DEFAULT_STATUSBAR);
913
    Exit;
914
  end;
742 daniel-mar 915
 
744 daniel-mar 916
  Assign(F, TREEVIEW_FILENAME);
743 daniel-mar 917
  {$I-}
740 daniel-mar 918
  Rewrite(F);
743 daniel-mar 919
  {$I+}
920
  if IoResult <> 0 then
921
  begin
922
    (* Can happen if disk is read-only (Runtime Error 150) *)
744 daniel-mar 923
    ShowMessage('Cannot open '+TREEVIEW_FILENAME+' for writing.', 'ERROR', true);
743 daniel-mar 924
    _Pause;
925
    DrawStatusBar(DEFAULT_STATUSBAR);
926
    Exit;
927
  end;
740 daniel-mar 928
 
743 daniel-mar 929
  res := false;
740 daniel-mar 930
  CreateOidDef(rootoid);
743 daniel-mar 931
  if _ReadOidFile(rootfile, rootoid, true) then
932
  begin
933
    _RecTreeExport(rootoid, F, 0);
934
    res := true;
935
  end;
740 daniel-mar 936
  FreeOidDef(rootoid);
937
 
743 daniel-mar 938
  Close(F);
939
 
740 daniel-mar 940
  DrawStatusBar(DEFAULT_STATUSBAR);
743 daniel-mar 941
  if res then
942
  begin
744 daniel-mar 943
    ShowMessage('TreeView successfully exported as '+TREEVIEW_FILENAME, 'TREEVIEW EXPORT', true);
743 daniel-mar 944
    _Pause;
945
  end;
740 daniel-mar 946
end;
947
 
733 daniel-mar 948
procedure OP_MainMenu;
949
var
950
  menu: PStringList;
951
  menuRes, menuLeft, menuTop: integer;
740 daniel-mar 952
  menuIdOID, menuIdRA, menuIdTree, menuIdExit: integer;
733 daniel-mar 953
begin
954
  repeat
955
    ClrScr;
956
 
738 daniel-mar 957
    DrawTitleBar('Welcome to OIDplus for DOS', '', '');
733 daniel-mar 958
    DrawStatusBar(DEFAULT_STATUSBAR);
959
    GoToXY(ScreenWidth-Length(VERSIONINFO), ScreenHeight-1);
960
    Write(VERSIONINFO);
961
 
735 daniel-mar 962
    CreateList(menu);
963
 
964
    menuIdOID  := ListAppend(menu, 'Manage OIDs');
965
    menuIdRA   := -99; (*ListAppend(menu, 'Manage RAs');*)
740 daniel-mar 966
    menuIdTree := ListAppend(menu, 'Export TreeView');
735 daniel-mar 967
    menuIdExit := ListAppend(menu, 'Return to DOS');
968
 
969
    menuLeft := round(ScreenWidth/2 -MAINMENU_WIDTH/2);
970
    menuTop  := round(ScreenHeight/2-MAINMENU_HEIGHT/2);
971
    menuRes  := DrawSelectionList(menuLeft, menuTop,
972
                                  MAINMENU_WIDTH, MAINMENU_HEIGHT,
973
                                  menu, true, 'MAIN MENU', 2);
733 daniel-mar 974
    FreeList(menu);
975
 
735 daniel-mar 976
    if menuRes = menuIdOID then
733 daniel-mar 977
    begin
978
      OP_ManageOIDs;
735 daniel-mar 979
    end
980
    else if menuRes = menuIdRA then
733 daniel-mar 981
    begin
982
      OP_ManageRAs;
740 daniel-mar 983
    end
984
    else if menuRes = menuIdTree then
985
    begin
986
      OP_Treeview;
733 daniel-mar 987
    end;
735 daniel-mar 988
  until (menuRes = menuIdExit) or (MAINMENU_ALLOW_ESC and (menuRes = -1));
733 daniel-mar 989
 
990
  OP_ReturnToMSDOS;
991
end;
992
 
993
begin
746 daniel-mar 994
  InitVideo; (* sets ScreenWidth and ScreenHeight *)
995
  CursorOff;
733 daniel-mar 996
  OP_MainMenu;
746 daniel-mar 997
  DoneVideo;
733 daniel-mar 998
end.