Subversion Repositories oidplus

Rev

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