Open64 (mfef90, whirl2f, and IR tools)
TAG: version-openad; SVN changeset: 916
|
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 }