Subversion Repositories oidplus

Rev

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