Subversion Repositories oidplus

Rev

Rev 743 | Rev 745 | 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;
389
  newOID^.Parent := oid^.FileId + oid^.DotNotation;
390
  if NumIdEditor(newOID, oid) and
391
     AsnEditor(newOID) and
392
     DescEditor(newOID) then
734 daniel-mar 393
  begin
744 daniel-mar 394
    newfilename := newOID^.FileId + OID_EXTENSION;
743 daniel-mar 395
    if _WriteOidFile(newfilename, newOID, true) then
396
    begin
397
      (* Add link to original file and enable the saving of it *)
398
      ListAppend(oid^.SubIds, newOID^.FileId + newOID^.DotNotation);
399
      NewOidEditor := true; (* request caller to save <oid> *)
400
    end;
734 daniel-mar 401
  end;
735 daniel-mar 402
  FreeOidDef(newOID);
733 daniel-mar 403
end;
404
 
405
procedure DeleteChildrenRecursive(oid: POID);
406
var
407
  i: integer;
735 daniel-mar 408
  childOID: POID;
733 daniel-mar 409
  filenameChild: string;
410
begin
411
  for i := 0 to ListCount(oid^.SubIds)-1 do
412
  begin
744 daniel-mar 413
    filenameChild := FileIdPart(ListGetElement(oid^.SubIds, i)) + OID_EXTENSION;
740 daniel-mar 414
    if FileExists(filenameChild) then
415
    begin
416
      CreateOidDef(childOID);
744 daniel-mar 417
      if _ReadOidFile(filenameChild, childOID, false) and
418
         (childOID^.Parent = oid^.FileId + oid^.DotNotation) then
743 daniel-mar 419
      begin
420
        DeleteChildrenRecursive(childOID);
421
      end;
740 daniel-mar 422
      FreeOidDef(childOID);
423
      DeleteFile(filenameChild);
424
    end;
733 daniel-mar 425
  end;
426
  ListClear(oid^.SubIds);
427
end;
428
 
429
procedure DeleteOidRecursive(selfOID: POID);
430
var
431
  i: integer;
735 daniel-mar 432
  parentOID: POID;
733 daniel-mar 433
  filenameSelf, filenameParent: string;
434
begin
435
  (* Remove all children and their files recursively *)
436
  DeleteChildrenRecursive(selfOID);
437
 
438
  (* Remove forward reference in parent OID *)
743 daniel-mar 439
  (* (this is the most important part)      *)
744 daniel-mar 440
  filenameParent := FileIdPart(selfOID^.Parent) + OID_EXTENSION;
740 daniel-mar 441
  if FileExists(filenameParent) then
733 daniel-mar 442
  begin
740 daniel-mar 443
    CreateOidDef(parentOID);
743 daniel-mar 444
    if _ReadOidFile(filenameParent, parentOID, true) then
740 daniel-mar 445
    begin
743 daniel-mar 446
      if ListDeleteElementByValue(parentOID^.SubIds, selfOID^.FileId + selfOID^.DotNotation) then
447
      begin
448
        _WriteOidFile(filenameParent, parentOID, true);
449
      end;
740 daniel-mar 450
    end;
451
    FreeOidDef(parentOID);
733 daniel-mar 452
  end;
453
 
454
  (* Delete own file *)
744 daniel-mar 455
  filenameSelf := selfOID^.FileId + OID_EXTENSION;
740 daniel-mar 456
  if FileExists(filenameSelf) then
457
  begin
458
    DeleteFile(filenameSelf);
459
  end;
733 daniel-mar 460
end;
461
 
735 daniel-mar 462
function _DeleteConfirmation: boolean;
463
var
464
  sc: Char;
465
begin
466
  repeat
467
    ShowMessage('Are you sure you want to delete this OID? (Y/N)', 'DELETE OID', true);
737 daniel-mar 468
    DrawStatusBar('Y=Yes, N=No');
735 daniel-mar 469
 
470
    sc := ReadKey;
471
    if sc = #0 then
472
    begin
473
      (* Extended key. Nothing we care about. *)
474
      ReadKey;
475
      continue;
476
    end;
477
 
478
    if UpCase(sc) = 'Y' then
479
    begin
480
      _DeleteConfirmation := true;
481
      break;
737 daniel-mar 482
    end
483
    else if UpCase(sc) = 'N' then
735 daniel-mar 484
    begin
485
      _DeleteConfirmation := false;
486
      break;
487
    end;
488
  until false;
489
end;
490
 
737 daniel-mar 491
procedure _DrawOidTitleBar(filename: string; oid: POID);
492
begin
493
  if oid^.DotNotation = '' then
738 daniel-mar 494
    DrawTitleBar('OID ROOT', TITLEBAR_LEFT_TEXT, filename)
737 daniel-mar 495
  else
738 daniel-mar 496
    DrawTitleBar('OID ' + oid^.DotNotation, TITLEBAR_LEFT_TEXT, filename);
737 daniel-mar 497
end;
498
 
733 daniel-mar 499
procedure DisplayOIDFile(filename: string);
500
var
501
  isRoot: boolean;
740 daniel-mar 502
  oid, tmpOID: POID;
733 daniel-mar 503
  i, menuX, menuY: integer;
504
  linesLeft, linesRequired: integer;
505
  sTmp, subfile: string;
506
  subsel, subfiles: PStringList;
507
  subselres: integer;
735 daniel-mar 508
  exitRequest: boolean;
509
  menuIdExit, menuIdAsnEdit, menuIdDescEdit, menuIdAdd, menuIdDelete: integer;
733 daniel-mar 510
begin
735 daniel-mar 511
  exitRequest := false;
733 daniel-mar 512
  repeat
743 daniel-mar 513
    CreateOidDef(oid);
514
 
515
    if not _ReadOidFile(filename, oid, true) then
740 daniel-mar 516
    begin
743 daniel-mar 517
      FreeOidDef(oid);
740 daniel-mar 518
      exit;
519
    end;
520
 
733 daniel-mar 521
    (* Print OID information *)
522
 
523
    ClrScr;
737 daniel-mar 524
    _DrawOidTitleBar(filename, oid);
733 daniel-mar 525
    DrawStatusBar(DEFAULT_STATUSBAR);
526
    GotoXY(1,2);
527
 
735 daniel-mar 528
    if oid^.DotNotation <> '' then
733 daniel-mar 529
    begin
530
      WriteLn('Dot-Notation:');
735 daniel-mar 531
      WriteLn(oid^.DotNotation);
733 daniel-mar 532
      WriteLn('');
533
    end;
534
 
735 daniel-mar 535
    if Trim(oid^.Description) <> '' then
733 daniel-mar 536
    begin
537
      WriteLn('Description:');
735 daniel-mar 538
      WriteLn(oid^.Description);
733 daniel-mar 539
      WriteLn('');
540
    end;
541
 
542
    menuX := WhereX + 1;
735 daniel-mar 543
    menuY := ScreenHeight - ACTIONMENU_SIZE - 1;
733 daniel-mar 544
 
735 daniel-mar 545
    if ListCount(oid^.ASNIDs) > 0 then
733 daniel-mar 546
    begin
547
      linesLeft := menuY - WhereY - 1;
735 daniel-mar 548
      linesRequired := 1 + ListCount(oid^.ASNIds);
733 daniel-mar 549
 
550
      if LinesLeft < LinesRequired then
551
      begin
552
        (* Compact display of ASN.1 identifiers *)
553
        Write('ASN.1-Identifiers: ');
735 daniel-mar 554
        for i := 0 to ListCount(oid^.ASNIds)-1 do
733 daniel-mar 555
        begin
556
          if i > 0 then Write(', ');
735 daniel-mar 557
          Write(ListGetElement(oid^.ASNIds, i));
733 daniel-mar 558
        end;
559
        WriteLn('');
560
      end
561
      else
562
      begin
563
        (* Long display of ASN.1 identifiers *)
564
        WriteLn('ASN.1-Identifiers:');
735 daniel-mar 565
        for i := 0 to ListCount(oid^.ASNIds)-1 do
733 daniel-mar 566
        begin
735 daniel-mar 567
          WriteLn('- '+ListGetElement(oid^.ASNIds, i));
733 daniel-mar 568
        end;
569
        WriteLn('');
570
      end;
571
    end;
572
 
573
    (* Now prepare the menu entries *)
574
 
744 daniel-mar 575
    CreateList(subsel);   (* Contains the human-readable OID name *)
737 daniel-mar 576
    CreateList(subfiles); (* Contains the file name               *)
733 daniel-mar 577
 
735 daniel-mar 578
    if oid^.Parent = '' then
733 daniel-mar 579
    begin
580
      isRoot := true;
581
    end
582
    else
583
    begin
735 daniel-mar 584
      isRoot := DotNotationPart(oid^.Parent) = oid^.DotNotation;
733 daniel-mar 585
    end;
586
 
735 daniel-mar 587
    if (oid^.Parent <> '') and not isRoot then
733 daniel-mar 588
    begin
735 daniel-mar 589
      sTmp := oid^.Parent;
744 daniel-mar 590
      subfile := FileIdPart(sTmp) + OID_EXTENSION;
740 daniel-mar 591
      if FileExists(subfile) then
592
      begin
593
        CreateOidDef(tmpOID);
744 daniel-mar 594
        if not _ReadOidFile(subfile, tmpOID, true) then
595
        begin
596
          ListAppend(subsel, 'Go to parent ' + DotNotationPart(sTmp) + ' (READ ERROR)');
597
          ListAppend(subfiles, 'ERROR: '+subfile+' Read error or file invalid');
598
        end
743 daniel-mar 599
        else
744 daniel-mar 600
        begin
601
          ListAppend(subsel, 'Go to parent ' + DotNotationPart(sTmp) + _ShowASNIds(tmpOID));
602
          ListAppend(subfiles, subfile);
603
        end;
740 daniel-mar 604
        FreeOidDef(tmpOID);
605
      end
606
      else
607
      begin
608
        ListAppend(subsel, 'Go to parent ' + DotNotationPart(sTmp) + ' (FILE NOT FOUND)');
744 daniel-mar 609
        ListAppend(subfiles, 'ERROR: File '+subfile+' was not found');
740 daniel-mar 610
      end;
733 daniel-mar 611
    end;
612
 
613
    if isRoot then
614
    begin
735 daniel-mar 615
      menuIdExit := ListAppend(subsel, 'Back to main menu');
616
      ListAppend(subfiles, '');
617
    end
618
    else menuIdExit := -99;
733 daniel-mar 619
 
735 daniel-mar 620
    for i := 0 to ListCount(oid^.SubIds)-1 do
733 daniel-mar 621
    begin
735 daniel-mar 622
      sTmp := ListGetElement(oid^.SubIds, i);
744 daniel-mar 623
      subfile := FileIdPart(sTmp) + OID_EXTENSION;
740 daniel-mar 624
      if FileExists(subfile) then
625
      begin
626
        CreateOidDef(tmpOID);
744 daniel-mar 627
        if not _ReadOidFile(subfile, tmpOID, true) then
628
        begin
629
          ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + ' (READ ERROR)');
630
          ListAppend(subfiles, 'ERROR: Read error at file '+subfile+', or file is invalid.');
631
        end
632
        else if tmpOID^.Parent <> oid^.FileId + oid^.DotNotation then
633
        begin
634
          ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + ' (BAD BACKREF)');
635
          ListAppend(subfiles, 'ERROR: File '+subfile+' has a wrong back-reference.');
636
        end
743 daniel-mar 637
        else
744 daniel-mar 638
        begin
639
          ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + _ShowASNIds(tmpOID));
640
          ListAppend(subfiles, subfile);
641
        end;
740 daniel-mar 642
        FreeOidDef(tmpOID);
643
      end
644
      else
645
      begin
646
        ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + ' (FILE NOT FOUND)');
744 daniel-mar 647
        ListAppend(subfiles, 'ERROR: File '+subfile+' was not found');
740 daniel-mar 648
      end;
733 daniel-mar 649
    end;
650
 
735 daniel-mar 651
    if oid^.DotNotation <> '' then
733 daniel-mar 652
    begin
735 daniel-mar 653
      menuIdAsnEdit := ListAppend(subsel, 'Edit ASN.1 identifiers');
654
      ListAppend(subfiles, '');
655
    end
656
    else menuIdAsnEdit := -99;
733 daniel-mar 657
 
735 daniel-mar 658
    menuIdDescEdit := ListAppend(subsel, 'Edit description');
659
    ListAppend(subfiles, '');
733 daniel-mar 660
 
735 daniel-mar 661
    menuIdAdd := ListAppend(subsel, 'Add child');
662
    ListAppend(subfiles, '');
733 daniel-mar 663
 
664
    if not isRoot then
665
    begin
735 daniel-mar 666
      menuIdDelete := ListAppend(subsel, 'Delete OID');
667
      ListAppend(subfiles, '');
668
    end
669
    else menuIdDelete := -99;
733 daniel-mar 670
 
734 daniel-mar 671
    (* Show menu *)
672
 
733 daniel-mar 673
    subselres := DrawSelectionList(menuX, menuY,
674
                                   ScreenWidth-2,
735 daniel-mar 675
                                   ACTIONMENU_SIZE,
733 daniel-mar 676
                                   subsel,
677
                                   true,
678
                                   'SELECT ACTION',
679
                                   1);
734 daniel-mar 680
 
681
    (* Process user selection *)
682
 
733 daniel-mar 683
    if subselres = -1 then
684
    begin
735 daniel-mar 685
      exitRequest := true;
733 daniel-mar 686
    end
735 daniel-mar 687
    else if subselres = menuIdAsnEdit then
733 daniel-mar 688
    begin
735 daniel-mar 689
      if AsnEditor(oid) then
743 daniel-mar 690
        _WriteOidFile(filename, oid, true);
735 daniel-mar 691
    end
692
    else if subselres = menuIdDescEdit then
693
    begin
694
      if DescEditor(oid) then
743 daniel-mar 695
        _WriteOidFile(filename, oid, true);
735 daniel-mar 696
    end
697
    else if subselres = menuIdAdd then
698
    begin
699
      if NewOidEditor(oid) then
743 daniel-mar 700
        _WriteOidFile(filename, oid, true);
735 daniel-mar 701
    end
702
    else if subselres = menuIdDelete then
703
    begin
704
      if _DeleteConfirmation then
733 daniel-mar 705
      begin
744 daniel-mar 706
        sTmp := FileIdPart(oid^.Parent) + OID_EXTENSION;
735 daniel-mar 707
        DeleteOidRecursive(oid);
740 daniel-mar 708
        if FileExists(sTmp) then
709
        begin
710
          filename := sTmp;
711
        end
712
        else
713
        begin
744 daniel-mar 714
          ShowMessage('Parent file ' + sTmp + ' was not found', 'ERROR', true);
740 daniel-mar 715
          _Pause;
716
          exitRequest := true;
717
        end;
733 daniel-mar 718
      end;
735 daniel-mar 719
    end
720
    else if subselres = menuIdExit then
721
    begin
722
      exitRequest := true;
723
    end
724
    else
725
    begin
726
      (* Normal OID *)
744 daniel-mar 727
      (* Above we already checked if the files are valild and existing *)
740 daniel-mar 728
      sTmp := ListGetElement(subfiles, subselres);
744 daniel-mar 729
      if Copy(sTmp, 1, Length('ERROR: ')) = 'ERROR: ' then
740 daniel-mar 730
      begin
744 daniel-mar 731
        Delete(sTmp, 1, Length('ERROR: '));
732
        ShowMessage(sTmp, 'ERROR', true);
733
        _Pause;
740 daniel-mar 734
      end
735
      else
736
      begin
744 daniel-mar 737
        filename := sTmp;
740 daniel-mar 738
      end;
733 daniel-mar 739
    end;
740
    FreeList(subsel);
741
    FreeList(subfiles);
742
 
735 daniel-mar 743
    FreeOidDef(oid);
744
  until exitRequest;
733 daniel-mar 745
end;
746
 
743 daniel-mar 747
function CreateRootOIDFile(filename: string; ShowErrorMessage: boolean): boolean;
733 daniel-mar 748
var
735 daniel-mar 749
  oid: POID;
733 daniel-mar 750
begin
735 daniel-mar 751
  CreateOidDef(oid);
752
  oid^.Description := 'This is the root of the OID tree.' +#13#10 +
753
                      #13#10 +
754
                      'Valid subsequent arcs are per definition:' + #13#10 +
755
                      '- 0 (itu-t)' + #13#10 +
756
                      '- 1 (iso)' + #13#10 +
757
                      '- 2 (joint-iso-itu-t)';
737 daniel-mar 758
  oid^.FileId      := ZeroPad(0, 8);
735 daniel-mar 759
  oid^.DotNotation := '';
737 daniel-mar 760
  oid^.Parent      := ZeroPad(0, 8);
743 daniel-mar 761
  CreateRootOIDFile := _WriteOidFile(filename, oid, ShowErrorMessage);
735 daniel-mar 762
  FreeOidDef(oid);
733 daniel-mar 763
end;
764
 
742 daniel-mar 765
function _GetRootFile(ShowErrorMessage: boolean): string;
735 daniel-mar 766
var
742 daniel-mar 767
  rootFile: string;
733 daniel-mar 768
begin
744 daniel-mar 769
  rootFile := ZeroPad(0, 8) + OID_EXTENSION;
743 daniel-mar 770
  _GetRootFile := rootFile;
742 daniel-mar 771
  if not FileExists(rootFile) then
733 daniel-mar 772
  begin
743 daniel-mar 773
    if not CreateRootOIDFile(rootFile, ShowErrorMessage) then
742 daniel-mar 774
    begin
743 daniel-mar 775
      _GetRootFile := '';
742 daniel-mar 776
    end;
777
  end;
733 daniel-mar 778
end;
779
 
740 daniel-mar 780
procedure OP_ManageOIDs;
742 daniel-mar 781
var
782
  rootfile: string;
740 daniel-mar 783
begin
784
  ClrScr;
785
  DrawTitleBar('Manage Object Identifiers', TITLEBAR_LEFT_TEXT, '');
786
  DrawStatusBar('Loading data... please wait...');
787
 
742 daniel-mar 788
  (* This will try creating a new root file if it does not exist *)
789
  rootfile := _GetRootFile(true);
790
  if rootfile = '' then Exit;
791
 
792
  DisplayOIDFile(rootfile);
740 daniel-mar 793
end;
794
 
733 daniel-mar 795
procedure OP_ManageRAs;
796
begin
797
  ClrScr;
738 daniel-mar 798
  DrawTitleBar('Manage Registration Authorities', TITLEBAR_LEFT_TEXT, '');
733 daniel-mar 799
  DrawStatusBar('');
800
 
801
  (* TODO: Implement "Manage RAs" feature *)
802
end;
803
 
804
procedure OP_ReturnToMSDOS;
805
begin
806
  ClrScr;
743 daniel-mar 807
  TextBackground(Black);
808
  TextColor(LightGray);
741 daniel-mar 809
  WriteLn('Thank you for using OIDplus for DOS.');
743 daniel-mar 810
  WriteLn('');
733 daniel-mar 811
end;
812
 
741 daniel-mar 813
function _GetTreeViewLine(oid: POID; indent: integer): string;
740 daniel-mar 814
var
815
  i: integer;
816
  sTmp: string;
817
begin
818
  (* Build line *)
819
  sTmp := RepeatStr(' ', indent*TREEVIEW_INDENT);
820
  if oid^.DotNotation = '' then
821
    sTmp := sTmp + 'Object Identifiers'
822
  else
823
    sTmp := sTmp + oid^.DotNotation;
824
  sTmp := sTmp + _ShowAsnIds(oid);
825
  if TREEVIEW_INCLUDE_DESC then
826
  begin
827
    if Trim(oid^.Description) <> '' then
828
    begin
829
      sTmp := sTmp + ': ' + oid^.Description;
830
    end;
831
  end;
832
  for i := 1 to Length(sTmp) do
833
  begin
834
    if (sTmp[i]=#13) or (sTmp[i]=#10) then sTmp[i] := ' ';
835
  end;
744 daniel-mar 836
  sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
741 daniel-mar 837
  _GetTreeViewLine := sTmp;
740 daniel-mar 838
end;
839
 
840
procedure _RecTreeExport(oid: POID; var F: Text; indent: integer);
841
var
842
  i: integer;
843
  sTmp: string;
844
  suboid: POID;
845
  childFilename: string;
846
begin
741 daniel-mar 847
  sTmp := _GetTreeViewLine(oid, indent);
740 daniel-mar 848
  sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
849
  WriteLn(F, sTmp);
850
 
851
  (* Recursively call children *)
852
  for i := 0 to ListCount(oid^.SubIds)-1 do
853
  begin
854
    sTmp := ListGetElement(oid^.SubIds, i);
855
    CreateOidDef(suboid);
744 daniel-mar 856
    childFilename := FileIdPart(sTmp) + OID_EXTENSION;
743 daniel-mar 857
    if not FileExists(childFilename) then
740 daniel-mar 858
    begin
744 daniel-mar 859
      sTmp := 'ERROR: MISSING ' + childFilename + ' (SHALL CONTAIN ' + DotNotationPart(sTmp) + ')!';
743 daniel-mar 860
      sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
861
      WriteLn(F, sTmp);
740 daniel-mar 862
    end
743 daniel-mar 863
    else if not _ReadOidFile(childFilename, suboid, false) then
740 daniel-mar 864
    begin
744 daniel-mar 865
      sTmp := 'ERROR: READ ERROR AT ' + childFilename + ' (SHALL CONTAIN ' + DotNotationPart(sTmp) + ')!';
740 daniel-mar 866
      sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
867
      WriteLn(F, sTmp);
743 daniel-mar 868
    end
744 daniel-mar 869
    else if suboid^.Parent <> oid^.FileId + oid^.DotNotation then
870
    begin
871
      (* This can happen if a file is missing, and then another OID gets this filename since the number seems to be free *)
872
      sTmp := 'ERROR: BAD BACKREF AT ' + childFilename + ' (SHALL CONTAIN ' + DotNotationPart(sTmp) + ')!';
873
      sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
874
      WriteLn(F, sTmp);
875
    end
743 daniel-mar 876
    else
877
    begin
878
      _RecTreeExport(suboid, F, indent+1);
879
      FreeOidDef(suboid);
880
    end
740 daniel-mar 881
  end;
882
end;
883
 
884
procedure OP_TreeView;
885
var
886
  F: Text;
887
  rootoid: POID;
742 daniel-mar 888
  rootfile: string;
743 daniel-mar 889
  res: boolean;
740 daniel-mar 890
begin
891
  ClrScr;
892
  DrawTitleBar('TreeView Export', TITLEBAR_LEFT_TEXT, '');
893
  DrawStatusBar('Exporting data... please wait...');
894
 
742 daniel-mar 895
  (* This will try creating a new root file if it does not exist *)
743 daniel-mar 896
  rootfile := _GetRootFile(true);
897
  if rootfile = '' then
898
  begin
899
    DrawStatusBar(DEFAULT_STATUSBAR);
900
    Exit;
901
  end;
742 daniel-mar 902
 
744 daniel-mar 903
  Assign(F, TREEVIEW_FILENAME);
743 daniel-mar 904
  {$I-}
740 daniel-mar 905
  Rewrite(F);
743 daniel-mar 906
  {$I+}
907
  if IoResult <> 0 then
908
  begin
909
    (* Can happen if disk is read-only (Runtime Error 150) *)
744 daniel-mar 910
    ShowMessage('Cannot open '+TREEVIEW_FILENAME+' for writing.', 'ERROR', true);
743 daniel-mar 911
    _Pause;
912
    DrawStatusBar(DEFAULT_STATUSBAR);
913
    Exit;
914
  end;
740 daniel-mar 915
 
743 daniel-mar 916
  res := false;
740 daniel-mar 917
  CreateOidDef(rootoid);
743 daniel-mar 918
  if _ReadOidFile(rootfile, rootoid, true) then
919
  begin
920
    _RecTreeExport(rootoid, F, 0);
921
    res := true;
922
  end;
740 daniel-mar 923
  FreeOidDef(rootoid);
924
 
743 daniel-mar 925
  Close(F);
926
 
740 daniel-mar 927
  DrawStatusBar(DEFAULT_STATUSBAR);
743 daniel-mar 928
  if res then
929
  begin
744 daniel-mar 930
    ShowMessage('TreeView successfully exported as '+TREEVIEW_FILENAME, 'TREEVIEW EXPORT', true);
743 daniel-mar 931
    _Pause;
932
  end;
740 daniel-mar 933
end;
934
 
733 daniel-mar 935
procedure OP_MainMenu;
936
var
937
  menu: PStringList;
938
  menuRes, menuLeft, menuTop: integer;
740 daniel-mar 939
  menuIdOID, menuIdRA, menuIdTree, menuIdExit: integer;
733 daniel-mar 940
begin
941
  repeat
942
    ClrScr;
943
 
738 daniel-mar 944
    DrawTitleBar('Welcome to OIDplus for DOS', '', '');
733 daniel-mar 945
    DrawStatusBar(DEFAULT_STATUSBAR);
946
    GoToXY(ScreenWidth-Length(VERSIONINFO), ScreenHeight-1);
947
    Write(VERSIONINFO);
948
 
735 daniel-mar 949
    CreateList(menu);
950
 
951
    menuIdOID  := ListAppend(menu, 'Manage OIDs');
952
    menuIdRA   := -99; (*ListAppend(menu, 'Manage RAs');*)
740 daniel-mar 953
    menuIdTree := ListAppend(menu, 'Export TreeView');
735 daniel-mar 954
    menuIdExit := ListAppend(menu, 'Return to DOS');
955
 
956
    menuLeft := round(ScreenWidth/2 -MAINMENU_WIDTH/2);
957
    menuTop  := round(ScreenHeight/2-MAINMENU_HEIGHT/2);
958
    menuRes  := DrawSelectionList(menuLeft, menuTop,
959
                                  MAINMENU_WIDTH, MAINMENU_HEIGHT,
960
                                  menu, true, 'MAIN MENU', 2);
733 daniel-mar 961
    FreeList(menu);
962
 
735 daniel-mar 963
    if menuRes = menuIdOID then
733 daniel-mar 964
    begin
965
      OP_ManageOIDs;
735 daniel-mar 966
    end
967
    else if menuRes = menuIdRA then
733 daniel-mar 968
    begin
969
      OP_ManageRAs;
740 daniel-mar 970
    end
971
    else if menuRes = menuIdTree then
972
    begin
973
      OP_Treeview;
733 daniel-mar 974
    end;
735 daniel-mar 975
  until (menuRes = menuIdExit) or (MAINMENU_ALLOW_ESC and (menuRes = -1));
733 daniel-mar 976
 
977
  OP_ReturnToMSDOS;
978
end;
979
 
980
begin
981
  OP_MainMenu;
982
end.