Subversion Repositories oidplus

Rev

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