Subversion Repositories oidplus

Rev

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