Subversion Repositories oidplus

Rev

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