home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 5 / 05.iso / a / a012 / 1.ddi / APPENDIX.EXE / ERRSAVE.PRG < prev    next >
Encoding:
Text File  |  1991-05-01  |  18.3 KB  |  556 lines

  1. /*
  2.    ERRSAVE.PRG:  Error object inspector and recorder.
  3.    Author: Craig Yellick
  4.    Excerpted from "Clipper 5: A Developer's Guide"
  5.    Copyright (c) 1991 M&T Books
  6.                       501 Galveston Drive
  7.                       Redwood City, CA 94063-4728
  8.                       (415) 366-3600
  9.  
  10.    Thanks to Ted Means for supplying the little Default() ditty in ASM.
  11.  
  12.    ------------------------------------------------------------------------
  13.  
  14.    To install this utility in your application the following lines must
  15.    be executed prior to any errors that you want to trap.  This means
  16.    they must be the first executable statements in your application if
  17.    you want ErrorSaver() to be in effect for the entire application. The
  18.    text filename is optional, if specified the file will be appended
  19.    with error information.
  20.  
  21.           local defErr := errorblock()
  22.           errorblock( { |e| ErrorSaver(e, defErr, ;
  23.                       "<App Title>", "ERR.TXT") } )
  24.  
  25.    Compile with: /n /w /a
  26.  
  27.    ------------------------------------------------------------------------
  28.  
  29.    Note: ErrorSaver() calls Default(), a small ASM routine that returns
  30.    the letter of the current drive volume. An object file, DEFAULT.OBJ,
  31.    has been included. If you currently use a 3rd-party library which
  32.    already supplies this function you can change the call to Default()
  33.    to use the library's syntax. If you prefer not to use the function
  34.    you can delete the reference, it's not critical.
  35.  
  36.    ------------------------------------------------------------------------
  37.  
  38.    File Contents
  39.    =============
  40.  
  41.    ErrorSaver( objError, [bError], [cAppTitle], [cFilename] )  -->  .f.
  42.  
  43.      Target function for error blocks.  Displays screen containing
  44.      complete error information and scrolling program trace back window.
  45.      If optional filename is specified the error information is appended
  46.      to file.
  47.  
  48.  
  49.    static Message( nTop, nLeft, [nDepth], [nWidth], ;
  50.                    [aStaticText], [cStaticColor],   ;
  51.                    [aVariText], [cColorVari] )  -->  nSelection
  52.  
  53.      General-purpose message display utility.  Many possible parameter
  54.      combinations. See source code comments for details.
  55.  
  56.  
  57.    static SetScrEnv( [aNewEnv] )  -->  aOldEnv
  58.  
  59.      Saves/restores screen environment to/from an array: cursor, color,
  60.      row, column, screen contents.
  61.  
  62.  
  63.    static XtoS( xValue )  -->  cValue
  64.  
  65.      Takes parameter of any data type and returns character string
  66.      representation.
  67.  
  68.  
  69.    static DosErrText( nError )  -->  cDescription
  70.  
  71.      Returns description of DOS error code number.
  72.  
  73.    ------------------------------------------------------------------------
  74. */
  75.  
  76.  
  77. /*
  78.     []  A test of the error handler.
  79.  
  80.     Compile ERRSAVE.PRG to ERRSAVE.OBJ and link with CLIPPER.LIB,
  81.     EXTEND.LIB and the supplied DEFAULT.OBJ.
  82.  
  83.     When you run ERRSAVE.EXE it will immediatly bomb with a "missing
  84.     database" error and display the ErrorSaver screen.  A file called
  85.     ERR.TXT will also have a copy of the error information appended to
  86.     the end of it.
  87.  
  88.     Comment-out the "#define TESTING" line and re-compile to prevent
  89.     function Main() and the four "nesting" functions from being
  90.     included in the ERRSAVE.OBJ you use in your applications.
  91. */
  92.  
  93. #define TESTING
  94. #ifdef TESTING
  95.  
  96.          function Main()
  97.            local defErr := errorblock()
  98.            errorblock( { |e| ErrorSaver(e, defErr, ;
  99.                        "Test Application", "ERR.TXT") } )
  100.            ? "Here we go..."
  101.            nest1()
  102.          return nil
  103.  
  104.          function nest1()
  105.            nest2()
  106.          return nil
  107.          function nest2()
  108.            nest3()
  109.          return nil
  110.          function nest3()
  111.            nest4()
  112.          return nil
  113.          function nest4()
  114.            nest5()
  115.          return nil
  116.          function nest5()
  117.            use WHERIZIT
  118.          return nil
  119. #endif
  120.  
  121. /* --------------------------------------------------------------------- */
  122.  
  123. /*
  124.     Handy preprocessor directives.
  125. */
  126.  
  127.  
  128. //  Convert integers to left-trimmed strings.
  129. #define lstr(n)  (ltrim(str(n)))
  130.  
  131. //  Convert logicals to text.
  132. #define YN(L)    if(L, "Yes", "No ")
  133.  
  134. //  Short-hand.
  135. #translate ifempty(<a>, <b>) => if(empty(<a>), <b>, <a>)
  136.  
  137. /* --------------------------------------------------------------------- */
  138.  
  139.  
  140. function ErrorSaver(e, defError, appTitle, filename)
  141. /*
  142.    Display a screen containing everything known about the run-time error
  143.    represented by the error object passed as a parameter.  If a filename
  144.    is specified, append error information to the file.  If an application
  145.    title is specified it will be displayed and written to the file.
  146.  
  147.    e          The error object containing run-time information.
  148.  
  149.    defError   The default error handler, required if you want
  150.               to be able to pass the error along to Clipper's
  151.               default error handler after inspecting it here.
  152.  
  153.    appTitle   Optional but important, this string will get displayed
  154.               on the screen and written to the file. Put a version
  155.               number here as well, it'll help with phone support.
  156.  
  157.    filename   Optional name of the file in which to record the error.
  158.               If it already exists it will be appended.
  159.  
  160. */
  161.  
  162. local errEnv, appEnv := SetScrEnv()
  163. local varList_, trace_
  164. local i, r, c, sel, argStr, argCnt, osDescr
  165.  
  166.   /*
  167.      Build array with procedure/line traceback. Start from 1, since we
  168.      don't care about being down here in ErrorSaver() or where the
  169.      errorblock was installed.
  170.   */
  171.   i := 1
  172.   trace_ := {}
  173.   do while .not. empty(procname(i))
  174.     aadd(trace_, procname(i) +" (" +lstr(procline(i)) +")")
  175.         i++
  176.   enddo
  177.  
  178.   //  Build list of arguments (if any)
  179.   if valtype(e:args) = "A"
  180.     argStr := ""
  181.     aeval(e:args, { |s| argStr += (XtoS(s) +", ")} )
  182.     argStr := left(argStr, min(len(argStr) -2, 35))
  183.     argCnt := lstr(len(e:args))
  184.   else
  185.     argStr := "<none>"
  186.     argCnt := "0"
  187.   endif
  188.  
  189.   //  Build description of operating systen error
  190.   if e:osCode > 0
  191.     osDescr := lstr(e:osCode) +": " +left(DosErrText(e:osCode), 35)
  192.   else
  193.     osDescr := "0: n/a"
  194.   endif
  195.  
  196.   varList_ := {"arg count         " +argCnt,              ;
  197.                "args              " +argStr,              ;
  198.                "canDefault        " +YN(e:canDefault),    ;
  199.                "canRetry          " +YN(e:canRetry),      ;
  200.                "canSubstitute     " +YN(e:canSubstitute), ;
  201.                "description       " +e:description,       ;
  202.                "filename          " +e:filename,          ;
  203.                "genCode           " +lstr(e:genCode),     ;
  204.                "operation         " +e:operation,         ;
  205.                "osCode            " +osDescr,             ;
  206.                "severity          " +lstr(e:severity),    ;
  207.                "subCode           " +lstr(e:subCode),     ;
  208.                "subSystem         " +e:subSystem,         ;
  209.                "tries             " +lstr(e:tries),       ;
  210.                "----------------- ",                      ;
  211.                "Free memory   (0) " +lstr(memory(0)),     ;
  212.                "Largest block (1) " +lstr(memory(1)),     ;
  213.                "Run area      (2) " +lstr(memory(2))  }
  214.  
  215.  
  216.   //  Display screen heading
  217.   if valtype(appTitle) <> "C"
  218.     appTitle := ""
  219.   endif
  220.   Message(1, 20,,, {padc(appTitle, 40), ;
  221.                     padc("Run-Time Error", 40)}, "R+/B")
  222.  
  223.  
  224.   //  If filename was specified, open it up and append error info.
  225.   if valtype(filename) = "C"
  226.     set alternate to (filename) additive
  227.     set alternate on
  228.     set console off
  229.     ? replicate("=", 70)
  230.     ?  "ErrorSaver: This run-time error logged on "
  231.     ?? dtoc(date()) +" at " +time()
  232.     if .not. empty(appTitle)
  233.       ?  "Application: " +appTitle
  234.     endif
  235.     ?  "Operating system = " +os() +", network = "
  236.     ?? ifempty(netname(), "<none>")
  237.     ?  "Available diskspace = "
  238.     ??  ltrim(transform(diskspace(), "999,999,999,999")) +" in "
  239.     ??  default() +":\" +curdir()
  240.     ?  "PATH    = " +ifempty(gete("PATH"),    "<none>")
  241.     ?  "COMSPEC = " +ifempty(gete("COMSPEC"), "<none>")
  242.     ?  "CLIPPER = " +ifempty(gete("CLIPPER"), "<none>")
  243.     //
  244.     //  Add other DOS environment variables you might need to know about.
  245.     //
  246.     ? replicate("-", 70)
  247.     ? "Traceback: Proc (Line)   Error Information"
  248.     ? "~~~~~~~~~~~~~~~~~~~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
  249.     for i := 1 to max(len(trace_), len(varList_))
  250.       if i <= len(trace_)
  251.         ? "  " +padr(trace_[i], 20)
  252.       else
  253.         ? space(22)
  254.       endif
  255.       ?? space(3)
  256.       if i <= len(varList_)
  257.          ?? varList_[i]
  258.       endif
  259.     next i
  260.     set console on
  261.     set alternate off
  262.     set alternate to
  263.   endif
  264.  
  265.  
  266.   //  Display error object instance variables
  267.   Message(4, 2, 19, 44, varlist_, "W+/B")
  268.  
  269.   //  Instructions for viewing traceback window.
  270.   //  (If window isn't filled, no need to display this.)
  271.   if len(trace_) > 7
  272.     Message(15, 47, 8, 30, {"Use page-up and", ;
  273.                             "page-down keys to", ;
  274.                             "scroll through the", ;
  275.                             "traceback window.", "", ;
  276.                             "Press ESC to continue."}, "GR+/B")
  277.   endif
  278.  
  279.   //  If window isn't filled, no need to wait for keystroke.
  280.   if len(trace_) <= 7
  281.     keyboard(chr(27))
  282.   endif
  283.  
  284.   //  Display traceback in scrolling window
  285.   Message(4, 47, 10, 30, ;                           // Co-ordinates
  286.          {"Traceback: Procedure (Line)"}, "W+/B", ;  // Static portion
  287.          trace_, "W/B,B+/W")                         // Scrollable portion
  288.  
  289.  
  290.   do while .t.
  291.     sel := Message(15, 47, 8, 30, ;
  292.                   {"Select..."}, "GR+/B", ;
  293.                   {"Quit to DOS", ;
  294.                    "Pass Error to Clipper", ;
  295.                    "View Application Screen"}, "W/B,B+/W")
  296.    do case
  297.    case sel = 1
  298.      exit
  299.    case sel = 2
  300.      if defError <> nil
  301.        SetScrEnv(appEnv)
  302.        return eval(defError, e)
  303.      endif
  304.    case sel = 3
  305.      errEnv := SetScrEnv()
  306.      SetScrEnv(appEnv)
  307.      r := row()
  308.      c := col()
  309.      setcolor("W+*/R")
  310.      @ 0,0 say "ERROR!"
  311.      devpos(r,c)
  312.      inkey(0)
  313.      SetScrEnv(errEnv)
  314.    endcase
  315.   enddo
  316.  
  317.   //  Before returining, restore screen environment.
  318.   SetScrEnv(appEnv)
  319.  
  320. return .f.
  321.  
  322. /* --------------------------------------------------------------------- */
  323.  
  324.  
  325. static function Message(r1, c1, rLen, cLen, ;
  326.                         stat_, clrStat, ;
  327.                         vari_, clrVari)
  328. /*
  329.    Display static message in box with scrolling variable section.
  330.    Returns variable selection number, or zero if no selection made.
  331.  
  332.    Important note-- the window size parameters are not the usual "four
  333.    corners", you must specify a starting row/col and optionally a
  334.    depth and width.  For example, (2,5,10,30) is a box 10 rows long
  335.    and 30 columns wide starting at row 2, column 5.
  336.  
  337.    ╔═══════════════════╗
  338.    ║ Static line(s)    ║
  339.    ║ :                 ║
  340.    ╟───────────────────╢
  341.    ║ Scrolling line(s) ║
  342.    ║ : (optional)      ║
  343.    ║ :                 ║
  344.    ╚═══════════════════╝
  345.  
  346.    r1, c1   Required starting row and column.
  347.  
  348.    rLen     Optional number of rows down from r1, if not specified
  349.             will be calculated based on maxRow() and larger
  350.             of element count in stat_ and vari_ arrays.
  351.  
  352.    cLen     Optional number of columns over from c1, if not specified
  353.             will be calculated based on maxCol() and longest
  354.             element within stat_ and vari_ arrays.
  355.  
  356.    stat_    Array of strings which form static part of message.
  357.  
  358.    clrStat  Option color for static part of message.
  359.  
  360.    vari_    Optional array of strings which form the scrolling part
  361.             of the message.
  362.  
  363.    clrVari  Optional color for scrolling part of message.
  364.  
  365. */
  366.  
  367. local i, sel := 0, maxC := 0, maxR := 0, clr := setcolor()
  368.  
  369.   //  Determine length of longest string in stat_ and vari_.
  370.   if cLen = nil
  371.     aeval(stat_, { |s| maxC := max(len(s), maxC) } )
  372.     if vari_ <> nil
  373.       aeval(vari_, { |s| maxC := max(len(s), maxC) } )
  374.     endif
  375.     //  Add extra columns for box lines and spacing.
  376.     maxC := min(maxCol() -c1, maxC +3)
  377.   else
  378.     maxC := min(maxCol() -c1, cLen)
  379.   endif
  380.  
  381.   //  Determine number of rows required.
  382.   if rLen = nil
  383.     //  Add extra rows for spacing.
  384.     i := if(vari_ = nil, 1, len(vari_) +2)
  385.     maxR := min(maxRow() -r1, len(stat_) +i)
  386.   else
  387.     maxR := min(maxRow() -r1, rLen)
  388.   endif
  389.  
  390.   //  Clear the area and draw box lines
  391.   setcolor(clrStat)
  392.   @ r1, c1 clear to r1 +maxR, c1 +maxC
  393.   @ r1, c1 to r1 +maxR, c1 +maxC double
  394.   if vari_ <> nil
  395.     @ r1 +len(stat_) +1, c1 say "╟" +replicate("─", maxC -1) +"╢"
  396.   endif
  397.  
  398.   //  Display static line(s)
  399.   for i := 1 to len(stat_)
  400.     @ r1 +i, c1 +2 say stat_[i]
  401.   next i
  402.  
  403.   //  If variable portion specified, display in scrolling window.
  404.   if vari_ <> nil
  405.     setcolor(clrVari)
  406.     sel := achoice(r1 +len(stat_) +2, c1 +2, ;
  407.                    r1 +maxR -1, c1 +maxC -2, vari_)
  408.   endif
  409.  
  410.   //  Restore original colors.
  411.   setcolor(clr)
  412.  
  413. return sel
  414.  
  415.  
  416. /* --------------------------------------------------------------------- */
  417.  
  418.  
  419. static function SetScrEnv(restore_)
  420. /*
  421.    Return array containing current cursor, color, row, column
  422.    and screen contents.  If an array is passed, restore the
  423.    same elements based on the array's contents.
  424. */
  425.  
  426. // Save current screen environment
  427. local old := { setcursor(), ;  //  1
  428.                setcolor(),  ;  //  2
  429.                row(), ;        //  3
  430.                col(), ;        //  4
  431.                savescreen(0,0,maxrow(),maxcol()) }  //  5
  432.  
  433.   if valtype(restore_) = "A"
  434.     setcursor(restore_[1])
  435.     setcolor(restore_[2])
  436.     devpos(restore_[3], restore_[4])
  437.     restscreen(0,0,maxrow(),maxcol(), restore_[5])
  438.   endif
  439.  
  440. return old
  441.  
  442.  
  443. /* --------------------------------------------------------------------- */
  444.  
  445.  
  446. static function XtoS(x)
  447. /*
  448.    Takes parameter of any type and returns a string version.
  449. */
  450. local s
  451.   if     valtype(x) = "C"
  452.     s := x
  453.   elseif valtype(x) = "N"
  454.     s := lstr(x)
  455.   elseif valtype(x) = "D"
  456.     s := dtoc(x)
  457.   elseif valtype(x) = "L"
  458.     s := if(x, ".t.", ".f.")
  459.   endif
  460. return s
  461.  
  462.  
  463. /* --------------------------------------------------------------------- */
  464.  
  465.  
  466. static function DosErrText(n)
  467. /*
  468.    Return description of DOS error code.
  469.    (Descriptions based on table D-1 in
  470.    Clipper 5.0 Programming & Utilities Guide.)
  471. */
  472. local descr_ := {"Invalid function number", ;  // 1
  473.                  "File not found", ;  // 2
  474.                  "Path not found", ;  // 3
  475.                  "Too many files open (no handles left)", ;  // 4
  476.                  "Access denied", ;  // 5
  477.                  "Invalid handle", ;  // 6
  478.                  "Memory control blocks destroyed (oh, my)", ; // 7
  479.                  "Insufficient memory", ;  // 8
  480.                  "Invalid memory block address", ;  // 9
  481.                  "Invalid environment", ;  // 10
  482.                  "Invalid format", ;  // 11
  483.                  "Invalid access code", ;  // 12
  484.                  "Invalid data", ;  // 13
  485.                  , ;  // 14
  486.                  "Invalid drive was specified", ;  // 15
  487.                  "Attempt to remove the current directory", ;  // 16
  488.                  "Not same device", ;  // 17
  489.                  "No more files", ;  // 18
  490.                  "Attempt to write on write-protected diskette", ;  // 19
  491.                  "Unknown unit", ;  // 20
  492.                  "Drive not ready", ;  // 21
  493.                  "Unknown command", ;  // 22
  494.                  "Data error (CRC)", ;  // 23
  495.                  "Bad request structure length", ;  // 24
  496.                  "Seek error", ;  // 25
  497.                  "Unknown media type", ;  // 26
  498.                  "Sector not found", ;  // 27
  499.                  "Printer out of paper", ;  // 28
  500.                  "Write fault", ;  // 29
  501.                  "Read fault", ;  // 30
  502.                  "General failure", ;  // 31
  503.                  "Sharing violation", ;  // 32
  504.                  "Lock violation", ;  // 33
  505.                  "Invalid disk change", ;  // 34
  506.                  "FCB unavailable", ;  // 35
  507.                  "Sharing buffer overflow", ;  // 36
  508.                  ,,,,,,,,,,,,, ;  // 37-49
  509.                  "Network request not supported", ;  // 50
  510.                  "Remote computer not listening", ;  // 51
  511.                  "Duplicate name on network", ;  // 52
  512.                  "Network name not found", ;  // 53
  513.                  "Network busy", ;  // 54
  514.                  "Network device no longer exists", ;  // 55
  515.                  "Network BIOS command limit exceeded", ;  // 56
  516.                  "Network adapter hardware error", ;  // 57
  517.                  "Incorrect response from network", ;  // 58
  518.                  "Unexpected network error", ;  // 59
  519.                  "Incompatible remote adapter", ;  // 60
  520.                  "Print queue full", ;  // 61
  521.                  "Not enough space for print file", ;  // 62
  522.                  "Print file deleted (not enough space)", ;  // 63
  523.                  "Network name deleted", ;  // 64
  524.                  "Access denied", ;  // 65
  525.                  "Network device type incorrect", ;  // 66
  526.                  "Network name not found", ;  // 67
  527.                  "Network name limit exceeded", ;  // 68
  528.                  "Network BIOS session limit exceeded", ;  // 69
  529.                  "Temporarily paused", ;  // 70
  530.                  "Network request not accepted", ;  // 71
  531.                  "Print or disk redirection paused", ;  // 72
  532.                  ,,,,,,, ;  // 73-79
  533.                  "File already exists", ;  // 80
  534.                  , ;  // 81
  535.                  "Cannot make directory entry", ;  // 82
  536.                  "Fail on INT 24h", ;  // 83
  537.                  "Too many redirections", ;  // 84
  538.                  "Duplicate redirection", ;  // 85
  539.                  "Invalid password", ;  // 86
  540.                  "Invalid parameter", ;  // 87
  541.                  "Network device fault", ;  // 88
  542.                  ;
  543.                  "Undefined or reserved error code!" } // +1
  544.  
  545.   /*
  546.      Check that code number is within known upper limit,
  547.      and that a description is available for it.
  548.   */
  549.   if (n > (len(descr_) -1)) .or. (descr_[n] = nil)
  550.     n := len(descr_)
  551.   endif
  552.  
  553. return descr_[n]
  554.  
  555. * eof ErrSave.Prg
  556.