engine.cc
1 #include "engine.h" 2 #include "hw.h" 3 #include "main.h" 4 #include "print-gui.h" 5 #include "print.h" 6 #include "osutils.h" 7 #include "options.h" 8 9 #include <cstring> 10 #include <cstdlib> 11 #include <iostream> 12 #include <fstream> 13 #include <sys/utsname.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <libgen.h> 17 18 static const char *id = "@(#) $Id$"; 19 20 extern "C" 21 { 22 #include "support.h" 23 }; 24 25 #define AUTOMATIC "automatic file format" 26 #define LSHW_XML "lshw XML format (.lshw, .xml)" 27 #define PLAIN_TEXT "plain text document (.text, .txt)" 28 #define JSON "JavaScript Object Notation document (.json)" 29 #define HTML "HTML document (.html, .htm)" 30 31 #define YIELD() while(gtk_events_pending()) gtk_main_iteration() 32 33 static hwNode container("container", hw::generic); 34 static hwNode *displayed = NULL; 35 static hwNode *selected1 = NULL; 36 static hwNode *selected2 = NULL; 37 static hwNode *selected3 = NULL; 38 39 extern GtkWidget *mainwindow; 40 extern GtkWidget *list1, *list2, *list3; 41 extern GtkWidget *description; 42 extern GtkWidget *statusbar; 43 extern GHashTable *pixbufs; 44 extern GSimpleAction *go_up_action; 45 extern GSimpleAction *save_action; 46 47 enum 48 { 49 COL_NAME = 0, 50 COL_NODE, 51 COL_WEIGHT, 52 COL_CONTINUATION, 53 NUM_COLS 54 }; 55 56 static void clear_list(GtkWidget * list1) 57 { 58 GtkTreeViewColumn *col; 59 60 while((col = gtk_tree_view_get_column(GTK_TREE_VIEW(list1), 0))) 61 gtk_tree_view_remove_column(GTK_TREE_VIEW(list1), col); 62 } 63 64 65 static void populate_sublist(GtkWidget * list1, hwNode * root, hwNode *current=NULL) 66 { 67 GtkListStore *list_store = NULL; 68 GtkTreeViewColumn *col = NULL; 69 GtkCellRenderer *renderer = NULL; 70 GtkTreeIter iter; 71 GtkTreeIter current_iter; 72 73 clear_list(list1); 74 75 if(!root) return; 76 77 list_store = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_STRING); 78 79 for(unsigned i = 0; i<root->countChildren(); i++) 80 { 81 string text; 82 83 gtk_list_store_append(list_store, &iter); 84 85 if(root->getChild(i) == current) current_iter = iter; 86 87 text = root->getChild(i)->getDescription(); 88 if(text == "") 89 text = root->getChild(i)->getProduct(); 90 if(text == "") 91 text = root->getChild(i)->getId(); 92 93 gtk_list_store_set (list_store, &iter, 94 COL_NAME, text.c_str(), 95 COL_NODE, root->getChild(i), 96 COL_WEIGHT, (root->getChild(i)->countChildren()>0)?PANGO_WEIGHT_BOLD:PANGO_WEIGHT_NORMAL, 97 COL_CONTINUATION, (root->getChild(i)->countChildren()>0)?"\342\226\270":"", 98 -1); 99 } 100 101 col = gtk_tree_view_column_new(); 102 gtk_tree_view_append_column(GTK_TREE_VIEW(list1), col); 103 104 renderer = gtk_cell_renderer_text_new(); 105 106 gtk_tree_view_column_pack_start(col, renderer, TRUE); 107 gtk_tree_view_column_add_attribute(col, renderer, "text", COL_NAME); 108 gtk_tree_view_column_add_attribute(col, renderer, "weight", COL_WEIGHT); 109 110 col = gtk_tree_view_column_new(); 111 gtk_tree_view_append_column(GTK_TREE_VIEW(list1), col); 112 renderer = gtk_cell_renderer_text_new(); 113 g_object_set(renderer, "xalign", (gfloat)1, NULL); 114 gtk_tree_view_column_pack_start(col, renderer, TRUE); 115 gtk_tree_view_column_add_attribute(col, renderer, "text", COL_CONTINUATION); 116 gtk_tree_view_column_add_attribute(col, renderer, "weight", COL_WEIGHT); 117 118 gtk_tree_view_set_model(GTK_TREE_VIEW(list1), GTK_TREE_MODEL(list_store)); 119 g_object_unref(list_store); 120 121 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(gtk_tree_view_get_selection(GTK_TREE_VIEW(list1))), GTK_SELECTION_BROWSE); 122 if(current) 123 gtk_tree_selection_select_iter(GTK_TREE_SELECTION(gtk_tree_view_get_selection(GTK_TREE_VIEW(list1))), ¤t_iter); 124 } 125 126 127 static void create_tags (GtkTextBuffer *buffer) 128 { 129 static bool initialized = false; 130 131 if(initialized) return; 132 133 initialized = true; 134 135 gtk_text_buffer_create_tag (buffer, "heading", 136 "weight", PANGO_WEIGHT_BOLD, 137 "size", 15 * PANGO_SCALE, 138 NULL); 139 140 gtk_text_buffer_create_tag (buffer, "italic", 141 "style", PANGO_STYLE_ITALIC, NULL); 142 143 gtk_text_buffer_create_tag (buffer, "bold", 144 "weight", PANGO_WEIGHT_BOLD, NULL); 145 146 gtk_text_buffer_create_tag (buffer, "big", 147 /* points times the PANGO_SCALE factor */ 148 "size", 20 * PANGO_SCALE, NULL); 149 150 gtk_text_buffer_create_tag (buffer, "xx-small", 151 "scale", PANGO_SCALE_XX_SMALL, NULL); 152 153 gtk_text_buffer_create_tag (buffer, "x-large", 154 "scale", PANGO_SCALE_X_LARGE, NULL); 155 156 gtk_text_buffer_create_tag (buffer, "monospace", 157 "family", "monospace", NULL); 158 159 gtk_text_buffer_create_tag (buffer, "blue_foreground", 160 "foreground", "blue", NULL); 161 162 gtk_text_buffer_create_tag (buffer, "red_background", 163 "background", "red", NULL); 164 165 gtk_text_buffer_create_tag (buffer, "big_gap_before_line", 166 "pixels_above_lines", 30, NULL); 167 168 gtk_text_buffer_create_tag (buffer, "big_gap_after_line", 169 "pixels_below_lines", 30, NULL); 170 171 gtk_text_buffer_create_tag (buffer, "double_spaced_line", 172 "pixels_inside_wrap", 10, NULL); 173 174 gtk_text_buffer_create_tag (buffer, "not_editable", 175 "editable", FALSE, NULL); 176 177 gtk_text_buffer_create_tag (buffer, "word_wrap", 178 "wrap_mode", GTK_WRAP_WORD, NULL); 179 180 gtk_text_buffer_create_tag (buffer, "char_wrap", 181 "wrap_mode", GTK_WRAP_CHAR, NULL); 182 183 gtk_text_buffer_create_tag (buffer, "no_wrap", 184 "wrap_mode", GTK_WRAP_NONE, NULL); 185 186 gtk_text_buffer_create_tag (buffer, "center", 187 "justification", GTK_JUSTIFY_CENTER, NULL); 188 189 gtk_text_buffer_create_tag (buffer, "right_justify", 190 "justification", GTK_JUSTIFY_RIGHT, NULL); 191 192 gtk_text_buffer_create_tag (buffer, "wide_margins", 193 "left_margin", 50, "right_margin", 50, 194 NULL); 195 196 gtk_text_buffer_create_tag (buffer, "strikethrough", 197 "strikethrough", TRUE, NULL); 198 199 gtk_text_buffer_create_tag (buffer, "underline", 200 "underline", PANGO_UNDERLINE_SINGLE, NULL); 201 202 gtk_text_buffer_create_tag (buffer, "double_underline", 203 "underline", PANGO_UNDERLINE_DOUBLE, NULL); 204 205 gtk_text_buffer_create_tag (buffer, "superscript", 206 "rise", 10 * PANGO_SCALE, /* 10 pixels */ 207 "size", 8 * PANGO_SCALE, /* 8 points */ 208 NULL); 209 210 gtk_text_buffer_create_tag (buffer, "subscript", 211 "rise", -10 * PANGO_SCALE, /* 10 pixels */ 212 "size", 8 * PANGO_SCALE, /* 8 points */ 213 NULL); 214 } 215 216 217 static void display(GtkWidget * mainwindow) 218 { 219 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (description)); 220 221 gtk_text_buffer_set_text(buffer, "", -1); 222 223 if(!displayed) 224 gtk_text_buffer_set_text(buffer, "Please select a node to display.", -1); 225 else 226 { 227 create_tags(buffer); 228 229 string hwpath = gethwpath(*displayed, container); 230 printmarkup(*displayed, GTK_TEXT_VIEW(description), hwpath, pixbufs); 231 } 232 } 233 234 235 void status(const char *message) 236 { 237 static guint context_id = 0; 238 239 if(!GTK_IS_WIDGET(statusbar)) return; 240 241 if(!context_id) 242 context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "scanning"); 243 else 244 gtk_statusbar_pop(GTK_STATUSBAR(statusbar), context_id); 245 246 if(message) gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, message); 247 248 YIELD(); 249 } 250 251 252 void refresh(GtkWidget *mainwindow) 253 { 254 hwNode computer("computer", hw::system); 255 static bool lock = false; 256 257 if(lock) return; 258 259 lock = true; 260 g_simple_action_set_enabled(save_action, FALSE); 261 262 populate_sublist(list1, NULL); 263 populate_sublist(list2, NULL); 264 populate_sublist(list3, NULL); 265 displayed = NULL; 266 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(description)), "scanning...", -1); 267 268 disable("output:sanitize"); 269 container = hwNode("container", hw::generic); 270 status("Scanning..."); 271 scan_system(computer); 272 status(NULL); 273 displayed = container.addChild(computer); 274 275 g_simple_action_set_enabled(go_up_action, FALSE); 276 g_simple_action_set_enabled(save_action, TRUE); 277 278 selected1 = NULL; 279 selected2 = NULL; 280 selected3 = NULL; 281 282 populate_sublist(list1, &container); 283 populate_sublist(list2, NULL); 284 populate_sublist(list3, NULL); 285 display(mainwindow); 286 287 lock = false; 288 } 289 290 291 void change_selection(unsigned list, GtkTreeView *treeview) 292 { 293 GtkTreeSelection *selection; 294 GtkTreeModel *model; 295 GtkTreeIter iter; 296 297 model = gtk_tree_view_get_model(treeview); 298 299 displayed = NULL; 300 301 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); 302 if (gtk_tree_selection_get_selected(selection, &model, &iter)) 303 gtk_tree_model_get (model, &iter, COL_NODE, &displayed, -1); 304 305 if(list<2) populate_sublist(list2, NULL); 306 if(list<3) populate_sublist(list3, NULL); 307 308 display(mainwindow); 309 } 310 311 312 static hwNode * find_parent(hwNode * n, hwNode *sub) 313 { 314 if(!n) return NULL; 315 316 if(n == sub) return n; 317 318 for(unsigned i=0; i<sub->countChildren(); i++) 319 { 320 if(sub->getChild(i) == n) return sub; 321 322 hwNode *r = find_parent(n, sub->getChild(i)); 323 if(r) return r; 324 } 325 326 return NULL; 327 } 328 329 330 void browse(unsigned list, GtkTreeView *treeview) 331 { 332 GtkTreeSelection *selection; 333 GtkTreeModel *model; 334 GtkTreeIter iter; 335 hwNode *n = NULL; 336 337 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); 338 if (gtk_tree_selection_get_selected(selection, &model, &iter)) 339 gtk_tree_model_get (model, &iter, COL_NODE, &n, -1); 340 341 if(n) 342 switch(list) 343 { 344 case 1: 345 //if(n!=selected1) 346 { 347 selected1 = n; 348 selected2 = NULL; 349 selected3 = NULL; 350 populate_sublist(list2, selected1, selected2); 351 populate_sublist(list3, selected2, selected3); 352 } 353 break; 354 case 2: 355 //if(n!=selected2) 356 { 357 selected2 = n; 358 selected3 = NULL; 359 populate_sublist(list3, selected2, selected3); 360 } 361 break; 362 case 3: 363 //if(n!=selected3) 364 { 365 selected3 = n; 366 if(n->countChildren()>0) 367 { 368 hwNode *oldselected1 = selected1; 369 selected1 = selected2; 370 selected2 = n; 371 selected3 = NULL; 372 populate_sublist(list1, oldselected1, selected1); 373 populate_sublist(list2, selected1, selected2); 374 populate_sublist(list3, selected2, selected3); 375 } 376 } 377 break; 378 } 379 380 g_simple_action_set_enabled(go_up_action, selected1 && (find_parent(selected1, &container)!= &container)); 381 382 (void) &::id; // avoid warning "id defined but not used" 383 } 384 385 386 void go_back(GtkWidget *mainwindow) 387 { 388 if(selected1 && (find_parent(selected1, &container)!= &container)) 389 { 390 selected3 = selected2; 391 selected2 = selected1; 392 selected1 = find_parent(selected1, &container); 393 if(selected1 == &container) selected1 = container.getChild(0); 394 populate_sublist(list1, find_parent(selected1, &container), selected1); 395 populate_sublist(list2, selected1, selected2); 396 populate_sublist(list3, selected2, selected3); 397 398 if(find_parent(displayed, &container)!= &container) 399 displayed = find_parent(displayed, &container); 400 } 401 402 g_simple_action_set_enabled(go_up_action, selected1 && (find_parent(selected1, &container)!= &container)); 403 404 display(mainwindow); 405 } 406 407 static const char *guess_format(char *s) 408 { 409 char *dot = strrchr(s, '.'); 410 411 if(!dot) 412 return LSHW_XML; 413 414 if(!strcasecmp(".html", dot) || !strcasecmp(".htm", dot)) 415 return HTML; 416 417 if(!strcasecmp(".text", dot) || !strcasecmp(".txt", dot)) 418 return PLAIN_TEXT; 419 420 if(!strcasecmp(".json", dot)) 421 return JSON; 422 423 return LSHW_XML; 424 } 425 426 static char *fix_filename(char *s, const char *extension) 427 { 428 char *dot = strrchr(s, '.'); 429 430 if(dot) 431 return s; 432 433 s = (char*)realloc(s, strlen(s) + 1 + strlen(extension) + 1); 434 strcat(s, "."); 435 strcat(s, extension); 436 437 return s; 438 } 439 440 static void redirect_cout(std::ofstream &out, bool enable = true) 441 { 442 static std::streambuf* old_cout; 443 444 if(enable) 445 { 446 old_cout = cout.rdbuf(); 447 cout.rdbuf(out.rdbuf()); 448 } 449 else 450 cout.rdbuf(old_cout); 451 } 452 453 void save_as(GtkWidget *mainwindow) 454 { 455 struct utsname buf; 456 GtkFileChooserNative *dialog = NULL; 457 GtkWidget *sanitize = NULL; 458 GtkFileFilter *filter = NULL; 459 bool proceed = true; 460 hwNode *computer = container.getChild(0); 461 462 if(!computer) // nothing to save 463 return; 464 465 dialog = gtk_file_chooser_native_new ("Save hardware configuration", 466 GTK_WINDOW(mainwindow), 467 GTK_FILE_CHOOSER_ACTION_SAVE, 468 "_Save", 469 "_Cancel"); 470 //gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); 471 sanitize = gtk_check_button_new_with_label("Anonymize output"); 472 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sanitize), enabled("output:sanitize")?TRUE:FALSE); 473 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), sanitize); 474 475 filter = gtk_file_filter_new (); 476 gtk_file_filter_set_name(filter, AUTOMATIC); 477 gtk_file_filter_add_pattern(filter, "*"); 478 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); 479 filter = gtk_file_filter_new (); 480 gtk_file_filter_set_name(filter, LSHW_XML); 481 gtk_file_filter_add_pattern(filter, "*.lshw"); 482 gtk_file_filter_add_pattern(filter, "*.xml"); 483 gtk_file_filter_add_mime_type(filter, "text/xml"); 484 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); 485 filter = gtk_file_filter_new (); 486 gtk_file_filter_set_name(filter, HTML); 487 gtk_file_filter_add_pattern(filter, "*.html"); 488 gtk_file_filter_add_pattern(filter, "*.htm"); 489 gtk_file_filter_add_mime_type(filter, "text/html"); 490 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); 491 filter = gtk_file_filter_new (); 492 gtk_file_filter_set_name(filter, PLAIN_TEXT); 493 gtk_file_filter_add_pattern(filter, "*.text"); 494 gtk_file_filter_add_pattern(filter, "*.txt"); 495 gtk_file_filter_add_mime_type(filter, "text/plain"); 496 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); 497 filter = gtk_file_filter_new (); 498 gtk_file_filter_set_name(filter, JSON); 499 gtk_file_filter_add_pattern(filter, "*.json"); 500 gtk_file_filter_add_mime_type(filter, "application/json"); 501 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); 502 503 if(uname(&buf)==0) 504 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), buf.nodename); 505 506 if (gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) 507 { 508 char *filename; 509 510 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sanitize))) 511 enable("output:sanitize"); 512 else 513 disable("output:sanitize"); 514 515 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); 516 filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog)); 517 if(filename && filter) 518 { 519 const gchar *filtername = gtk_file_filter_get_name(filter); 520 521 if(strcmp(filtername, AUTOMATIC)==0) 522 filtername = guess_format(filename); 523 524 if(!exists(filename)) // creating a new file 525 { 526 if(strcmp(filtername, LSHW_XML)==0) 527 filename = fix_filename(filename, "lshw"); 528 else 529 if(strcmp(filtername, HTML)==0) 530 filename = fix_filename(filename, "html"); 531 else 532 if(strcmp(filtername, PLAIN_TEXT)==0) 533 filename = fix_filename(filename, "txt"); 534 else 535 if(strcmp(filtername, JSON)==0) 536 filename = fix_filename(filename, "json"); 537 } 538 539 if(exists(filename)) // existing file 540 { 541 char * buffer1 = g_strdup(filename); 542 char * buffer2 = g_strdup(filename); 543 544 GtkWidget *dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW(mainwindow), 545 GTK_DIALOG_DESTROY_WITH_PARENT, 546 GTK_MESSAGE_WARNING, 547 GTK_BUTTONS_NONE, 548 "A file named <i><tt>%s</tt></i> already exists in folder <tt>%s</tt>.\n\nDo you want to overwrite it?", 549 basename(buffer1), dirname(buffer2)); 550 gtk_dialog_add_buttons(GTK_DIALOG(dialog), 551 "_Cancel", GTK_RESPONSE_CANCEL, 552 "_Overwrite", GTK_RESPONSE_ACCEPT, 553 NULL); 554 proceed = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT); 555 gtk_widget_destroy (dialog); 556 g_free(buffer1); 557 g_free(buffer2); 558 } 559 560 if(proceed) 561 { 562 if(strcmp(filtername, LSHW_XML)==0) 563 { 564 std::ofstream out(filename); 565 redirect_cout(out); 566 cout << computer->asXML(); 567 redirect_cout(out, false); 568 } 569 else 570 if(strcmp(filtername, HTML)==0) 571 { 572 std::ofstream out(filename); 573 redirect_cout(out); 574 print(*computer, true); 575 redirect_cout(out, false); 576 } 577 else 578 if(strcmp(filtername, PLAIN_TEXT)==0) 579 { 580 std::ofstream out(filename); 581 redirect_cout(out); 582 print(*computer, false); 583 redirect_cout(out, false); 584 } 585 else 586 if(strcmp(filtername, JSON)==0) 587 { 588 std::ofstream out(filename); 589 redirect_cout(out); 590 cout << computer->asJSON() << endl; 591 redirect_cout(out, false); 592 } 593 } 594 g_free (filename); 595 } 596 } 597 598 g_object_unref (dialog); 599 }