Subversion Repositories oidplus

Rev

Rev 744 | Rev 746 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. program OIDPLUS;
  2.  
  3. (************************************************)
  4. (* OIDPLUS.PAS                                  *)
  5. (* Author:   Daniel Marschall                   *)
  6. (* Revision: 2022-02-15                         *)
  7. (* License:  Apache 2.0                         *)
  8. (* This file contains:                          *)
  9. (* - "OIDplus for DOS" program                  *)
  10. (************************************************)
  11.  
  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.  
  20. uses
  21.   Dos, Crt, StrList, VtsFuncs, VtsCui, OidFile, OidUtils;
  22.  
  23. const
  24.   VERSIONINFO            = 'Revision: 2022-02-15';
  25.   DEFAULT_STATUSBAR      = '(C)2020-2022 ViaThinkSoft. Licensed under the terms of the Apache 2.0 license.';
  26.   TITLEBAR_LEFT_TEXT     = 'OIDplus';
  27.   DISKIO_SOUND_DEBUGGING = false;
  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;
  36.   TREEVIEW_INDENT        = 0;
  37.   TREEVIEW_INCLUDE_DESC  = true;
  38.   TREEVIEW_WIDTH         = 80;
  39.   OID_EXTENSION          = '.OID';
  40.   TREEVIEW_FILENAME      = 'OIDTREE.TXT';
  41.  
  42. procedure _Pause;
  43. var
  44.   bakX, bakY: integer;
  45. begin
  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
  58.   DrawStatusBar('Write file ' + filename + '...');
  59.   res := WriteOidFile(filename, oid);
  60.   if DISKIO_SOUND_DEBUGGING then
  61.   begin
  62.     Sound(70);
  63.     Delay(DISKIO_SOUND_DELAY - 10);
  64.     NoSound;
  65.     Delay(10);
  66.   end;
  67.   DrawStatusBar(DEFAULT_STATUSBAR);
  68.  
  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;
  76. end;
  77.  
  78. function _ReadOidFile(filename: string; oid: POid; ShowErrorMessage: boolean): boolean;
  79. var
  80.   res: boolean;
  81. begin
  82.   DrawStatusBar('Read file ' + filename + '...');
  83.   res := ReadOidFile(filename, oid);
  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.  
  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;
  100. end;
  101.  
  102. function _ShowASNIds(childOID: POID): string;
  103. var
  104.   j, jmax: integer;
  105.   sTmp: string;
  106. begin
  107.   sTmp := '';
  108.   jmax := ListCount(childOID^.ASNIds)-1;
  109.   for j := 0 to jmax do
  110.   begin
  111.     if j = 0 then sTmp := sTmp + ' (';
  112.     sTmp := sTmp + ListGetElement(childOID^.ASNIds, j);
  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
  123.   AsnAlreadyExisting := ListContains(oid^.AsnIds, asnid);
  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;
  133.   menuIdNew, menuIdSave, menuIdExit: integer;
  134. begin
  135.   AsnEditor := false;
  136.  
  137.   repeat
  138.     CreateList(asnList);
  139.  
  140.     for i := 0 to ListCount(oid^.ASNIds)-1 do
  141.     begin
  142.       ListAppend(asnList, ListGetElement(oid^.ASNIDs, i));
  143.     end;
  144.     menuIdNew  := ListAppend(asnList, '<NEW>');
  145.     menuIdSave := ListAppend(asnList, '<SAVE>');
  146.     menuIdExit := ListAppend(asnList, '<CANCEL>');
  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);
  157.     FreeList(asnList);
  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
  168.     else if res = menuIdNew then
  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
  201.     else if res = menuIdSave then
  202.     begin
  203.       (* "SAVE" item was selected *)
  204.       AsnEditor := true;
  205.       Exit;
  206.     end
  207.     else if res = menuIdExit then
  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 *)
  229.             ListDeleteElementByIndex(oid^.ASNIDs, res);
  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;
  272.     DescEditor := true; (* request caller to save <oid> *)
  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 *)
  284.   CreateList(list);
  285.   FindFirst(RepeatStr('?',8)+OID_EXTENSION, Archive, DirInfo);
  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);
  317.     if DotNotationPart(sTmp) = searchDotNotation then
  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;
  343.       if not IsPositiveInteger(sInput) then
  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;
  369.         NumIdEditor := true; (* request caller to save <oid> *)
  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;
  383.   newOID: POID;
  384. begin
  385.   NewOidEditor := false;
  386.  
  387.   CreateOidDef(newOID);
  388.   newOID^.FileId := NextPossibleFileID;
  389.   newOID^.ParentFileId := oid^.FileId;
  390.   newOID^.ParentDotNotation := oid^.DotNotation;
  391.   if NumIdEditor(newOID, oid) and
  392.      AsnEditor(newOID) and
  393.      DescEditor(newOID) then
  394.   begin
  395.     newfilename := newOID^.FileId + OID_EXTENSION;
  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;
  402.   end;
  403.   FreeOidDef(newOID);
  404. end;
  405.  
  406. procedure DeleteChildrenRecursive(oid: POID);
  407. var
  408.   i: integer;
  409.   childOID: POID;
  410.   filenameChild: string;
  411. begin
  412.   for i := 0 to ListCount(oid^.SubIds)-1 do
  413.   begin
  414.     filenameChild := FileIdPart(ListGetElement(oid^.SubIds, i)) + OID_EXTENSION;
  415.     if FileExists(filenameChild) then
  416.     begin
  417.       CreateOidDef(childOID);
  418.       if _ReadOidFile(filenameChild, childOID, false) and
  419.          (childOID^.ParentFileId = oid^.FileId) and
  420.          (childOID^.ParentDotNotation = oid^.DotNotation) then
  421.       begin
  422.         DeleteChildrenRecursive(childOID);
  423.       end;
  424.       FreeOidDef(childOID);
  425.       DeleteFile(filenameChild);
  426.     end;
  427.   end;
  428.   ListClear(oid^.SubIds);
  429. end;
  430.  
  431. procedure DeleteOidRecursive(selfOID: POID);
  432. var
  433.   i: integer;
  434.   parentOID: POID;
  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 *)
  441.   (* (this is the most important part)      *)
  442.   filenameParent := selfOID^.ParentFileId + OID_EXTENSION;
  443.   if FileExists(filenameParent) then
  444.   begin
  445.     CreateOidDef(parentOID);
  446.     if _ReadOidFile(filenameParent, parentOID, true) then
  447.     begin
  448.       if ListDeleteElementByValue(parentOID^.SubIds, selfOID^.FileId + selfOID^.DotNotation) then
  449.       begin
  450.         _WriteOidFile(filenameParent, parentOID, true);
  451.       end;
  452.     end;
  453.     FreeOidDef(parentOID);
  454.   end;
  455.  
  456.   (* Delete own file *)
  457.   filenameSelf := selfOID^.FileId + OID_EXTENSION;
  458.   if FileExists(filenameSelf) then
  459.   begin
  460.     DeleteFile(filenameSelf);
  461.   end;
  462. end;
  463.  
  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);
  470.     DrawStatusBar('Y=Yes, N=No');
  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;
  484.     end
  485.     else if UpCase(sc) = 'N' then
  486.     begin
  487.       _DeleteConfirmation := false;
  488.       break;
  489.     end;
  490.   until false;
  491. end;
  492.  
  493. procedure _DrawOidTitleBar(filename: string; oid: POID);
  494. begin
  495.   if oid^.DotNotation = '' then
  496.     DrawTitleBar('OID ROOT', TITLEBAR_LEFT_TEXT, filename)
  497.   else
  498.     DrawTitleBar('OID ' + oid^.DotNotation, TITLEBAR_LEFT_TEXT, filename);
  499. end;
  500.  
  501. procedure DisplayOIDFile(filename: string);
  502. var
  503.   isRoot: boolean;
  504.   oid, tmpOID: POID;
  505.   i, menuX, menuY: integer;
  506.   linesLeft, linesRequired: integer;
  507.   sTmp, subfile: string;
  508.   subsel, subfiles: PStringList;
  509.   subselres: integer;
  510.   exitRequest: boolean;
  511.   menuIdExit, menuIdAsnEdit, menuIdDescEdit, menuIdAdd, menuIdDelete: integer;
  512. begin
  513.   exitRequest := false;
  514.   repeat
  515.     CreateOidDef(oid);
  516.  
  517.     if not _ReadOidFile(filename, oid, true) then
  518.     begin
  519.       FreeOidDef(oid);
  520.       exit;
  521.     end;
  522.  
  523.     (* Print OID information *)
  524.  
  525.     ClrScr;
  526.     _DrawOidTitleBar(filename, oid);
  527.     DrawStatusBar(DEFAULT_STATUSBAR);
  528.     GotoXY(1,2);
  529.  
  530.     if oid^.DotNotation <> '' then
  531.     begin
  532.       WriteLn('Dot-Notation:');
  533.       WriteLn(oid^.DotNotation);
  534.       WriteLn('');
  535.     end;
  536.  
  537.     if Trim(oid^.Description) <> '' then
  538.     begin
  539.       WriteLn('Description:');
  540.       WriteLn(oid^.Description);
  541.       WriteLn('');
  542.     end;
  543.  
  544.     menuX := WhereX + 1;
  545.     menuY := ScreenHeight - ACTIONMENU_SIZE - 1;
  546.  
  547.     if ListCount(oid^.ASNIDs) > 0 then
  548.     begin
  549.       linesLeft := menuY - WhereY - 1;
  550.       linesRequired := 1 + ListCount(oid^.ASNIds);
  551.  
  552.       if LinesLeft < LinesRequired then
  553.       begin
  554.         (* Compact display of ASN.1 identifiers *)
  555.         Write('ASN.1-Identifiers: ');
  556.         for i := 0 to ListCount(oid^.ASNIds)-1 do
  557.         begin
  558.           if i > 0 then Write(', ');
  559.           Write(ListGetElement(oid^.ASNIds, i));
  560.         end;
  561.         WriteLn('');
  562.       end
  563.       else
  564.       begin
  565.         (* Long display of ASN.1 identifiers *)
  566.         WriteLn('ASN.1-Identifiers:');
  567.         for i := 0 to ListCount(oid^.ASNIds)-1 do
  568.         begin
  569.           WriteLn('- '+ListGetElement(oid^.ASNIds, i));
  570.         end;
  571.         WriteLn('');
  572.       end;
  573.     end;
  574.  
  575.     (* Now prepare the menu entries *)
  576.  
  577.     CreateList(subsel);   (* Contains the human-readable OID name *)
  578.     CreateList(subfiles); (* Contains the file name               *)
  579.  
  580.     if oid^.ParentFileId = '' then
  581.     begin
  582.       isRoot := true;
  583.     end
  584.     else
  585.     begin
  586.       isRoot := oid^.ParentDotNotation = oid^.DotNotation;
  587.     end;
  588.  
  589.     if (oid^.ParentFileId <> '') and not isRoot then
  590.     begin
  591.       subfile := oid^.ParentFileId + OID_EXTENSION;
  592.       if FileExists(subfile) then
  593.       begin
  594.         CreateOidDef(tmpOID);
  595.         if not _ReadOidFile(subfile, tmpOID, true) then
  596.         begin
  597.           ListAppend(subsel, 'Go to parent ' + oid^.ParentDotNotation + ' (READ ERROR)');
  598.           ListAppend(subfiles, 'ERROR: '+subfile+' Read error or file invalid');
  599.         end
  600.         else
  601.         begin
  602.           ListAppend(subsel, 'Go to parent ' + oid^.ParentDotNotation + _ShowASNIds(tmpOID));
  603.           ListAppend(subfiles, subfile);
  604.         end;
  605.         FreeOidDef(tmpOID);
  606.       end
  607.       else
  608.       begin
  609.         ListAppend(subsel, 'Go to parent ' + oid^.ParentDotNotation + ' (FILE NOT FOUND)');
  610.         ListAppend(subfiles, 'ERROR: File '+subfile+' was not found');
  611.       end;
  612.     end;
  613.  
  614.     if isRoot then
  615.     begin
  616.       menuIdExit := ListAppend(subsel, 'Back to main menu');
  617.       ListAppend(subfiles, '');
  618.     end
  619.     else menuIdExit := -99;
  620.  
  621.     for i := 0 to ListCount(oid^.SubIds)-1 do
  622.     begin
  623.       sTmp := ListGetElement(oid^.SubIds, i);
  624.       subfile := FileIdPart(sTmp) + OID_EXTENSION;
  625.       if FileExists(subfile) then
  626.       begin
  627.         CreateOidDef(tmpOID);
  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
  633.         else if (tmpOID^.ParentFileId <> oid^.FileId) or
  634.                 (tmpOID^.ParentDotNotation <> oid^.DotNotation) then
  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
  639.         else
  640.         begin
  641.           ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + _ShowASNIds(tmpOID));
  642.           ListAppend(subfiles, subfile);
  643.         end;
  644.         FreeOidDef(tmpOID);
  645.       end
  646.       else
  647.       begin
  648.         ListAppend(subsel, 'Go to child  ' + DotNotationPart(sTmp) + ' (FILE NOT FOUND)');
  649.         ListAppend(subfiles, 'ERROR: File '+subfile+' was not found');
  650.       end;
  651.     end;
  652.  
  653.     if oid^.DotNotation <> '' then
  654.     begin
  655.       menuIdAsnEdit := ListAppend(subsel, 'Edit ASN.1 identifiers');
  656.       ListAppend(subfiles, '');
  657.     end
  658.     else menuIdAsnEdit := -99;
  659.  
  660.     menuIdDescEdit := ListAppend(subsel, 'Edit description');
  661.     ListAppend(subfiles, '');
  662.  
  663.     menuIdAdd := ListAppend(subsel, 'Add child');
  664.     ListAppend(subfiles, '');
  665.  
  666.     if not isRoot then
  667.     begin
  668.       menuIdDelete := ListAppend(subsel, 'Delete OID');
  669.       ListAppend(subfiles, '');
  670.     end
  671.     else menuIdDelete := -99;
  672.  
  673.     (* Show menu *)
  674.  
  675.     subselres := DrawSelectionList(menuX, menuY,
  676.                                    ScreenWidth-2,
  677.                                    ACTIONMENU_SIZE,
  678.                                    subsel,
  679.                                    true,
  680.                                    'SELECT ACTION',
  681.                                    1);
  682.  
  683.     (* Process user selection *)
  684.  
  685.     if subselres = -1 then
  686.     begin
  687.       exitRequest := true;
  688.     end
  689.     else if subselres = menuIdAsnEdit then
  690.     begin
  691.       if AsnEditor(oid) then
  692.         _WriteOidFile(filename, oid, true);
  693.     end
  694.     else if subselres = menuIdDescEdit then
  695.     begin
  696.       if DescEditor(oid) then
  697.         _WriteOidFile(filename, oid, true);
  698.     end
  699.     else if subselres = menuIdAdd then
  700.     begin
  701.       if NewOidEditor(oid) then
  702.         _WriteOidFile(filename, oid, true);
  703.     end
  704.     else if subselres = menuIdDelete then
  705.     begin
  706.       if _DeleteConfirmation then
  707.       begin
  708.         sTmp := oid^.ParentFileId + OID_EXTENSION;
  709.         DeleteOidRecursive(oid);
  710.         if FileExists(sTmp) then
  711.         begin
  712.           filename := sTmp;
  713.         end
  714.         else
  715.         begin
  716.           ShowMessage('Parent file ' + sTmp + ' was not found', 'ERROR', true);
  717.           _Pause;
  718.           exitRequest := true;
  719.         end;
  720.       end;
  721.     end
  722.     else if subselres = menuIdExit then
  723.     begin
  724.       exitRequest := true;
  725.     end
  726.     else
  727.     begin
  728.       (* Normal OID *)
  729.       (* Above we already checked if the files are valild and existing *)
  730.       sTmp := ListGetElement(subfiles, subselres);
  731.       if Copy(sTmp, 1, Length('ERROR: ')) = 'ERROR: ' then
  732.       begin
  733.         Delete(sTmp, 1, Length('ERROR: '));
  734.         ShowMessage(sTmp, 'ERROR', true);
  735.         _Pause;
  736.       end
  737.       else
  738.       begin
  739.         filename := sTmp;
  740.       end;
  741.     end;
  742.     FreeList(subsel);
  743.     FreeList(subfiles);
  744.  
  745.     FreeOidDef(oid);
  746.   until exitRequest;
  747. end;
  748.  
  749. function CreateRootOIDFile(filename: string; ShowErrorMessage: boolean): boolean;
  750. var
  751.   oid: POID;
  752. begin
  753.   CreateOidDef(oid);
  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 := '';
  764.   CreateRootOIDFile := _WriteOidFile(filename, oid, ShowErrorMessage);
  765.   FreeOidDef(oid);
  766. end;
  767.  
  768. function _GetRootFile(ShowErrorMessage: boolean): string;
  769. var
  770.   rootFile: string;
  771. begin
  772.   rootFile := ZeroPad(0, 8) + OID_EXTENSION;
  773.   _GetRootFile := rootFile;
  774.   if not FileExists(rootFile) then
  775.   begin
  776.     if not CreateRootOIDFile(rootFile, ShowErrorMessage) then
  777.     begin
  778.       _GetRootFile := '';
  779.     end;
  780.   end;
  781. end;
  782.  
  783. procedure OP_ManageOIDs;
  784. var
  785.   rootfile: string;
  786. begin
  787.   ClrScr;
  788.   DrawTitleBar('Manage Object Identifiers', TITLEBAR_LEFT_TEXT, '');
  789.   DrawStatusBar('Loading data... please wait...');
  790.  
  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);
  796. end;
  797.  
  798. procedure OP_ManageRAs;
  799. begin
  800.   ClrScr;
  801.   DrawTitleBar('Manage Registration Authorities', TITLEBAR_LEFT_TEXT, '');
  802.   DrawStatusBar('');
  803.  
  804.   (* TODO: Implement "Manage RAs" feature *)
  805. end;
  806.  
  807. procedure OP_ReturnToMSDOS;
  808. begin
  809.   ClrScr;
  810.   TextBackground(Black);
  811.   TextColor(LightGray);
  812.   WriteLn('Thank you for using OIDplus for DOS.');
  813.   WriteLn('');
  814. end;
  815.  
  816. function _GetTreeViewLine(oid: POID; indent: integer): string;
  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;
  839.   sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
  840.   _GetTreeViewLine := sTmp;
  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
  850.   sTmp := _GetTreeViewLine(oid, indent);
  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);
  859.     childFilename := FileIdPart(sTmp) + OID_EXTENSION;
  860.     if not FileExists(childFilename) then
  861.     begin
  862.       sTmp := 'ERROR: MISSING ' + childFilename + ' (SHALL CONTAIN ' + DotNotationPart(sTmp) + ')!';
  863.       sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
  864.       WriteLn(F, sTmp);
  865.     end
  866.     else if not _ReadOidFile(childFilename, suboid, false) then
  867.     begin
  868.       sTmp := 'ERROR: READ ERROR AT ' + childFilename + ' (SHALL CONTAIN ' + DotNotationPart(sTmp) + ')!';
  869.       sTmp := TrimLineToWidth(sTmp, TREEVIEW_WIDTH);
  870.       WriteLn(F, sTmp);
  871.     end
  872.     else if (suboid^.ParentFileId <> oid^.FileId) or
  873.             (suboid^.ParentDotNotation <> oid^.DotNotation) then
  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
  880.     else
  881.     begin
  882.       _RecTreeExport(suboid, F, indent+1);
  883.       FreeOidDef(suboid);
  884.     end
  885.   end;
  886. end;
  887.  
  888. procedure OP_TreeView;
  889. var
  890.   F: Text;
  891.   rootoid: POID;
  892.   rootfile: string;
  893.   res: boolean;
  894. begin
  895.   ClrScr;
  896.   DrawTitleBar('TreeView Export', TITLEBAR_LEFT_TEXT, '');
  897.   DrawStatusBar('Exporting data... please wait...');
  898.  
  899.   (* This will try creating a new root file if it does not exist *)
  900.   rootfile := _GetRootFile(true);
  901.   if rootfile = '' then
  902.   begin
  903.     DrawStatusBar(DEFAULT_STATUSBAR);
  904.     Exit;
  905.   end;
  906.  
  907.   Assign(F, TREEVIEW_FILENAME);
  908.   {$I-}
  909.   Rewrite(F);
  910.   {$I+}
  911.   if IoResult <> 0 then
  912.   begin
  913.     (* Can happen if disk is read-only (Runtime Error 150) *)
  914.     ShowMessage('Cannot open '+TREEVIEW_FILENAME+' for writing.', 'ERROR', true);
  915.     _Pause;
  916.     DrawStatusBar(DEFAULT_STATUSBAR);
  917.     Exit;
  918.   end;
  919.  
  920.   res := false;
  921.   CreateOidDef(rootoid);
  922.   if _ReadOidFile(rootfile, rootoid, true) then
  923.   begin
  924.     _RecTreeExport(rootoid, F, 0);
  925.     res := true;
  926.   end;
  927.   FreeOidDef(rootoid);
  928.  
  929.   Close(F);
  930.  
  931.   DrawStatusBar(DEFAULT_STATUSBAR);
  932.   if res then
  933.   begin
  934.     ShowMessage('TreeView successfully exported as '+TREEVIEW_FILENAME, 'TREEVIEW EXPORT', true);
  935.     _Pause;
  936.   end;
  937. end;
  938.  
  939. procedure OP_MainMenu;
  940. var
  941.   menu: PStringList;
  942.   menuRes, menuLeft, menuTop: integer;
  943.   menuIdOID, menuIdRA, menuIdTree, menuIdExit: integer;
  944. begin
  945.   repeat
  946.     ClrScr;
  947.  
  948.     DrawTitleBar('Welcome to OIDplus for DOS', '', '');
  949.     DrawStatusBar(DEFAULT_STATUSBAR);
  950.     GoToXY(ScreenWidth-Length(VERSIONINFO), ScreenHeight-1);
  951.     Write(VERSIONINFO);
  952.  
  953.     CreateList(menu);
  954.  
  955.     menuIdOID  := ListAppend(menu, 'Manage OIDs');
  956.     menuIdRA   := -99; (*ListAppend(menu, 'Manage RAs');*)
  957.     menuIdTree := ListAppend(menu, 'Export TreeView');
  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);
  965.     FreeList(menu);
  966.  
  967.     if menuRes = menuIdOID then
  968.     begin
  969.       OP_ManageOIDs;
  970.     end
  971.     else if menuRes = menuIdRA then
  972.     begin
  973.       OP_ManageRAs;
  974.     end
  975.     else if menuRes = menuIdTree then
  976.     begin
  977.       OP_Treeview;
  978.     end;
  979.   until (menuRes = menuIdExit) or (MAINMENU_ALLOW_ESC and (menuRes = -1));
  980.  
  981.   OP_ReturnToMSDOS;
  982. end;
  983.  
  984. begin
  985.   OP_MainMenu;
  986. end.
  987.