Subversion Repositories oidplus

Rev

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