Subversion Repositories oidplus

Rev

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