Open64 (mfef90, whirl2f, and IR tools)  TAG: version-openad; SVN changeset: 916
DaVinci.cxx
Go to the documentation of this file.
00001 /*
00002 
00003   Copyright (C) 2000, 2001 Silicon Graphics, Inc.  All Rights Reserved.
00004 
00005   This program is free software; you can redistribute it and/or modify it
00006   under the terms of version 2 of the GNU General Public License as
00007   published by the Free Software Foundation.
00008 
00009   This program is distributed in the hope that it would be useful, but
00010   WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
00012 
00013   Further, this software is distributed without any warranty that it is
00014   free of the rightful claim of any third person regarding infringement 
00015   or the like.  Any license provided herein, whether implied or 
00016   otherwise, applies only to this software file.  Patent licenses, if 
00017   any, provided herein do not apply to combinations of this program with 
00018   other software, or any other product whatsoever.  
00019 
00020   You should have received a copy of the GNU General Public License along
00021   with this program; if not, write the Free Software Foundation, Inc., 59
00022   Temple Place - Suite 330, Boston MA 02111-1307, USA.
00023 
00024   Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pky,
00025   Mountain View, CA 94043, or:
00026 
00027   http://www.sgi.com
00028 
00029   For further information regarding this notice, see:
00030 
00031   http://oss.sgi.com/projects/GenInfo/NoticeExplan
00032 
00033 */
00034 
00035 
00036 /* -*-Mode: c++;-*- (Tell emacs to use c++ mode) */
00037 /* ====================================================================
00038  *
00039  *
00040  * Description:
00041  *      Interface to daVinci, a visualization system for displaying
00042  *      directed graphs.
00043  *
00044  * ====================================================================
00045  * ====================================================================
00046  */
00047 
00048 #include <sys/types.h>              // for pid_t
00049 #include <sys/wait.h>               // for waitpid()
00050 #include <unistd.h>                 // for fork(), pipe(), etc.
00051 #include <signal.h>                 // for SIGINT
00052 #include <stdarg.h>                 // for varargs.
00053 #include <time.h>
00054 #include <string.h>
00055 #include <errno.h>
00056 
00057 #include "DaVinci.h"
00058 
00059 #define MAX_MENU_LABEL_LEN  100
00060 
00061 #define CALLBACK_DEBUG
00062 
00063 // ------------------------------------------------------------------------
00064 //         CALLBACK HOOKS -- default handlers.
00065 // ------------------------------------------------------------------------
00066 
00067 void
00068 DaVinci_Callback::Node_Select(const INT n_ids, const NODE_ID id_array[])
00069 {
00070 #ifdef CALLBACK_DEBUG
00071   fprintf(stderr, "Node_Select([");
00072   char *sep = " ";
00073   for (INT i = 0; i < n_ids; ++i) {
00074     fprintf(stderr, "%s%p", sep, id_array[i]);
00075     sep = ", ";
00076   }
00077   fprintf(stderr, " ])\n");
00078 #endif
00079 }
00080 
00081 void
00082 DaVinci_Callback::Edge_Select(const EDGE_ID& id)
00083 {
00084 #ifdef CALLBACK_DEBUG
00085   fprintf(stderr, "Edge_Select(%p,%p)\n", id.src, id.dst);
00086 #endif
00087 }
00088 
00089 void
00090 DaVinci_Callback::Menu_Select(const char *menu_id)
00091 {
00092 #ifdef CALLBACK_DEBUG
00093   fprintf(stderr, "Menu_Select(%s)\n", menu_id);
00094 #endif
00095 }
00096 
00097 // ------------------------------------------------------------------------
00098 //         NODE/EDGE TYPE support.
00099 // ------------------------------------------------------------------------
00100 
00101 NODE_TYPE&
00102 NODE_TYPE::Name(const char *typ_name)
00103 {
00104   if ( typ_name ) {
00105     strncpy(_type_name, typ_name, sizeof(_type_name) - 1);
00106   }
00107   // more? handle arb. long typ_name.
00108   // more: DaVinci 2.1? use node type database.
00109   return *this;
00110 }
00111 
00112 NODE_TYPE&
00113 NODE_TYPE::Color(const char *rgb)
00114 {
00115   strncpy(_node_color, rgb, sizeof(_node_color) - 1);
00116   // more: handle long color names; check color validity.
00117   return *this;
00118 }
00119 
00120 EDGE_TYPE&
00121 EDGE_TYPE::Name(const char *typ_name)
00122 {
00123   if ( typ_name ) {
00124     strncpy(_type_name, typ_name, sizeof(_type_name) - 1);
00125   }
00126   // more? handle arb. long typ_name.
00127   // more: DaVinci 2.1? use node type database.
00128   return *this;
00129 }
00130 
00131 
00132 EDGE_TYPE&
00133 EDGE_TYPE::Color(const char *rgb)
00134 {
00135   strncpy(_edge_color, rgb, sizeof(_edge_color) - 1);
00136   // more: handle long color names; check color validity.
00137   return *this;
00138 }
00139 
00140 // ------------------------------------------------------------------------
00141 //        Menu_info support -- status (& string pool)
00142 // ------------------------------------------------------------------------
00143 
00144 
00145 const char *
00146 Menu_info::Add(const char *cp)
00147 {
00148   Item_info::iterator it = items.find( cp );
00149 
00150   if ( cp == NULL ) return NULL;
00151 
00152   if ( it != items.end() ) {
00153     return (*it).first;
00154   } else {
00155     char       *buf = CXX_NEW_ARRAY(char, strlen(cp)+1, _m);
00156     const char *cpy = strcpy( buf, cp );
00157     items[ cpy ] = DM_UNKNOWN;
00158     return cpy;
00159   }
00160 }
00161 
00162 void
00163 Menu_info::Set(const char *cp, Item_status status)
00164 {
00165   if ( cp ) {
00166     if ( items.find( cp ) == items.end() ) {
00167       char *buf = CXX_NEW_ARRAY(char, strlen(cp)+1, _m);
00168       cp = strcpy( buf, cp );
00169     }
00170     items[ cp ] = status;
00171   }
00172 }
00173 
00174 // ------------------------------------------------------------------------
00175 //         DaVinci::IO  low level IO to/from DaVinci
00176 // ------------------------------------------------------------------------
00177 
00178 DaVinci::IO::~IO()
00179 {
00180     Close ();
00181 }
00182 
00183 void
00184 DaVinci::IO::Close()
00185 {
00186   if ( _to_fp )   (void)fclose( _to_fp   );
00187   if ( _from_fp ) (void)fclose( _from_fp );
00188 }
00189 
00190 void
00191 DaVinci::IO::Out_Fmt(const char *fmt, ...)
00192 {
00193   if ( _to_fp == NULL ) {
00194     fprintf(stderr, "DaVinci::IO::Out_Fmt _to_fp not set!\n");
00195     return;
00196   }
00197   va_list ap;
00198   va_start(ap, fmt);
00199 
00200   vfprintf(_to_fp, fmt, ap);  // send to DaVinci.
00201 
00202   if ( _trace_fp ) {
00203     if ( ! _trace_tagged ) {
00204       fprintf(_trace_fp, "TO-DAVINCI: ");
00205       _trace_tagged = true;
00206     }
00207     vfprintf(_trace_fp, fmt, ap);
00208 
00209     if ( strchr( fmt, '\n' ) ) {  // assume linefeeds only in fmt.
00210       _trace_tagged = false;
00211     }
00212     fflush(_trace_fp);
00213   }
00214 
00215   va_end(ap);
00216 }
00217 
00218 char *
00219 DaVinci::IO::In_Line()
00220 {
00221   static char buf[10000];   //more: deal with arb. long lines ..
00222 
00223   if ( _from_fp == NULL ) {
00224     fprintf(stderr, "DaVinci::IO::Out_Fmt _from_fp not set!\n");
00225     return NULL;
00226   }
00227   char *rp = fgets( buf, sizeof(buf), _from_fp );
00228 
00229   if ( rp ) {
00230     char *np = strchr( rp, '\n' );
00231     if ( np ) {
00232       *np = '\0';
00233     } else {
00234       fprintf(stderr, "in_line truncation! (%.50s ..)\n", buf);
00235     }
00236     if (strlen(rp) >= sizeof(buf) ) {
00237       fprintf(stderr, "INTERNAL ERROR! DaVinci::IO:in_line buf overflow\n");
00238       abort();
00239     }
00240   }
00241   if ( _trace_fp && rp ) {
00242     fprintf(_trace_fp, "FROM-DAVINCI: %s\n", buf);
00243     fflush(_trace_fp);
00244 
00245     _trace_tagged = false;  // read whole lines from DaVinci.
00246   }
00247   return rp;
00248 }
00249 
00250 // ------------------------------------------------------------------------
00251 //         DaVinci private members.
00252 // ------------------------------------------------------------------------
00253 
00254 #define FT_DAVINCI           FTAG(1 <<  0)
00255 #define FT_TITLE             FTAG(1 <<  1)
00256 #define FT_SHOW_STATUS       FTAG(1 <<  2)
00257 #define FT_SHOW_MESSAGE      FTAG(1 <<  3)
00258 #define FT_MENU_CREATE       FTAG(1 <<  4)
00259 #define FT_MENU_ACTIVATE     FTAG(1 <<  5)
00260 #define FT_MENU_DEACTIVATE   FTAG(1 <<  6)
00261 #define FT_GRAPH_BEGIN       FTAG(1 <<  7)
00262 #define FT_NODE_BEGIN        FTAG(1 <<  8)
00263 #define FT_OUT_EDGE          FTAG(1 <<  9)
00264 #define FT_NODE_END          FTAG(1 << 10)
00265 #define FT_GRAPH_END         FTAG(1 << 11)
00266 #define FT_CHANGE_ATTR       FTAG(1 << 12)
00267 #define FT_UPDATE_BEGIN      FTAG(1 << 13)
00268 #define FT_NEW_NODE          FTAG(1 << 14)
00269 #define FT_NEW_EDGE          FTAG(1 << 15)
00270 #define FT_UPDATE_END        FTAG(1 << 16)
00271 
00272 #define BASE_SET ( \
00273    FT_DAVINCI | FT_TITLE | FT_SHOW_STATUS | FT_SHOW_MESSAGE  \
00274  | FT_MENU_CREATE | FT_MENU_ACTIVATE | FT_MENU_DEACTIVATE    \
00275  | FT_GRAPH_END | FT_CHANGE_ATTR | FT_UPDATE_END             \
00276  )
00277 
00278 const char *
00279 DaVinci::Ft_Str(const FTAG ftag)
00280 {
00281   char *s = "<<ft_str: unknown tag>>";
00282 
00283   switch ( ftag ) {
00284   case FT_DAVINCI:          s = "DaVinci";           break;
00285   case FT_TITLE:            s = "title";             break;
00286   case FT_SHOW_STATUS:      s = "show_status";       break;
00287   case FT_SHOW_MESSAGE:     s = "show_message";      break;
00288   case FT_MENU_CREATE:      s = "menu_create";       break;
00289   case FT_MENU_ACTIVATE:    s = "menu_activate";     break;
00290   case FT_MENU_DEACTIVATE:  s = "menu_deactivate";   break;
00291   case FT_GRAPH_BEGIN:      s = "graph_begin";       break;
00292   case FT_NODE_BEGIN:       s = "node_begin";        break;
00293   case FT_OUT_EDGE:         s = "out_edge";          break;
00294   case FT_NODE_END:         s = "node_end";          break;
00295   case FT_GRAPH_END:        s = "graph_end";         break;
00296   case FT_CHANGE_ATTR:      s = "change_attr";       break;
00297   case FT_UPDATE_BEGIN:     s = "update_begin";      break;
00298   case FT_NEW_NODE:         s = "new_node";          break;
00299   case FT_NEW_EDGE:         s = "new_edge";          break;
00300   case FT_UPDATE_END:       s = "update_end";        break;
00301   default:
00302     ; // unknown tag.
00303   }
00304   return s;
00305 }
00306 
00307 void
00308 DaVinci::Usage_Error(FTAG curr, FTAGS prereq)
00309 {
00310   fprintf(stderr, "Error while Calling DaVinci::%s - ", Ft_Str(curr));
00311 
00312   if ( ! _display_ok ) {
00313     fprintf(stderr, "DaVinci display not ok\n");
00314   } else {
00315     fprintf(stderr, "preceeding %s expected member of {", Ft_Str(_ftag_last));
00316     FTAGS fts   = prereq;
00317     char *comma = "";
00318     for (FTAGS ft = FT_DAVINCI; fts != 0; ft <<= 1) {
00319       if ( ft & fts ) {
00320         fprintf(stderr, "%s %s", comma, Ft_Str(ft));
00321         comma = ",";
00322         fts ^= ft;
00323       }
00324     }
00325     fprintf(stderr, " }\n");
00326   }
00327 }
00328 
00329 DA_ACK
00330 DaVinci::Wait_For_Ack()
00331 {
00332   EVENT_T  event;
00333   char    *line;
00334 
00335   // NOTE: never expect to queue an OK or COM_ERROR because Wait_For_Ack()
00336   //       is called at the end of each command to DaVinci.  Therefore
00337   //       must look for OK/COM_ERROR in the unparsed input stream.  Any
00338   //       other events from davinci are queued for processing in event_loop().
00339   //       If this assumption is proven invalid will need to alter protocol.
00340   //       In particular, should keep a side queue for OK/COM_ERROR msgs ..
00341 
00342   while ( (line = _io.In_Line()) != NULL ) {
00343     if ( Parse_Event( line, &event ) ) {
00344       switch ( event.kind ) {
00345       case EK_OK:
00346         return NULL;
00347       case EK_COM_ERROR:
00348         return event.u.com_error.msg;
00349       default:
00350         _event_q.push( event );
00351       }
00352     }
00353   }
00354   _display_ok = false;
00355 
00356   return "Unexpected EOF from DaVinci";
00357 }
00358 
00359 bool
00360 DaVinci::Parse_Node_Ids(const char *epfx, INT *n_nodes, NODE_ID **node_ids)
00361 {
00362   INT         n_alloc = 5;
00363   NODE_ID    *ids     = CXX_NEW_ARRAY(NODE_ID, n_alloc, _m);
00364   INT         cnt     = 0;
00365   const char *cp      = epfx;
00366   const char *sp;
00367   NODE_ID     id;
00368 
00369   // To avoid warnings, get id lists from the MEM_POOL whence other
00370   // DaVinci storage comes.  Since the amount of storage required
00371   // will generally be small, don't bother attempting to reuse/reclaim
00372   // before the pool is popped or deleted.
00373 
00374   if ( cp[0] != '(' || cp[1] != '[' ) {        // (["id","id"..])
00375     fprintf(stderr, "BAD NODE_ID list (lp): %s\n", epfx);
00376     return false;
00377   }
00378   cp += 2;
00379 
00380   while ( *cp != ']' ) {
00381     sp = strchr( cp, ',' );
00382     if ( sp == NULL ) {
00383       sp = strchr( cp, ']' );
00384       if ( sp == NULL ) {
00385         fprintf(stderr, "BAD NODE_ID list (sep): %s\n", epfx);
00386         return false;
00387       }
00388     }
00389     if ( sscanf( cp, "\"%p\"", &id ) != 1 ) {
00390       fprintf(stderr, "BAD NODE_ID (id): .. %s\n", cp);
00391       return false;
00392     }
00393     if ( cnt >= n_alloc ) {    // more: improve storage management.
00394       n_alloc = cnt + 10;
00395       NODE_ID *nids = CXX_NEW_ARRAY(NODE_ID, n_alloc, _m);
00396       for (INT i = 0; i < cnt; ++i) {
00397         nids[i] = ids[i];
00398       }
00399       ids = nids;
00400     }
00401     ids[ cnt++ ] = id;
00402 
00403     cp = ( *sp == ',' ? sp + 1 : sp );
00404   }
00405   *n_nodes  = cnt;
00406   *node_ids = ids;
00407 
00408   return true;
00409 }
00410 
00411 bool
00412 Parse_Edge_Id( const char *epfx, EVENT_T *event ) // ("node_id:node_id")
00413 {
00414   if ( sscanf(epfx, "(\"%p:%p\")",
00415               &event->u.sel_edge.edge_src,
00416               &event->u.sel_edge.edge_dst) != 2 ) {
00417     fprintf(stderr, "Malformed EDGE_ID %s\n", epfx);
00418     return false;
00419   }
00420   return true;
00421 }
00422 
00423 const char *
00424 DaVinci::Parse_Menu_Label( const char *epfx ) // ("label")
00425 {
00426   char  label[MAX_MENU_LABEL_LEN];
00427   INT   len   = strlen( epfx );
00428 
00429   if ( epfx[0] != '('
00430        || epfx[1] != '"'
00431        || epfx[len - 2] != '"'
00432        || epfx[len - 1] != ')' ) {
00433     fprintf(stderr, "parse_menu_label: not wrapped as expected\n");
00434     return NULL;
00435   }
00436   strncpy( label, epfx + 2, len - 4 );
00437   label[ len - 4 ] = '\0';
00438 
00439   // more: would be nice to have a string pool that accepts substrings
00440   //       w/o needing to copy before testing for membership in pool.
00441 
00442   return  _menu_state.Add( label );  // use as string pool.
00443 }
00444 
00445 static struct {
00446   const char *name;     // order by name - for binary search.
00447   EVENT_KIND  kind;
00448 } Event_Tbl[] = {
00449   { "communication_error",     EK_COM_ERROR },
00450   { "edge_selection_label",    EK_SEL_EDGE  },
00451   { "menu_selection",          EK_SEL_MENU  },
00452   { "node_selections_labels",  EK_SEL_NODES },
00453   { "ok",                      EK_OK        },
00454   { "quit",                    EK_QUIT      }
00455 };
00456 #define N_EVENT   ( sizeof(Event_Tbl) / sizeof(Event_Tbl[0]) )
00457 
00458 bool
00459 DaVinci::Parse_Event(const char *line, EVENT_T *event)
00460 {
00461   char *epfx;  // immediately after '(' or at end of reply.
00462 
00463   epfx = (char *)strchr( line, '(' );
00464   if ( epfx == NULL ) {
00465     epfx = (char *)strchr( line, '\0' );
00466   }
00467   INT nchr = epfx - line;
00468   INT lo   = 0;
00469   INT hi   = N_EVENT - 1;
00470   INT mid;
00471   INT cmp;
00472 
00473   while ( lo <= hi ) {
00474     mid = ( lo + hi ) / 2;
00475     cmp = strncmp( Event_Tbl[ mid ].name, line, nchr );
00476     if ( cmp == 0 ) break;
00477     if ( cmp < 0 ) {
00478       lo = mid + 1;
00479     } else {
00480       hi = mid - 1;
00481     } 
00482   }
00483   if ( cmp != 0 ) {
00484 #ifndef REPORT_FONT_WARNINGS
00485     if ( strncmp(line, "Font ", 5) == 0 ) {
00486       return false;
00487     }
00488 #endif
00489     fprintf(stderr, "DaVinci::Parse_Event UNKNOWN: %s\n", line);
00490     return false;
00491   }
00492   event->kind = Event_Tbl[ mid ].kind;
00493 
00494   switch ( event->kind ) {
00495   case EK_COM_ERROR:
00496     event->u.com_error.msg = line;  // more: better storage management.
00497     break;
00498   case EK_QUIT:
00499   case EK_OK:
00500     // no add'l data.
00501     break;
00502   case EK_SEL_EDGE:
00503     if ( ! Parse_Edge_Id( epfx, event ) ) {
00504       return false;
00505     }
00506     break;
00507   case EK_SEL_MENU:
00508     {
00509       const char *ss = Parse_Menu_Label( epfx );
00510       if ( ss == NULL ) return false;
00511       event->u.sel_menu.label = ss;
00512     }
00513     break;
00514   case EK_SEL_NODES:
00515     if ( ! Parse_Node_Ids( epfx, &event->u.sel_nodes.n_nodes,
00516                                  &event->u.sel_nodes.node_ids ) ) {
00517       return false;
00518     }
00519     break;
00520   default:
00521     fprintf(stderr, "INTERNAL ERROR: missing event case %d\n", event->kind);
00522     return false;
00523   }
00524   return true;
00525 }
00526 
00527 void
00528 DaVinci::Emit_Menu(INT n_items, const MENU_INFO *items)
00529 {
00530   for (INT i = 0; i < n_items; ++i) {
00531     if ( items[i].subitems && items[i].n_subitems > 0 ) {
00532       _io.Out_Fmt( "submenu_entry(\"%s\", \"%s\",[",
00533                    items[i].id, items[i].label);
00534       Emit_Menu( items[i].n_subitems, items[i].subitems );
00535       _io.Out_Fmt( "])" );
00536     } else {
00537       _io.Out_Fmt( "menu_entry(\"%s\", \"%s\")",
00538                    items[i].id, items[i].label);
00539     }
00540     if ( i < n_items - 1 ) _io.Out_Fmt( "," );
00541 
00542     _menu_state.Set( items[i].id,
00543                      (items[i].initially_active ? DM_ACTIVE : DM_INACTIVE));
00544   }
00545 }
00546 
00547 void
00548 DaVinci::Emit_Attr(const NODE_TYPE& nt, char **comma)
00549 {
00550   char *val = NULL;
00551 
00552   if ( nt._node_color[0] != '\0' ) {
00553     _io.Out_Fmt( ",a(\"COLOR\",\"%s\")", nt._node_color);
00554   }
00555 
00556   switch ( nt._node_shape ) {
00557   case NS_UNSET:    val = NULL;      break;
00558   case NS_BOX:      val = "box";     break;
00559   case NS_CIRCLE:   val = "circle";  break;
00560   case NS_ELLIPSE:  val = "ellipse"; break;
00561   case NS_RHOMBUS:  val = "rhombus"; break;
00562   case NS_TEXT:     val = "text";    break;
00563   default:
00564     fprintf(stderr, "DaVinci::emit_attr/node unexpected shape %d\n",
00565             nt._node_shape);
00566   }
00567   if ( val) {
00568     _io.Out_Fmt( "%sa(\"_GO\",\"%s\")", *comma, val);
00569     *comma = ",";
00570   }
00571 
00572   switch ( nt._border ) {
00573   case NB_UNSET:  val = NULL;      break;
00574   case NB_SINGLE: val = "single";  break;
00575   case NB_DOUBLE: val = "double";  break;
00576   default:
00577     fprintf(stderr, "DaVinci:emit_attr/node unexpected border type %d\n",
00578             nt._border);
00579   }
00580   if ( val ) {
00581     _io.Out_Fmt( "%sa(\"BORDER\",\"%s\")", *comma, val);
00582     *comma = ",";
00583   }
00584 
00585   switch ( nt._hide ) {
00586   case NH_UNSET: val = NULL;     break;
00587   case NH_HIDE:  val = "true";   break;
00588   case NH_SHOW:  val = "false";  break;
00589   default:
00590     fprintf(stderr, "DaVinci:emit_attr/node unexpected hide/show value %d\n",
00591             nt._hide);
00592   }
00593   if ( val ) {
00594     _io.Out_Fmt( "%sa(\"HIDDEN\",\"%s\")", *comma, val);
00595     *comma = ",";
00596   }
00597 }
00598 
00599 void
00600 DaVinci::Emit_Attr(const EDGE_TYPE& et)
00601 {
00602 
00603   char *val   = NULL;
00604   char *comma = "";
00605 
00606   if ( et._edge_color[0] != '\0' ) {
00607     _io.Out_Fmt( "a(\"EDGECOLOR\",\"%s\")", et._edge_color );
00608     comma = ",";
00609   }
00610 
00611   switch ( et._edge_pattern ) {
00612   case EP_UNSET:   val = NULL;      break;
00613   case EP_SOLID:   val = "solid";   break;
00614   case EP_DOTTED:  val = "dotted";  break;
00615   case EP_DASHED:  val = "dashed";  break;
00616   case EP_THICK:   val = "thick";   break;
00617   case EP_DOUBLE:  val = "double";  break;
00618   default:
00619     fprintf(stderr, "DaVinci::emit_attr/edge unexpected edge pattern %d\n",
00620             et._edge_pattern);
00621   }
00622   if ( val ) {
00623     _io.Out_Fmt( "%sa(\"EDGEPATTERN\",\"%s\")", comma, val );
00624     comma = ",";
00625   }
00626 
00627   switch ( et._edge_dir ) {
00628   case ED_UNSET:    val = NULL;       break;
00629   case ED_NORMAL:   val = "normal";   break;
00630   case ED_INVERSE:  val = "inverse";  break;
00631   case ED_BOTH:     val = "both";     break;
00632   case ED_NONE:     val = "none";     break;
00633   }
00634   if ( val ) {
00635     _io.Out_Fmt( "%sa(\"_DIR\",\"%s\")", comma, val );
00636     comma = ",";
00637   }
00638 }
00639 
00640 void
00641 DaVinci::Menu_Basic_Do( const char *label )
00642 {
00643   if ( strcmp( label, "exit_event_loop" ) == 0 ) {
00644     Exit_Event_Loop();
00645   }
00646 }
00647 
00648 DA_ACK
00649 DaVinci::Menu_Set_Active()
00650 {
00651   bool first = true;
00652 
00653   _io.Out_Fmt( "app_menu(activate_menus([" );
00654   for (Item_info::iterator m_iter = _menu_state.items.begin();
00655        m_iter != _menu_state.items.end(); ++m_iter) {
00656     if ( (*m_iter).second == DM_ACTIVE) {
00657       const char *menu_id = (*m_iter).first;
00658       _io.Out_Fmt( "%s\"%s\"", (first ? "" : ","), menu_id );
00659       first = false;
00660     }
00661   }
00662   _io.Out_Fmt( "]))\n" );
00663 
00664   return Wait_For_Ack();
00665 }
00666 
00667 void
00668 DaVinci::Kill_Davinci()
00669 {
00670     INT stat;
00671     
00672     _display_ok = false;
00673     kill (_pid, SIGINT);
00674     waitpid (_pid, &stat, WNOHANG);  // capture any SIGCHLD so not to
00675                                     // confuse master.
00676     _io.Close();
00677 }
00678 
00679 // ------------------------------------------------------------------------
00680 //         DaVinci public members.
00681 // ------------------------------------------------------------------------
00682 
00683 DaVinci::DaVinci(MEM_POOL *m, FILE *_trace_fp, bool usage_check) :
00684   _menu_state(m)
00685 {
00686   _m                = m;
00687   _basic_menu_added = false;
00688   _in_event_loop    = false;
00689   _display_ok       = false;
00690   _usage_check      = usage_check;  // debugging aid, off by default.
00691   _ftag_last        = FT_DAVINCI;
00692   _node_cnt         = 0;
00693   _edge_cnt         = 0;
00694 
00695   INT read_pipe[2];
00696   INT write_pipe[2];
00697 
00698   if ( pipe( read_pipe ) == -1 || pipe(write_pipe) == -1 ) {
00699     perror( "DaVinci" );
00700     return;
00701   }
00702   FILE *from_display = fdopen(read_pipe[0],  "r");
00703   FILE *to_display   = fdopen(write_pipe[1], "w");
00704   char *logfile      = getenv("DAVINCI_LOGFILE");
00705 
00706   setbuf(from_display, NULL);  // more: line buffer better ?
00707   setbuf(to_display,   NULL);
00708 
00709   switch ( _pid = fork() ) {
00710   case -1:                     // can't fork
00711     fprintf(stderr, "Unable to fork (for daVinci)\n");
00712     close (read_pipe[0]);
00713     close (read_pipe[1]);
00714     close (write_pipe[0]);
00715     close (write_pipe[1]);
00716     return;
00717   case 0:                       // child
00718     dup2 (write_pipe[0], 0);    // reset stdin, stdout, and stderr
00719     dup2 (read_pipe[1], 1);
00720     dup2 (read_pipe[1], 2);
00721     
00722     close (write_pipe[0]);      // close unused pipes.
00723     close (read_pipe[1]);       // leave the other pair opened so that
00724                                 // daVinci does not die when master exits
00725     if ( logfile ) {
00726       char fname[1000];
00727       // append time to avoid overwriting previous log file,
00728       // which would happen if daVinci is started more than
00729       // once in a session.
00730       sprintf(fname, "%s.%ld", logfile, time(NULL));
00731       execlp ("daVinci", "daVinci", "-pipe", "-log", fname, 0);
00732     } else {
00733       execlp ("daVinci", "daVinci", "-pipe", 0);
00734     }
00735     // error to stdout so it appears on pipe read by parent proc.
00736     // use message syntax that DaVinci::Parse_Event() recognizes.
00737     printf("communication_error(\"execlp of daVinci: %s %s\")\n",
00738            strerror(errno), "(define $DAVINCIHOME; need daVinci on $PATH)");
00739     exit (1);
00740 
00741   default:                      // parent
00742     close (read_pipe[1]);
00743     close (write_pipe[0]);
00744   }
00745   _io.Init( to_display, from_display );
00746   _io.Trace( _trace_fp );
00747 
00748   DA_ACK msg = Wait_For_Ack();
00749   if ( msg ) {
00750     fprintf(stderr, "DaVinci connection failed: %s\n", msg);
00751     return;
00752   }
00753   _display_ok = true;
00754 
00755   Emit_Do( "set(font_size(6))" );  // more? provide external control.
00756   Emit_Do( "set(gap_height(40))" );
00757   Emit_Do( "set(gap_width(20))" );
00758 }
00759 
00760 DaVinci::~DaVinci()
00761 {
00762   // more? wish to allow DaVinci to stay up.  is it possible close pipes.
00763 }
00764 
00765 // -- Limit Menu_basic to one level -- see menu_basic_do().
00766 //    The list is expected to be very small.
00767 //
00768 static MENU_INFO Menu_basic[] = {
00769   { "exit_event_loop", "exit_event_loop", true, 0, NULL }
00770 };
00771 #define N_MENU_BASIC  ( sizeof(Menu_basic) / sizeof(Menu_basic[0]) )
00772 
00773 void
00774 DaVinci::Event_Loop(DaVinci_Callback *cb_hook)
00775 {
00776   static DaVinci_Callback dflt_cb_hook;
00777 
00778   EVENT_T event;
00779   INT     i;
00780 
00781   if ( _in_event_loop || ! _display_ok ) return;
00782 
00783   if ( cb_hook == NULL ) {
00784     cb_hook = &dflt_cb_hook;
00785   }
00786   if ( ! _basic_menu_added ) {
00787     DA_ACK msg = Menu_Create( N_MENU_BASIC, Menu_basic );
00788     if ( msg ) {
00789       fprintf(stderr, "Unable to add Basic Menu -- %s.\n",
00790               "best to not start event_loop");
00791       return;
00792     }
00793     _basic_menu_added = true;
00794   }
00795   _in_event_loop = true;
00796 
00797   while ( _display_ok ) {
00798     while ( ! _event_q.empty() ) {
00799       event = _event_q.front();
00800       _event_q.pop();
00801       switch ( event.kind ) {
00802       case EK_COM_ERROR:
00803         fprintf(stderr, "event_loop: Unexpected: %s\n", event.u.com_error.msg);
00804         break;
00805       case EK_OK:
00806         fprintf(stderr, "event_loop: Unexpected: OK\n");
00807         break;
00808       case EK_SEL_EDGE:
00809         {
00810           EDGE_ID edge_id(event.u.sel_edge.edge_src,
00811                           event.u.sel_edge.edge_dst);
00812           cb_hook->Edge_Select( edge_id );
00813         }
00814         break;
00815       case EK_SEL_MENU:
00816         for (i = 0; i < N_MENU_BASIC; ++i) {
00817           if ( strcmp( event.u.sel_menu.label, Menu_basic[i].id ) == 0 ) {
00818             Menu_Basic_Do( event.u.sel_menu.label );
00819             break;
00820           }
00821         }
00822         if ( i >= N_MENU_BASIC ) {
00823           cb_hook->Menu_Select( event.u.sel_menu.label );
00824         }
00825         break;
00826       case EK_SEL_NODES:
00827         cb_hook->Node_Select( event.u.sel_nodes.n_nodes,
00828                               event.u.sel_nodes.node_ids );
00829         break;
00830       case EK_QUIT:
00831         _display_ok    = false;  // DaVinci exited.
00832         _in_event_loop = false;
00833         break;
00834       default:
00835         fprintf(stderr, "ERROR: event_loop missing event case %d\n",
00836                 event.kind);
00837       }
00838       if ( ! _in_event_loop ) {
00839         return;
00840       }
00841     }
00842     char *line = _io.In_Line();
00843 
00844     if ( line == NULL ) {
00845       _display_ok = false;
00846       break;
00847     }
00848     if ( Parse_Event( line, &event ) ) {
00849       _event_q.push( event );
00850     }
00851   }
00852   // more? flush event queue ? -- might reenter event loop later ..
00853 }
00854 
00855 void
00856 DaVinci::Exit_Event_Loop()
00857 {
00858   _in_event_loop = false;  // always to do this.
00859 }
00860 
00861 DA_ACK
00862 DaVinci::Title(const char *title)
00863 {
00864   if ( ! Usage_Ok( FT_TITLE, BASE_SET ) ) return "Usage-error";
00865 
00866   _io.Out_Fmt( "window(title(\"%s\"))\n", title );
00867   return Wait_For_Ack();
00868 }
00869 
00870 DA_ACK
00871 DaVinci::Show_Status(const char *status)
00872 {
00873   if ( ! Usage_Ok( FT_SHOW_STATUS, BASE_SET ) ) return "Usage-error";
00874 
00875   _io.Out_Fmt( "window(show_status(\"%s\"))\n", status );
00876   return Wait_For_Ack();
00877 }
00878 
00879 DA_ACK
00880 DaVinci::Show_Message(const char *msg)
00881 {
00882   if ( ! Usage_Ok( FT_SHOW_MESSAGE, BASE_SET ) ) return "Usage-error";
00883 
00884   _io.Out_Fmt( "window(show_message(\"%s\"))\n", msg );
00885   return Wait_For_Ack();
00886 }
00887 
00888 DA_ACK
00889 DaVinci::Menu_Create(INT n_items, const MENU_INFO *items)
00890 {
00891   if ( ! Usage_Ok( FT_MENU_CREATE, BASE_SET ) ) return "Usage-error";
00892 
00893   if ( n_items == 0 ) return NULL;  // no-op.
00894 
00895   _io.Out_Fmt( "app_menu(create_menus([" );
00896   Emit_Menu( n_items, items );
00897   _io.Out_Fmt( "]))\n" );
00898 
00899   DA_ACK msg = Wait_For_Ack();
00900   if ( msg ) return msg;
00901 
00902   return Menu_Set_Active();
00903 }
00904 
00905 DA_ACK
00906 DaVinci::Menu_Activate(INT n_ids, const char *id[])
00907 {
00908   if ( ! Usage_Ok( FT_MENU_ACTIVATE, BASE_SET ) ) return "Usage-error";
00909 
00910   for (INT i = 0; i < n_ids; ++i) {
00911     _menu_state.Set( id[i], DM_ACTIVE );
00912   }
00913   return Menu_Set_Active();
00914 }
00915 
00916 DA_ACK
00917 DaVinci::Menu_Deactivate(INT n_ids, const char *id[])
00918 {
00919   if ( ! Usage_Ok( FT_MENU_DEACTIVATE, BASE_SET ) ) return "Usage-error";
00920 
00921   for (INT i = 0; i < n_ids; ++i) {
00922     _menu_state.Set( id[i], DM_INACTIVE );
00923   }
00924   return Menu_Set_Active();
00925 }
00926 
00927 void
00928 DaVinci::Graph_Begin()
00929 {
00930   if ( ! Usage_Ok( FT_GRAPH_BEGIN, BASE_SET ) ) return;
00931 
00932   _io.Out_Fmt( "graph(new_placed([" );
00933   _node_cnt = 0;
00934 }
00935 
00936 void
00937 DaVinci::Node_Begin(NODE_ID id, const char *label, const NODE_TYPE& node_type)
00938 {
00939   if ( ! Usage_Ok( FT_NODE_BEGIN, (FT_GRAPH_BEGIN|FT_NODE_END) ) ) return;
00940 
00941   if ( _usage_check ) {
00942     if ( _node_def_set.count(id) > 0 ) {
00943       fprintf(stderr, "DaVinci::Node_Begin USAGE-ERROR, %s 0x%p\n",
00944               "duplicate def for node", id);
00945     } else {
00946       _node_def_set.insert(id);
00947     }
00948   }
00949   _io.Out_Fmt( "%sl(\"%x\",n(\"%s\",[a(\"OBJECT\",\"%s\")",
00950                ( _node_cnt > 0 ? "," : "" ),
00951                id, node_type._type_name, label);
00952   _node_cnt += 1;
00953   _edge_cnt =  0;           // i.e., out edges for this node decl.
00954   char *comma = ",";
00955   Emit_Attr( node_type, &comma );
00956   _io.Out_Fmt( "],[" );     // end node attributes, begin out_edges.
00957 
00958 }
00959 
00960 void
00961 DaVinci::Out_Edge(const EDGE_ID&   edge_id,
00962                   const EDGE_TYPE& edge_type,
00963                   const NODE_ID    dest_id)
00964 {
00965   if ( ! Usage_Ok( FT_OUT_EDGE, (FT_NODE_BEGIN|FT_OUT_EDGE) ) ) return;
00966 
00967   if ( _usage_check ) {
00968     _node_ref_set.insert(edge_id.dst);
00969   }
00970   _io.Out_Fmt( "%sl(\"%x:%x\",e(\"%s\",[",
00971                ( _edge_cnt > 0 ? "," : "" ),
00972                edge_id.src, edge_id.dst, edge_type._type_name);
00973   _edge_cnt += 1;
00974 
00975   Emit_Attr( edge_type );
00976   _io.Out_Fmt( "],r(\"%x\")))", dest_id);
00977 }
00978 
00979 void
00980 DaVinci::Node_End()
00981 {
00982   if ( ! Usage_Ok( FT_NODE_END, (FT_NODE_BEGIN|FT_OUT_EDGE) ) ) return;
00983 
00984   _io.Out_Fmt( "]))" );
00985 }
00986 
00987 DA_ACK
00988 DaVinci::Graph_End()
00989 {
00990   if ( _usage_check ) {
00991     for (set<NODE_ID>::iterator it_ref = _node_ref_set.begin();
00992          it_ref != _node_ref_set.end(); ++it_ref) {
00993       NODE_ID ref_id = *it_ref;
00994 
00995       if ( _node_def_set.count(ref_id) == 0 ) {
00996         fprintf(stderr, "ERROR DaVinci node 0x%p referenced, %s\n",
00997                 ref_id, "but not defined.");
00998       }
00999     }
01000   }
01001   if ( ! Usage_Ok( FT_GRAPH_END, (FT_NODE_END|FT_GRAPH_BEGIN) ) ) {
01002     return "Usage-error";
01003   }
01004   _io.Out_Fmt( "]))\n" );
01005 
01006   return Wait_For_Ack();
01007 }
01008 
01009 DA_ACK
01010 DaVinci::Change_Attr(const NODE_ID     id,
01011                      const NODE_TYPE&  nt,
01012                      const char       *new_label)
01013 {
01014   if ( ! Usage_Ok( FT_CHANGE_ATTR, BASE_SET ) ) return "Usage-error";
01015 
01016   char *comma = "";
01017 
01018   _io.Out_Fmt( "graph(change_attr([node(\"%x\",[", id);
01019 
01020   if ( new_label ) {
01021     _io.Out_Fmt( "a(\"OBJECT\",\"%s\")", new_label );
01022     comma = ",";
01023   }
01024   Emit_Attr( nt, &comma );
01025 
01026   _io.Out_Fmt( "])]))\n");
01027 
01028   return Wait_For_Ack();
01029 }
01030 
01031 DA_ACK
01032 DaVinci::Change_Attr(const EDGE_ID& edge_id, const EDGE_TYPE& et)
01033 {
01034   if ( ! Usage_Ok( FT_CHANGE_ATTR, BASE_SET ) ) return "Usage-error";
01035 
01036   _io.Out_Fmt( "graph(change_attr([edge(\"%x:%x\",[",
01037                edge_id.src, edge_id.dst );
01038   Emit_Attr( et );
01039   _io.Out_Fmt( "])]))\n");
01040 
01041   return Wait_For_Ack();
01042 }
01043 
01044 void
01045 DaVinci::Update_Begin()
01046 {
01047   if ( ! Usage_Ok( FT_UPDATE_BEGIN, BASE_SET ) ) return;
01048 
01049   _io.Out_Fmt( "graph(update([" );
01050   _node_cnt = 0;
01051   _edge_cnt = 0;
01052 }
01053 
01054 void
01055 DaVinci::New_Node(NODE_ID id, const char *label, const NODE_TYPE& nt )
01056 {
01057   if ( ! Usage_Ok( FT_NEW_NODE, (FT_UPDATE_BEGIN|FT_NEW_NODE) ) ) return;
01058 
01059   if ( _edge_cnt > 0 ) {
01060     fprintf(stderr, "Must list ALL new_nodes before first new_edge\n");
01061     fprintf(stderr, "Skipping this node to avoid DaVinci error.\n");
01062     return;
01063   }
01064   _io.Out_Fmt( "%snew_node(\"%x\",[a(\"OBJECT\",\"%s\")",
01065                (_node_cnt > 0 ? "," : ""), id, label );
01066   char *comma = ",";
01067   Emit_Attr( nt, &comma );
01068   _node_cnt += 1;
01069 }
01070 
01071 void
01072 DaVinci::New_Edge(const EDGE_ID&   id,
01073                   const EDGE_TYPE& et,
01074                   NODE_ID          src,
01075                   NODE_ID          dst)
01076 {
01077   if ( ! Usage_Ok( FT_NEW_EDGE, (FT_UPDATE_BEGIN|FT_NEW_NODE|FT_NEW_EDGE) ) ) {
01078     return;
01079   }
01080   if ( _edge_cnt == 0 ) {
01081     _io.Out_Fmt( "],[" );  // end new_node + begin new_edge list.
01082   }
01083   _io.Out_Fmt( "%snew_edge(\"%x:%x\",\"\",[", (_edge_cnt > 0 ? "," : ""),
01084                id.src, id.dst );
01085   Emit_Attr( et );
01086   _io.Out_Fmt( "],\"%x\",\"%x\")", src, dst);
01087   _edge_cnt += 1;
01088 }
01089 
01090 DA_ACK
01091 DaVinci::Update_End()
01092 {
01093   if ( ! Usage_Ok( FT_UPDATE_END,
01094                    (FT_UPDATE_BEGIN|FT_NEW_NODE|FT_NEW_EDGE) ) ) {
01095     return "Usage-error";
01096   }
01097   if ( _edge_cnt == 0 ) {
01098     _io.Out_Fmt( "],[" );  // end new_node + begin empty new_edge list.
01099   }
01100   _io.Out_Fmt( "]))\n");
01101 
01102   return Wait_For_Ack();
01103 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines