home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 19 / AACD19.BIN / AACD / Programming / LIProgram / LibraryInterceptor.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-02-21  |  16.3 KB  |  707 lines

  1. /****h* LibraryInterceptor/LibraryInterceptor.c [1.2] ******************
  2. *
  3. * NAME
  4. *    LibraryInterceptor
  5. *
  6. * DESCRIPTION
  7. *    This program waits for any calls to OpenLibrary() & then shows
  8. *    a GUI that allows the user to change the name or version of
  9. *    the Library that's to be opened.  The biggest bug in this code
  10. *    was NOT specifying the register assignments for the arguments to
  11. *    NewFunction().
  12. *
  13. * HISTORY
  14. *    21-Feb-2001 Program now runs correctly.
  15. *
  16. * NOTES
  17. *    GUI Designed by : Jim Steichen
  18. ************************************************************************
  19. *
  20. */
  21.  
  22. #include <string.h>
  23.  
  24. #include <exec/execbase.h>
  25. #include <exec/types.h>
  26.  
  27. #include <AmigaDOSErrs.h>
  28.  
  29. #include <intuition/intuition.h>
  30. #include <intuition/classes.h>
  31. #include <intuition/classusr.h>
  32. #include <intuition/gadgetclass.h>
  33.  
  34. #include <libraries/gadtools.h>
  35.  
  36. #include <graphics/displayinfo.h>
  37. #include <graphics/gfxbase.h>
  38.  
  39. #include <workbench/workbench.h>
  40. #include <workbench/startup.h>
  41.  
  42. #include <clib/exec_protos.h>
  43. #include <clib/intuition_protos.h>
  44. #include <clib/gadtools_protos.h>
  45. #include <clib/graphics_protos.h>
  46. #include <clib/utility_protos.h>
  47. #include <clib/diskfont_protos.h>
  48.  
  49. #include "CPGM:GlobalObjects/CommonFuncs.h"
  50.  
  51. #define StrBfPtr( g ) (((struct StringInfo *)g->SpecialInfo)->Buffer)
  52. #define IntBfPtr( g ) (((struct StringInfo *)g->SpecialInfo)->LongInt)
  53.  
  54. #define LibNameStr    0
  55. #define LibVersionInt 1
  56. #define OpenLibBt     2
  57. #define RestoreBt     3
  58. #define KillBt        4
  59.  
  60. #define LI_CNT        5
  61.  
  62. #define NAMEGADGET    LIGadgets[ LibNameStr ]
  63. #define VERSIONGADGET LIGadgets[ LibVersionInt ]
  64.  
  65. #define LIBRARYNAME    StrBfPtr( NAMEGADGET )
  66. #define LIBRARYVERSION IntBfPtr( VERSIONGADGET )
  67.  
  68. IMPORT struct WBStartup *_WBenchMsg;
  69. IMPORT struct Library   *SysBase;
  70.  
  71. PRIVATE char ver[] = "$VER: LibraryInterceptor 1.2 (21-Feb-2001) by J.T. Steichen";
  72.  
  73. struct IntuitionBase *IntuitionBase;
  74. struct GfxBase       *GfxBase;
  75. struct Library       *GadToolsBase;
  76. struct Library       *Exec_Base = NULL;
  77.  
  78. // ----------------------------------------------------------------- 
  79.  
  80. #define LVO_OpenLibrary 0xFFFFFDD8  // 0xFFFFFDD8 == -552
  81.  
  82. PUBLIC  LONG OldLibraryVector = NULL;
  83.  
  84. PRIVATE BOOL ReadyToExit      = FALSE;
  85.  
  86. PRIVATE UBYTE InitialName[256] = { 0, };
  87. PRIVATE long  InitialVersion   = 0L;
  88.  
  89. // -----------------------------------------------------------------
  90.  
  91. PRIVATE struct Screen       *Scr     = NULL;
  92. PRIVATE struct Window       *LIWnd   = NULL;
  93. PRIVATE struct Gadget       *LIGList = NULL;
  94. PRIVATE struct IntuiMessage  LIMsg;
  95. PRIVATE struct Gadget       *LIGadgets[ LI_CNT ];
  96.  
  97. PRIVATE struct TextFont *LIFont = NULL;
  98. PRIVATE struct CompFont  CFont  = { 0, };
  99. PRIVATE struct TextAttr  Attr   = { 0, };
  100. PRIVATE struct TextAttr *Font   = NULL;
  101.  
  102. PRIVATE UBYTE *PubScreenName = "Workbench";
  103. PRIVATE APTR   VisualInfo    = NULL;
  104.  
  105. PRIVATE UWORD  LILeft   = 100;
  106. PRIVATE UWORD  LITop    = 16;
  107. PRIVATE UWORD  LIWidth  = 440;
  108. PRIVATE UWORD  LIHeight = 112;
  109.  
  110. PRIVATE UBYTE *LIWdt    = "Library Interceptor ©1999-2001:";
  111. PRIVATE UBYTE *ScrTitle = "Library Interceptor ©1999-2001 by J.T. Steichen";
  112. PRIVATE UBYTE *email    = "jimbot@rconnect.com";
  113.  
  114. PRIVATE UWORD LIGTypes[] = {
  115.  
  116.    STRING_KIND, INTEGER_KIND, BUTTON_KIND,
  117.    BUTTON_KIND, BUTTON_KIND
  118. };
  119.  
  120. PRIVATE int LibNameStrClicked(    void );
  121. PRIVATE int LibVersionIntClicked( void );
  122. PRIVATE int OpenLibBtClicked(     void );
  123. PRIVATE int RestoreBtClicked(     void );
  124. PRIVATE int KillBtClicked(        void );
  125.  
  126. PRIVATE struct NewGadget LINGad[] = {
  127.  
  128.     46, 23, 251, 17, (UBYTE *) "Library Name:",     NULL, LibNameStr, 
  129.    PLACETEXT_ABOVE, NULL, (APTR) LibNameStrClicked,
  130.    
  131.    149, 62,  41, 17, (UBYTE *) "Library Version:",  NULL, LibVersionInt, 
  132.    PLACETEXT_ABOVE, NULL, (APTR) LibVersionIntClicked,
  133.    
  134.     10, 84, 101, 17, (UBYTE *) "_Open Library",     NULL, OpenLibBt, 
  135.    PLACETEXT_IN, NULL, (APTR) OpenLibBtClicked,
  136.  
  137.    119, 84, 120, 17, (UBYTE *) "_Restore Parm's",   NULL, RestoreBt, 
  138.    PLACETEXT_IN, NULL, (APTR) RestoreBtClicked,
  139.  
  140.    245, 84, 150, 17, (UBYTE *) "_Kill Interceptor", NULL, KillBt, 
  141.    PLACETEXT_IN, NULL, (APTR) KillBtClicked
  142. };
  143.  
  144. PRIVATE ULONG LIGTags[] = {
  145.  
  146.    GTST_MaxChars, 256, STRINGA_Justification, GACT_STRINGCENTER, TAG_DONE,
  147.  
  148.    GTIN_Number, 0, GTIN_MaxChars, 10, 
  149.    STRINGA_Justification, GACT_STRINGCENTER, TAG_DONE,
  150.  
  151.    GT_Underscore, '_', TAG_DONE,
  152.    GT_Underscore, '_', TAG_DONE,
  153.    GT_Underscore, '_', TAG_DONE
  154. };
  155.  
  156. // ----------------------------------------------------------------
  157.  
  158. PRIVATE int SetupScreen( void )
  159. {
  160.    Font = &Attr;
  161.  
  162.    if ((Scr = LockPubScreen( PubScreenName )) == NULL)
  163.       return( -1 );
  164.  
  165.    ComputeFont( Scr, Font, &CFont, 0, 0 );
  166.  
  167.    if ((VisualInfo = GetVisualInfo( Scr, TAG_DONE )) == NULL)
  168.       return( -2 );
  169.  
  170.    return( 0 );
  171. }
  172.  
  173. PRIVATE void CloseDownScreen( void )
  174. {
  175.    if (VisualInfo != NULL) 
  176.       {
  177.       FreeVisualInfo( VisualInfo );
  178.       VisualInfo = NULL;
  179.       }
  180.  
  181.    if (Scr != NULL) 
  182.       {
  183.       UnlockPubScreen( NULL, Scr );
  184.       Scr = NULL;
  185.       }
  186.  
  187.    return;
  188. }
  189.  
  190. PRIVATE int OpenLIWindow( void )
  191. {
  192.    struct NewGadget  ng;
  193.    struct Gadget    *g;
  194.    UWORD             lc, tc;
  195.    UWORD             wleft = LILeft, wtop = LITop, ww, wh;
  196.  
  197.    if (LIWnd != NULL)    // LIWnd already opened!
  198.       goto SkipOpening;
  199.       
  200.    ComputeFont( Scr, Font, &CFont, LIWidth, LIHeight );
  201.  
  202.    ww = ComputeX( CFont.FontX, LIWidth  );
  203.    wh = ComputeY( CFont.FontY, LIHeight );
  204.  
  205.    if ((wleft + ww + CFont.OffX + Scr->WBorRight) > Scr->Width) 
  206.       wleft = Scr->Width - ww;
  207.  
  208.    if ((wtop + wh + CFont.OffY + Scr->WBorBottom) > Scr->Height) 
  209.       wtop = Scr->Height - wh;
  210.  
  211.    if ((LIFont = OpenDiskFont( Font )) == NULL)
  212.       return( -5 );
  213.  
  214.    if ((g = CreateContext( &LIGList )) == NULL)
  215.       return( -1 );
  216.  
  217.    for (lc = 0, tc = 0; lc < LI_CNT; lc++) 
  218.       {
  219.       CopyMem( (char *) &LINGad[ lc ], (char *) &ng, 
  220.                (long) sizeof( struct NewGadget )
  221.              );
  222.  
  223.       ng.ng_VisualInfo = VisualInfo;
  224.       ng.ng_TextAttr   = Font;
  225.       ng.ng_LeftEdge   = CFont.OffX + ComputeX( CFont.FontX, 
  226.                                                 ng.ng_LeftEdge
  227.                                               );
  228.  
  229.       ng.ng_TopEdge    = CFont.OffY + ComputeY( CFont.FontY, 
  230.                                                 ng.ng_TopEdge
  231.                                               );
  232.  
  233.       ng.ng_Width      = ComputeX( CFont.FontX, ng.ng_Width );
  234.       ng.ng_Height     = ComputeY( CFont.FontY, ng.ng_Height);
  235.  
  236.       LIGadgets[ lc ] = g = CreateGadgetA( (ULONG) LIGTypes[ lc ], 
  237.                               g, 
  238.                               &ng, 
  239.                               (struct TagItem *) &LIGTags[ tc ] );
  240.  
  241.       while (LIGTags[ tc ]) 
  242.          tc += 2;
  243.  
  244.       tc++;
  245.       
  246.       if (NOT g)
  247.          return( -2 );
  248.       }
  249.  
  250.    if ((LIWnd = OpenWindowTags( NULL,
  251.  
  252.                   WA_Left,        wleft,
  253.                   WA_Top,         wtop,
  254.                   WA_Width,       ww + CFont.OffX + Scr->WBorRight,
  255.                   WA_Height,      wh + CFont.OffY + Scr->WBorBottom,
  256.  
  257.                   WA_IDCMP,       STRINGIDCMP | INTEGERIDCMP 
  258.                     | BUTTONIDCMP | IDCMP_VANILLAKEY
  259.                     | IDCMP_REFRESHWINDOW,
  260.  
  261.                   WA_Flags,       WFLG_DRAGBAR | WFLG_DEPTHGADGET
  262.                     | WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_RMBTRAP,
  263.  
  264.                   WA_Gadgets,     LIGList,
  265.                   WA_Title,       LIWdt,
  266.                   WA_ScreenTitle, ScrTitle,
  267.                   TAG_DONE )) == NULL)
  268.       return( -4 );
  269.  
  270. SkipOpening:
  271.  
  272.    GT_RefreshWindow( LIWnd, NULL );
  273.  
  274.    return( 0 );
  275. }
  276.  
  277.  
  278. PRIVATE int LibNameStrClicked( void )
  279. {
  280.    return( (int) TRUE );
  281. }
  282.  
  283. PRIVATE int LibVersionIntClicked( void )
  284. {
  285.    return( (int) TRUE );
  286. }
  287.  
  288. PRIVATE void CloseLIWindow( void )
  289. {
  290.    if (LIWnd != NULL) 
  291.       {
  292.       CloseWindow( LIWnd );
  293.       LIWnd = NULL;
  294.       }
  295.  
  296.    if (LIGList != NULL) 
  297.       {
  298.       FreeGadgets( LIGList );
  299.       LIGList = NULL;
  300.       }
  301.  
  302.    if (LIFont != NULL) 
  303.       {
  304.       CloseFont( LIFont );
  305.       LIFont = NULL;
  306.       }
  307.  
  308.    return;
  309. }
  310.  
  311. PRIVATE int OpenLibBtClicked( void )
  312. {
  313.    strcpy( &InitialName[0], LIBRARYNAME );
  314.    
  315.    InitialVersion = LIBRARYVERSION;
  316.    
  317.    CloseLIWindow();
  318.    return( (int) FALSE );
  319. }
  320.  
  321. /****i* RestoreBtClicked() *********************************************
  322. *
  323. * NAME
  324. *    RestoreBtClicked()
  325. *
  326. * DESCRIPTION
  327. *    Place stored library name & version back into the data entry
  328. *    gadgets.
  329. ************************************************************************
  330. *
  331. */
  332.  
  333. PRIVATE int RestoreBtClicked( void )
  334. {
  335.    GT_SetGadgetAttrs( NAMEGADGET, LIWnd, NULL,
  336.                       GTST_String, (STRPTR) &InitialName[0],
  337.                       TAG_DONE
  338.                     );
  339.  
  340.    GT_SetGadgetAttrs( VERSIONGADGET, LIWnd, NULL,
  341.                       GTIN_Number, InitialVersion, 
  342.                       TAG_DONE
  343.                     );
  344.    
  345.    return( (int) TRUE );
  346. }
  347.  
  348. PRIVATE int KillBtClicked( void )
  349. {
  350.    ReadyToExit = TRUE; // Rest of program will now know to shut down.
  351.  
  352.    return( OpenLibBtClicked() );
  353. }
  354.  
  355. PRIVATE void AboutProgram( void )
  356. {
  357.    char m[256], *msg   = &m[0];
  358.    char t[80],  *title = &t[0];
  359.  
  360.    if (GetProgramName( msg, 255L ) == 0)
  361.       strcpy( title, "About LibraryInterceptor:" );
  362.    else
  363.       sprintf( title, "About %s:", msg );   
  364.  
  365.    sprintf( msg, "This program allows the user to change Library\n"
  366.                  "parameters before a Library gets opened.\n"
  367.                  "my e-mail:  %s", email 
  368.           );
  369.  
  370.  
  371.    SetReqButtons( "OKAY" );
  372.    (void) Handle_Problem( msg, title, NULL );
  373.    SetReqButtons( "CONTINUE|ABORT!" );
  374.    
  375.    return;
  376. }
  377.  
  378. PRIVATE int LIVanillaKey( int whichkey )
  379. {
  380.    int rval = TRUE;
  381.    
  382.    switch (whichkey)
  383.       {
  384.       case 'o':
  385.       case 'O':
  386.          rval = OpenLibBtClicked();
  387.          break;
  388.          
  389.       case 'r':
  390.       case 'R':
  391.          rval = RestoreBtClicked();
  392.          break;
  393.       
  394.       case 'k':
  395.       case 'K':
  396.       case 'x':
  397.       case 'X':
  398.       case 'q':
  399.       case 'Q':
  400.          rval = KillBtClicked();
  401.          break;
  402.  
  403.       case 'i':
  404.       case 'I':
  405.          AboutProgram();
  406.          break;
  407.           
  408.       default:
  409.          break;      
  410.       }
  411.  
  412.    return( rval );
  413. }
  414.  
  415. PRIVATE void ShutdownProgram( void )
  416. {
  417.    CloseLIWindow();
  418.    CloseDownScreen();
  419.    CloseLibs();
  420.  
  421.    if (Exec_Base != NULL)
  422.       {
  423.       CloseLibrary( Exec_Base );
  424.       Exec_Base = NULL;
  425.       }
  426.  
  427.    return;
  428. }
  429.  
  430. PRIVATE int HandleLIIDCMP( void )
  431. {
  432.    struct IntuiMessage  *m;
  433.    int                 (*func)( void );
  434.    BOOL                  running = TRUE;
  435.  
  436.    while (running == TRUE)
  437.       {
  438.       if ((m = GT_GetIMsg( LIWnd->UserPort )) == NULL) 
  439.          {
  440.          (void) Wait( 1L << LIWnd->UserPort->mp_SigBit );
  441.          continue;
  442.          }
  443.  
  444.       CopyMem( (char *) m, (char *) &LIMsg, 
  445.                (long) sizeof( struct IntuiMessage )
  446.              );
  447.  
  448.       GT_ReplyIMsg( m );
  449.  
  450.       switch (LIMsg.Class) 
  451.          {
  452.          case IDCMP_REFRESHWINDOW:
  453.             GT_BeginRefresh( LIWnd );
  454.             GT_EndRefresh( LIWnd, TRUE );
  455.             break;
  456.  
  457.          case IDCMP_VANILLAKEY:
  458.             running = LIVanillaKey( LIMsg.Code );
  459.             break;
  460.  
  461.          case IDCMP_GADGETUP:
  462.             func = (void *) ((struct Gadget *)LIMsg.IAddress)->UserData;
  463.             if (func != NULL)
  464.                running = func();
  465.             
  466.             break;
  467.          }
  468.       }
  469.  
  470.    if (ReadyToExit == TRUE)
  471.       {
  472.       running = FALSE;
  473.       }
  474.  
  475.    return( running );
  476. }
  477.  
  478. // ------------------------------------------------------------------
  479.  
  480.  
  481. typedef __asm struct Library *(*FPTR)( register __a1 UBYTE *, 
  482.                                        register __d0 long 
  483.                                      );
  484.  
  485. PUBLIC __saveds __asm struct Library
  486.  
  487.        *NewFunction( register __a1 UBYTE *libname, 
  488.                      register __d0 long   libversion 
  489.                    )
  490. {
  491.    FPTR            fptr = NULL;
  492.    struct Library *ptr  = NULL;
  493.    int             chk  = TRUE;
  494.  
  495.    fptr = (FPTR) OldLibraryVector;
  496.  
  497. #  ifdef DEBUG
  498.    fprintf( stderr, "fptr == 0x%08LX\n", fptr );   
  499. #  endif
  500.  
  501.    if (LIWnd == NULL)
  502.       {
  503.       if (OpenLIWindow() < 0)
  504.          {
  505.          ShutdownProgram();
  506.          ReadyToExit = TRUE;
  507.       
  508.          ptr = fptr( libname, libversion );
  509.          return( ptr );
  510.          }   
  511.  
  512.       strncpy( &InitialName[0], libname, 255 );
  513.       InitialVersion = libversion;
  514.  
  515.       GT_SetGadgetAttrs( NAMEGADGET, LIWnd, NULL,
  516.                          GTST_String, (STRPTR) &InitialName[0],
  517.                          TAG_DONE
  518.                        );
  519.  
  520.       GT_SetGadgetAttrs( VERSIONGADGET, LIWnd, NULL,
  521.                          GTIN_Number, InitialVersion, 
  522.                          TAG_DONE
  523.                        );
  524.    
  525.       SetNotifyWindow( LIWnd );
  526.    
  527.       chk = HandleLIIDCMP();
  528.       ptr = fptr( &InitialName[0], InitialVersion );
  529.       }
  530.    else
  531.       ptr = fptr( libname, libversion ); // ??????????????
  532.  
  533.    return( ptr );
  534. }
  535.  
  536. // ----------------------------------------------------------------
  537.  
  538. PRIVATE int SetupProgram( void )
  539. {
  540.    if (OpenLibs() < 0)
  541.       return( -1 );
  542.  
  543.    if ((Exec_Base = OpenLibrary( "exec.library", 39L )) == NULL)
  544.       {
  545.       CloseLibs();
  546.       return( -1 );
  547.       }      
  548.  
  549.    if (SetupScreen() < 0)
  550.       {
  551.       CloseLibs();
  552.       CloseLibrary( Exec_Base );
  553.       Exec_Base = NULL;
  554.       return( -2 );
  555.       }   
  556.  
  557.    return( 0 );   
  558. }
  559.  
  560.  
  561. PRIVATE void SFReplace( void )
  562. {
  563. #  ifdef DEBUG
  564.    fprintf( stderr, "NewFunction() == 0x%08LX\n", NewFunction );
  565. #  endif
  566.  
  567.    Forbid();
  568.  
  569.       OldLibraryVector = SetFunction( Exec_Base,
  570.                                       LVO_OpenLibrary, 
  571.                                       NewFunction
  572.                                     );
  573.  
  574.       // Clear the cpu's cache so the execution cache is valid:
  575.       CacheClearU();
  576.  
  577.    Permit();
  578.  
  579. #  ifdef DEBUG
  580.    fprintf( stderr, "OldLibraryVecotor == 0x%08LX\n", OldLibraryVector );
  581. #  endif
  582.  
  583.    return;
  584. }
  585.  
  586. PRIVATE BOOL SFRestore( void )
  587. {
  588.    BOOL All_OKAY = FALSE;
  589.  
  590.    FPTR func;
  591.  
  592.    Forbid();
  593.  
  594.       // Put old pointer back and get current pointer at same time
  595.       func = SetFunction( Exec_Base,
  596.                           LVO_OpenLibrary,
  597.                           OldLibraryVector
  598.                         );
  599.  
  600.       // Clear the cpu's cache so the execution cache is valid:
  601.       CacheClearU();
  602.  
  603.       // Check to see if the pointer we got back is ours:
  604.       if (func != (FPTR) NewFunction)
  605.          {
  606.          // If not, leave jump table in place:
  607.          All_OKAY = FALSE;
  608.  
  609.          (void) SetFunction( Exec_Base, LVO_OpenLibrary, func );
  610.          }
  611.       else
  612.          {
  613.          All_OKAY = TRUE;
  614.          }
  615.  
  616.       // Clear the cpu's cache so the execution cache is valid:
  617.       CacheClearU();
  618.  
  619.    Permit();
  620.  
  621.    return( All_OKAY );
  622. }
  623.  
  624. // --------------------------------------------------------------------
  625.  
  626. PUBLIC int main( int argc, char **argv )
  627. {
  628.    long offset = LVO_OpenLibrary;
  629.  
  630. #  ifdef DEBUG
  631.    long *v = NULL, Vector = NULL;
  632. #  endif
  633.    
  634.    if (SetupProgram() < 0)
  635.       {
  636.       fprintf( stderr, "Couldn't Setup %s!\n", argv[0] );
  637.       return( RETURN_FAIL );
  638.       }
  639.  
  640. #  ifdef DEBUG
  641.    fprintf( stderr, 
  642.             "SysBase == 0x%08LX, Exec_Base = 0x%08LX\n", 
  643.             SysBase, Exec_Base 
  644.           );
  645.  
  646.    v      = (long *) (((long) Exec_Base) + offset + 2);
  647.    Vector = *v;
  648.  
  649.    fprintf( stderr, 
  650.             "Before patch, OpenLibrary() == 0x%08LX: JMP 0x%08LX\n",
  651.             ((long) Exec_Base) + offset, 
  652.             Vector
  653.           );
  654.  
  655.    fprintf( stderr, "NewFunction() == 0x%08LX\n", &NewFunction );
  656. #  endif
  657.  
  658.    SFReplace(); // Install LibraryInterceptor code.
  659.  
  660. #  ifdef DEBUG
  661.    Vector = *v;
  662.  
  663.    fprintf( stderr, 
  664.             "After patch, OpenLibrary() == 0x%08LX: JMP 0x%08LX\n",
  665.             ((long) Exec_Base) + offset, 
  666.             Vector
  667.           );
  668. #  endif
  669.  
  670.    while (ReadyToExit == FALSE)
  671.       ;
  672.  
  673. //#  ifdef DEBUG
  674.    fprintf( stderr, "Attempting to restore OpenLibrary() vector...\n" );
  675. //#  endif
  676.  
  677.    if (SFRestore() != TRUE)
  678.       {
  679.       char em[80];
  680.       
  681. #     ifdef DEBUG
  682.       fprintf( stderr, "Couldn't Restore OpenLibrary() vector!\n" );
  683. #     endif
  684.       
  685.       SetReqButtons( "Aaarrgghh!!" );
  686.  
  687.       sprintf( &em[0], "%s ERROR:", argv[0] );
  688.  
  689.       (void) Handle_Problem( "Couldn't Restore OpenLibrary() vector!",
  690.                              &em[0], NULL
  691.                            );
  692.       
  693.       ShutdownProgram();
  694.       return( RETURN_ERROR );
  695.       }
  696.  
  697. //#  ifdef DEBUG
  698.    fprintf( stderr, "Shutting down %s\n", argv[0] );
  699. //#  endif
  700.  
  701.    ShutdownProgram();
  702.       
  703.    return( RETURN_OK );
  704. }
  705.  
  706. /* -------------- END of LibraryInterceptor.c file! ----------------- */
  707.