home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 3 / goldfish_volume_3.bin / files / fish / disks / d1074.lha / Programs / C_dt / source / dispatch.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-01  |  15.0 KB  |  589 lines

  1. /*
  2. ** $PROJECT: c.datatype
  3. **
  4. ** $VER: dispatch.c 39.4 (01.04.95)
  5. **
  6. ** by
  7. **
  8. ** Stefan Ruppert , Windthorststraße 5 , 65439 Flörsheim , GERMANY
  9. **
  10. ** (C) Copyright 1995
  11. ** All Rights Reserved !
  12. **
  13. ** $HISTORY:
  14. **
  15. ** 01.04.95 : 039.004 : added GLOBAL template
  16. ** 23.03.95 : 039.003 : added TEXT arg, now full tabs handling
  17. ** 13.03.95 : 039.002 : autodoc completed
  18. ** 06.03.95 : 039.001 : initial
  19. */
  20.  
  21. /* ------------------------------- include -------------------------------- */
  22.  
  23. #include "classbase.h"
  24.  
  25. /* ------------------------------- autodoc -------------------------------- */
  26.  
  27. /*FS*/ /*"AutoDoc"*/
  28.  
  29. /*GB*** c.datatype/c.datatype ************************************************
  30. *
  31. *    NAME
  32. *        c.datatype - data type for any c source
  33. *
  34. *    FUNCTION
  35. *        This datatype is designed to display C and C++ source codes. It
  36. *        display's different parts of the C source in different style and
  37. *        color.At the moment these parts are :
  38. *           STANDARD - any text which, doesn't match the following parts
  39. *           COMMENT  - any comment such like \* ... *\ and // ...
  40. *           CPP      - any C-PreProcessor keyword like "#define" or "#include"
  41. *           KEYWORD  - any C/C++ keyword, which isn't handled explicitly
  42. *           STORAGE  - extern,static,register,auto keywords
  43. *           TYPES    - basic type keywords like int,char,long etc.
  44. *           TYPENAME - any name following a struct,union,class or enum
  45. *           STRING   - any string or char literal
  46. *           NUMBER   - any number constant decimal,hex
  47. *
  48. *        It uses a parser generated by bison with my yacc grammer.Because it's
  49. *        a parser, it may occur a parse error on some unusual source code. If
  50. *        this happens please send me a description of this parse error and
  51. *        maybe the input file. So I can fix this problem !
  52. *
  53. *    PREFS
  54. *        Env:DataTypes/c.prefs with the following two ReadArgs() templates :
  55. *
  56. *        - CPART/A/K,PEN/N/K,R=RED/N/K,G=GREEN/N/K,B=BLUE/N/K,ITALIC/S,BOLD/S,
  57. *          UNDERLINED/S,TEXT/S
  58. *
  59. *          CPART is one of the explaned cpart names like COMMENT or CPP.
  60. *          PEN assigns the color with the pen number to the specified part
  61. *          R,G,B defines a new color for the specified part. This color is
  62. *                allocated with ObtainBestPenA(...,OBP_Precision,
  63. *                                                  PRECISION_ICON);
  64. *          ITALIC,BOLD,UNDERLINED specifies the font style for the part
  65. *          TEXT treat this CPART as normal text
  66. *
  67. *        - GLOBAL/A/S,TABLENGTH/N/K
  68. *
  69. *          GLOBAL indicates, that this line is a global setting. Note: The
  70. *              /A/S combination isn't supported from ReadArgs(), so I check
  71. *              it manually !
  72. *          TABLENGTH - number of spaces to use for a tab !
  73. *
  74. *    AUTHOR
  75. *        Stefan Ruppert
  76. *        Windthorststrasse 5
  77. *        65439 Floersheim am Main
  78. *        Germany
  79. *        EMail: ruppert@vs3.informatik.fh-wiesbaden.de
  80. *
  81. *    SEE ALSO
  82. *        text.datatype
  83. *
  84. ******************************************************************************
  85. *
  86. */
  87.  
  88. /*FE*/
  89.  
  90. /* ------------------------------- defines -------------------------------- */
  91.  
  92. #define PUDDLE_SIZE        2048
  93. #define G(o)               ((struct Gadget *) (o))
  94.  
  95. #define BUFFER_SIZE        1024
  96. #define EOS                '\0'
  97.  
  98. /* ------------------------ preference definition ------------------------- */
  99.  
  100. /*FS*/ /*"Definitions"*/
  101. const STRPTR prefstemplate[] = {
  102.   { "CPART/A/K,PEN/N/K,R=RED/N/K,G=GREEN/N/K,B=BLUE/N/K,"
  103.      "ITALIC/S,BOLD/S,UNDERLINED/S,TEXT/S"  },
  104.   { "GLOBAL/S,TABLENGTH/N/K" D(",DEBUG/S") },
  105.   { NULL }};
  106.  
  107. D(extern int cdtparse_debug;)
  108.  
  109. enum
  110. {
  111.     TMPLT_CPART,
  112.     TMPLT_GLOBAL
  113. };
  114.  
  115. enum
  116. {
  117.     CPARTARG_CSECTION,
  118.     CPARTARG_PEN,
  119.     CPARTARG_RED,
  120.     CPARTARG_GREEN,
  121.     CPARTARG_BLUE,
  122.     CPARTARG_ITALIC,
  123.     CPARTARG_BOLD,
  124.     CPARTARG_UNDERLINED,
  125.     CPARTARG_TEXT,
  126.     CPARTARG_MAX
  127. };
  128.  
  129. enum
  130. {
  131.     GLOBALARG_GLOBAL,
  132.     GLOBALARG_TABLENGTH,
  133. #ifdef DEBUG
  134.     GLOBALARG_DEBUG,
  135. #endif
  136. };
  137.  
  138. const STRPTR cparts[C_MAX] =
  139. {
  140.     "STANDARD",
  141.     "COMMENT",
  142.     "CPP",
  143.     "KEYWORD",
  144.     "STORAGE",
  145.     "TYPES",
  146.     "TYPENAME",
  147.     "STRING",
  148.     "NUMBER"
  149. };
  150.  
  151. const struct Style defstyle[C_MAX] =
  152. {
  153.     {1,0,FS_NORMAL             }, /* C_STANDARD */
  154.     {1,0,FSF_ITALIC            }, /* C_COMMENT */
  155.     {1,0,FSF_BOLD | FSF_ITALIC }, /* C_CPP */
  156.     {2,0,FSF_BOLD              }, /* C_KEYWORD */
  157.     {1,0,FSF_UNDERLINED        }, /* C_STORAGE */
  158.     {1,0,FSF_BOLD              }, /* C_TYPES */
  159.     {2,0,FS_NORMAL             }, /* C_TYPENAME */
  160.     {3,0,FS_NORMAL             }, /* C_STRING */
  161.     {3,0,FSF_BOLD              }  /* C_NUMBER */
  162. };
  163. /*FE*/
  164.  
  165. /* ------------------------------ init class ------------------------------ */
  166.  
  167. /*FS*/ LibCall Class *initClass(REGA6 struct ClassBase *cb)
  168. {
  169.     Class *cl;
  170.  
  171.     if((cl = MakeClass(DATATYPENAME,TEXTDTCLASS,NULL,sizeof(struct CData ),0)))
  172.     {
  173.         cl->cl_Dispatcher.h_Entry = (HOOKFUNC) dispatch;
  174.         cl->cl_UserData = (ULONG) cb;
  175.  
  176.         AddClass(cl);
  177.     }
  178.  
  179.     return(cl);
  180. }
  181. /*FE*/
  182.  
  183. /* ------------------------------ read prefs ------------------------------ */
  184.  
  185. /*FS*/ BOOL readPrefs(struct ClassBase *cb,struct CData *cd,STRPTR file)
  186. {
  187.     struct RDArgs *rdargs;
  188.     struct RDArgs *args;
  189.     BPTR fh;
  190.  
  191.     ENTERING;
  192.  
  193.     if((fh = Open(file,MODE_OLDFILE)))
  194.     {
  195.         UBYTE buf[256];
  196.         ULONG para[CPARTARG_MAX];
  197.         LONG i;
  198.  
  199.         DB(("def file %s opened\n",file));
  200.  
  201.         while(FGets(fh,buf,sizeof(buf)))
  202.         {
  203.             const STRPTR *tmplt = prefstemplate;
  204.             ULONG tmpltnr = 0;
  205.             DB(("line : %s",buf));
  206.  
  207.             while(*tmplt)
  208.             {
  209.                 if((rdargs = (struct RDArgs *) AllocDosObject(DOS_RDARGS,NULL)))
  210.                 {
  211.                     rdargs->RDA_Source.CS_Buffer   = buf;
  212.                     rdargs->RDA_Source.CS_Length   = strlen(buf);
  213.  
  214.                     for(i=0 ; i < (sizeof(para)/sizeof(LONG)) ; i++)
  215.                         para[i]=0;
  216.  
  217.                     DB(("rdargs at : %lx\n",rdargs));
  218.                     
  219.                     if((args = ReadArgs(*tmplt,(LONG *) para,rdargs)))
  220.                     {
  221.                         DB(("args at %lx\n",args));
  222.  
  223.                         switch(tmpltnr)
  224.                         {
  225.                         case TMPLT_CPART:
  226.                             for(i = C_MAX - 1; i >= 0 ; i--)
  227.                                 if(!Stricmp((STRPTR) para[CPARTARG_CSECTION],(STRPTR) cparts[i]))
  228.                                 {
  229.                                     struct Style *style = &cd->cd_CStyle[i];
  230.  
  231.                                     if(para[CPARTARG_TEXT])
  232.                                         style->Style = (UWORD) ~0;
  233.                                     else
  234.                                     {
  235.                                         if(para[CPARTARG_PEN])
  236.                                             style->FgPen = (UWORD) *((ULONG *) para[CPARTARG_PEN]);
  237.                                         else if(para[CPARTARG_RED] || para[CPARTARG_GREEN] || para[CPARTARG_BLUE])
  238.                                         {
  239.                                             struct PenNode *pen;
  240.                                             if((pen = AllocPooled(cd->cd_Pool,sizeof(struct PenNode))))
  241.                                             {
  242.                                                 pen->pn_Section = i;
  243.                                                 pen->pn_Pen     = -1;
  244.  
  245.                                                 if(para[CPARTARG_RED])
  246.                                                     pen->pn_Red   = (*((ULONG *) para[CPARTARG_RED]))   << 24;
  247.                                                 if(para[CPARTARG_GREEN])
  248.                                                     pen->pn_Green = (*((ULONG *) para[CPARTARG_GREEN])) << 24;
  249.                                                 if(para[CPARTARG_BLUE])
  250.                                                     pen->pn_Blue  = (*((ULONG *) para[CPARTARG_BLUE]))  << 24;
  251.  
  252.                                                 AddTail(&cd->cd_PenList,(struct Node *) pen);
  253.                                             }
  254.                                         }
  255.  
  256.                                         style->Style = FS_NORMAL;
  257.                                         if(para[CPARTARG_ITALIC])
  258.                                             style->Style |= FSF_ITALIC;
  259.                                         if(para[CPARTARG_BOLD])
  260.                                             style->Style |= FSF_BOLD;
  261.                                         if(para[CPARTARG_UNDERLINED])
  262.                                             style->Style |= FSF_UNDERLINED;
  263.                                     }
  264.                                 }
  265.                             break;
  266.                         case TMPLT_GLOBAL:
  267.                             if(para[GLOBALARG_GLOBAL])
  268.                             {
  269.                                 if(para[GLOBALARG_TABLENGTH])
  270.                                     cd->cd_TabLength = *((ULONG *) para[GLOBALARG_TABLENGTH]);
  271.  
  272.                                 D({
  273.                                     if(para[GLOBALARG_DEBUG])
  274.                                         cdtparse_debug = 1;
  275.                                     bug("yydebug is : %ld\n",cdtparse_debug);
  276.                                   });
  277.                             }
  278.                             break;
  279.                         }
  280.                     }
  281.  
  282.                     FreeDosObject(DOS_RDARGS , rdargs);
  283.                 }
  284.                 tmpltnr++;
  285.                 tmplt++;
  286.             }
  287.         }
  288.         Close(fh);
  289.     }
  290.  
  291.     LEAVING;
  292.  
  293.     return((BOOL) (fh == NULL));
  294. }
  295. /*FE*/
  296.  
  297. /* ---------------------------- notify object ----------------------------- */
  298.  
  299. /*FS*/ ULONG notifyAttrChanges(Object * o, void * ginfo, ULONG flags, ULONG tag1,...)
  300. {
  301.      return(DoMethod(o, OM_NOTIFY, &tag1, ginfo, flags));
  302. }
  303. /*FE*/
  304.  
  305. /* --------------------------- class dispatcher --------------------------- */
  306.  
  307. /*FS*/ ClassCall ULONG dispatch(REGA0 Class *cl,REGA2 Object *obj,REGA1 Msg msg)
  308. {
  309.     struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
  310.     struct CData *cd = INST_DATA(cl,obj);
  311.     ULONG retval;
  312.  
  313.     switch(msg->MethodID)
  314.     {
  315.     case OM_NEW:
  316.         {
  317.             Object *newobj;
  318.             if((newobj = (Object *) DoSuperMethodA(cl,obj,msg)))
  319.             {
  320.                 cd = INST_DATA(cl,newobj);
  321.  
  322.                 NewList(&cd->cd_PenList);
  323.                 cd->cd_TabLength = 8;
  324.  
  325.                 if((cd->cd_Pool = CreatePool(MEMF_CLEAR | MEMF_ANY , PUDDLE_SIZE,PUDDLE_SIZE)))
  326.                 {
  327.                     STRPTR buffer = NULL;
  328.                     ULONG bufferlen = 0;
  329.  
  330.                     if(GetDTAttrs(newobj,TDTA_Buffer    ,&buffer,
  331.                                                 TDTA_BufferLen ,&bufferlen,
  332.                                                 TAG_DONE) == 2 && buffer && bufferlen)
  333.                     {
  334.                         int i;
  335.  
  336.                         for(i = 0 ; i < C_MAX ; i++)
  337.                             cd->cd_CStyle[i] = defstyle[i];
  338.  
  339.                         if(readPrefs(cb,cd,"PROGDIR:Prefs/DataTypes/c.prefs"))
  340.                             readPrefs(cb,cd,"Env:DataTypes/c.prefs");
  341.  
  342.                         retval = (ULONG) newobj;
  343.                     } else
  344.                         SetIoErr(ERROR_REQUIRED_ARG_MISSING);
  345.                 }
  346.             }
  347.  
  348.             if(!retval)
  349.             {
  350.                 D(bug("c.datatype error : %ld\n",IoErr()));
  351.                 CoerceMethod(cl,(Object *) retval,OM_DISPOSE);
  352.             }
  353.         }
  354.         break;
  355.     case OM_DISPOSE:
  356.         {
  357.             struct List *linelist;
  358.             struct PenNode *pen;
  359.  
  360.             if(GetDTAttrs(obj,TDTA_LineList,&linelist,TAG_DONE) && linelist)
  361.                 NewList(linelist);
  362.  
  363.             while((pen = (struct PenNode *) RemHead(&cd->cd_PenList)))
  364.                 if(pen->pn_Pen != -1)
  365.                     ReleasePen(cd->cd_ColorMap,pen->pn_Pen);
  366.  
  367.             if(cd->cd_Pool)
  368.                 DeletePool(cd->cd_Pool);
  369.  
  370.             retval = DoSuperMethodA(cl,obj,msg);
  371.         }
  372.         break;
  373.     case OM_SET:
  374.     case OM_UPDATE:
  375.         /* Pass the attributes to the super class and force a refresh
  376.          * if we need it */
  377.         if((retval = DoSuperMethodA (cl, obj, msg)) && (OCLASS (obj) == cl))
  378.         {
  379.             struct RastPort *rp;
  380.  
  381.             /* Get a pointer to the rastport */
  382.             if((rp = ObtainGIRPort (((struct opSet *) msg)->ops_GInfo)))
  383.             {
  384.                 struct gpRender gpr;
  385.  
  386.                 /* Force a redraw */
  387.                 gpr.MethodID   = GM_RENDER;
  388.                 gpr.gpr_GInfo  = ((struct opSet *) msg)->ops_GInfo;
  389.                 gpr.gpr_RPort  = rp;
  390.                 gpr.gpr_Redraw = GREDRAW_UPDATE;
  391.                 DoMethodA (obj, (Msg) &gpr);
  392.  
  393.                 /* Release the temporary rastport */
  394.                 ReleaseGIRPort (rp);
  395.             }
  396.         }
  397.         break;
  398.     case GM_LAYOUT:
  399.          /* Tell everyone that we are busy doing things */
  400.          notifyAttrChanges (obj, ((struct gpLayout *) msg)->gpl_GInfo, NULL,
  401.                                   GA_ID,       G(obj)->GadgetID,
  402.                                   DTA_Busy,    TRUE,
  403.                                   TAG_DONE);
  404.         /* Let the super-class partake */
  405.         retval = DoSuperMethodA (cl, obj, msg);
  406.  
  407.         /* We need to do this one asynchronously */
  408.         retval += DoAsyncLayout (obj, (struct gpLayout *) msg);
  409.         break;
  410.     case DTM_PROCLAYOUT:
  411.         /* Tell everyone that we are busy doing things */
  412.         notifyAttrChanges (obj, ((struct gpLayout *) msg)->gpl_GInfo, NULL,
  413.                                  GA_ID,       G(obj)->GadgetID,
  414.                                  DTA_Busy,    TRUE,
  415.                                  TAG_DONE);
  416.  
  417.         /* Let the super-class partake and then fall through to our layout method */
  418.         DoSuperMethodA (cl, obj, msg);
  419.     case DTM_ASYNCLAYOUT:
  420.         /* Layout the text */
  421.         retval = layout(cb, cl, obj, (struct gpLayout *) msg);
  422.         break;
  423.     default:
  424.         retval = DoSuperMethodA(cl,obj,msg);
  425.     }
  426.  
  427.     return(retval);
  428. }
  429. /*FE*/
  430.  
  431. /*FS*/ GetA4 ULONG layout(struct ClassBase *cb, Class * cl, Object * obj, struct gpLayout * gpl)
  432. {
  433.     struct DTSpecialInfo *si = (struct DTSpecialInfo *) G(obj)->SpecialInfo;
  434.     struct CData *cd = INST_DATA(cl,obj);
  435.  
  436.     struct CParse cparse;
  437.     struct RastPort trp;
  438.  
  439.     ULONG visible;
  440.     ULONG hunit;
  441.  
  442.     ULONG total = 0;
  443.     ULONG bsig = 0;
  444.  
  445.     /* Attributes obtained from super-class */
  446.     struct TextAttr *tattr;
  447.     struct TextFont *font;
  448.     struct List *linelist;
  449.     ULONG bufferlen;
  450.     STRPTR buffer;
  451.  
  452.     struct IBox *domain;
  453.     STRPTR title;
  454.  
  455.     D(bug("layout !\n"));
  456.  
  457.     /* Get all the attributes that we are going to need for a successful layout */
  458.     if(GetDTAttrs(obj,DTA_TextFont,  (ULONG) &font,
  459.                             DTA_TextAttr,  (ULONG) &tattr,
  460.                             DTA_Domain,    (ULONG) &domain,
  461.                             DTA_ObjName,   (ULONG) &title,
  462.                             TDTA_LineList, (ULONG) &linelist,
  463.                             TDTA_Buffer,   (ULONG) &buffer,
  464.                             TDTA_BufferLen,(ULONG) &bufferlen,
  465.                             TAG_DONE) == 7)
  466.     {
  467.         ULONG maxwidth = 0;
  468.  
  469.         /* Lock the global object data so that nobody else can manipulate it */
  470.         ObtainSemaphore (&(si->si_Lock));
  471.  
  472.         /* Make sure we have a buffer */
  473.         if(buffer)
  474.         {
  475.             /* Initialize the temporary RastPort */
  476.             InitRastPort (&trp);
  477.             SetFont (&trp, font);
  478.  
  479.             /* We only need to perform layout if we are doing word wrap, or this
  480.              * is the initial layout call */
  481.             if (gpl->gpl_Initial)
  482.             {
  483.                 struct PenNode *pen;
  484.  
  485.                 if((cd->cd_ColorMap = gpl->gpl_GInfo->gi_Screen->ViewPort.ColorMap))
  486.                 {
  487.                     for(pen = (struct PenNode *) cd->cd_PenList.lh_Head ;
  488.                          pen->pn_Node.mln_Succ ;
  489.                          pen = (struct PenNode *) pen->pn_Node.mln_Succ)
  490.                     {
  491.                         pen->pn_Pen = ObtainBestPen(cd->cd_ColorMap,
  492.                                                              pen->pn_Red,pen->pn_Green,pen->pn_Blue,
  493.                                                              OBP_Precision , PRECISION_ICON,
  494.                                                              TAG_DONE);
  495.                         DB(("pen : %ld allocated for section %ld with RGB : %lx %lx %lx\n",
  496.                              pen->pn_Pen,pen->pn_Section,pen->pn_Red,pen->pn_Green,pen->pn_Blue));
  497.  
  498.                         if(pen->pn_Pen != -1)
  499.                             cd->cd_CStyle[pen->pn_Section].FgPen = pen->pn_Pen;
  500.                     }
  501.                 }
  502.  
  503.                 cparse.BegPtr   = buffer;
  504.                 cparse.ActPtr   = buffer;
  505.                 cparse.TxtPtr   = buffer;
  506.                 cparse.SegPtr   = buffer;
  507.                 cparse.EndPtr   = buffer + bufferlen;
  508.                 cparse.TabWidth = TextLength(&trp," ",1);
  509.                 cparse.Mode     = C_STANDARD;
  510.  
  511.                 cparse.XOffset  = 0;
  512.                 cparse.YOffset  = 0;
  513.                 cparse.MaxWidth = 0;
  514.  
  515.                 cparse.LineList = linelist;
  516.                 cparse.RPort    = &trp;
  517.                 cparse.Data     = cd;
  518.  
  519.                 D({ BPTR fh;
  520.                      BPTR oldfh;
  521.                      if(cdtparse_debug)
  522.                      if((fh = Open("CON:////C-DataType YYDebug/WAIT/CLOSE",MODE_NEWFILE)))
  523.                          oldfh = SelectOutput(fh);
  524.                  );
  525.  
  526.                 cdtparse_parse(cb,&cparse);
  527.  
  528.                 cdtparse_free(cb,&cparse);
  529.  
  530.                 D(  if(fh && cdtparse_debug)
  531.                      {
  532.                         SelectOutput(oldfh);
  533.                         Close(fh);
  534.                      }
  535.                   }
  536.                  );
  537.  
  538.                 total    = cparse.YOffset / font->tf_YSize;
  539.                 maxwidth = cparse.MaxWidth;
  540.             }
  541.             else
  542.             {
  543.                 /* No layout to perform */
  544.                 total    = si->si_TotVert;
  545.                 maxwidth = si->si_TotHoriz;
  546.             }
  547.         }
  548.  
  549.         /* Compute the lines and columns type information */
  550.         si->si_VertUnit  = font->tf_YSize;
  551.         si->si_VisVert   = visible = domain->Height / si->si_VertUnit;
  552.         si->si_TotVert   = total;
  553.  
  554.         si->si_HorizUnit = hunit = 1;
  555.         si->si_VisHoriz  = (LONG) domain->Width / hunit;
  556.         si->si_TotHoriz  = maxwidth;
  557.  
  558.         /* Release the global data lock */
  559.         ReleaseSemaphore(&si->si_Lock);
  560.  
  561.         /* Were we aborted? */
  562.         if (bsig == 0)
  563.         {
  564.             /* Not aborted, so tell the world of our newest attributes */
  565.             notifyAttrChanges (obj, gpl->gpl_GInfo, NULL,
  566.                                      GA_ID,                   G(obj)->GadgetID,
  567.  
  568.                                      DTA_VisibleVert,         visible,
  569.                                      DTA_TotalVert,           total,
  570.                                      DTA_NominalVert,         font->tf_YSize * 25,
  571.                                      DTA_VertUnit,            font->tf_YSize,
  572.  
  573.                                      DTA_VisibleHoriz,        (ULONG) (domain->Width / hunit),
  574.                                      DTA_TotalHoriz,          maxwidth,
  575.                                      DTA_NominalHoriz,        font->tf_XSize * 80,
  576.                                      DTA_HorizUnit,           hunit,
  577.  
  578.                                      DTA_Title,               title,
  579.                                      DTA_Busy,                FALSE,
  580.                                      DTA_Sync,                TRUE,
  581.                                      TAG_DONE);
  582.         }
  583.     }
  584.  
  585.     return(total);
  586. }
  587. /*FE*/
  588.  
  589.