Back to index

lshw  02.16
engine.cc
Go to the documentation of this file.
00001 #include "engine.h"
00002 #include "hw.h"
00003 #include "main.h"
00004 #include "print-gui.h"
00005 #include "print.h"
00006 #include "osutils.h"
00007 #include "options.h"
00008 
00009 #include <iostream>
00010 #include <fstream>
00011 #include <sys/utsname.h>
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <libgen.h>
00015 
00016 static const char *id = "@(#) $Id: engine.cc 2433 2012-01-10 22:01:30Z lyonel $";
00017 
00018 extern "C"
00019 {
00020 #include "support.h"
00021 };
00022 
00023 #define AUTOMATIC "automatic file format"
00024 #define LSHW_XML "lshw XML format (.lshw, .xml)"
00025 #define PLAIN_TEXT "plain text document (.text, .txt)"
00026 #define JSON "JavaScript Object Notation document (.json)"
00027 #define HTML "HTML document (.html, .htm)"
00028 
00029 #define YIELD()  while(gtk_events_pending()) gtk_main_iteration()
00030 
00031 static hwNode container("container", hw::generic);
00032 static hwNode *displayed = NULL;
00033 static hwNode *selected1 = NULL;
00034 static hwNode *selected2 = NULL;
00035 static hwNode *selected3 = NULL;
00036 
00037 extern GtkWidget *mainwindow;
00038 extern GtkWidget *list1, *list2, *list3;
00039 extern GtkWidget *description;
00040 extern GtkWidget *go_up_button;
00041 extern GtkWidget *save_button;
00042 extern GtkWidget *statusbar;
00043 
00044 enum
00045 {
00046   COL_NAME = 0,
00047   COL_NODE,
00048   COL_WEIGHT,
00049   COL_CONTINUATION,
00050   NUM_COLS
00051 };
00052 
00053 static void clear_list(GtkWidget * list1)
00054 {
00055   GtkTreeViewColumn *col;
00056 
00057   while((col = gtk_tree_view_get_column(GTK_TREE_VIEW(list1), 0)))
00058     gtk_tree_view_remove_column(GTK_TREE_VIEW(list1), col);
00059 }
00060 
00061 
00062 static void populate_sublist(GtkWidget * list1, hwNode * root, hwNode *current=NULL)
00063 {
00064   GtkListStore *list_store = NULL;
00065   GtkTreeViewColumn   *col = NULL;
00066   GtkCellRenderer     *renderer = NULL;
00067   GtkTreeIter iter;
00068   GtkTreeIter current_iter;
00069 
00070   clear_list(list1);
00071 
00072   if(!root) return;
00073 
00074   list_store = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_STRING);
00075 
00076   for(unsigned i = 0; i<root->countChildren(); i++)
00077   {
00078     string text;
00079 
00080     gtk_list_store_append(list_store, &iter);
00081 
00082     if(root->getChild(i) == current) current_iter = iter;
00083 
00084     text = root->getChild(i)->getDescription();
00085     if(text == "")
00086       text = root->getChild(i)->getProduct();
00087     if(text == "")
00088       text = root->getChild(i)->getId();
00089 
00090     gtk_list_store_set (list_store, &iter,
00091       COL_NAME, text.c_str(),
00092       COL_NODE, root->getChild(i),
00093       COL_WEIGHT, (root->getChild(i)->countChildren()>0)?PANGO_WEIGHT_BOLD:PANGO_WEIGHT_NORMAL,
00094       COL_CONTINUATION, (root->getChild(i)->countChildren()>0)?"\342\226\270":"",
00095       -1);
00096   }
00097 
00098   col = gtk_tree_view_column_new();
00099   gtk_tree_view_append_column(GTK_TREE_VIEW(list1), col);
00100 
00101   renderer = gtk_cell_renderer_text_new();
00102 
00103   gtk_tree_view_column_pack_start(col, renderer, TRUE);
00104   gtk_tree_view_column_add_attribute(col, renderer, "text", COL_NAME);
00105   gtk_tree_view_column_add_attribute(col, renderer, "weight", COL_WEIGHT);
00106 
00107   col = gtk_tree_view_column_new();
00108   gtk_tree_view_append_column(GTK_TREE_VIEW(list1), col);
00109   renderer = gtk_cell_renderer_text_new();
00110   g_object_set(renderer, "xalign", (gfloat)1, NULL);
00111   gtk_tree_view_column_pack_start(col, renderer, TRUE);
00112   gtk_tree_view_column_add_attribute(col, renderer, "text", COL_CONTINUATION);
00113   gtk_tree_view_column_add_attribute(col, renderer, "weight", COL_WEIGHT);
00114 
00115   gtk_tree_view_set_model(GTK_TREE_VIEW(list1), GTK_TREE_MODEL(list_store));
00116   g_object_unref(list_store);
00117 
00118   gtk_tree_selection_set_mode(GTK_TREE_SELECTION(gtk_tree_view_get_selection(GTK_TREE_VIEW(list1))), GTK_SELECTION_BROWSE);
00119   if(current)
00120     gtk_tree_selection_select_iter(GTK_TREE_SELECTION(gtk_tree_view_get_selection(GTK_TREE_VIEW(list1))), &current_iter);
00121 }
00122 
00123 
00124 static void create_tags (GtkTextBuffer *buffer)
00125 {
00126   static bool initialized = false;
00127 
00128   if(initialized) return;
00129 
00130   initialized = true;
00131 
00132   gtk_text_buffer_create_tag (buffer, "heading",
00133     "weight", PANGO_WEIGHT_BOLD,
00134     "size", 15 * PANGO_SCALE,
00135     NULL);
00136 
00137   gtk_text_buffer_create_tag (buffer, "italic",
00138     "style", PANGO_STYLE_ITALIC, NULL);
00139 
00140   gtk_text_buffer_create_tag (buffer, "bold",
00141     "weight", PANGO_WEIGHT_BOLD, NULL);
00142 
00143   gtk_text_buffer_create_tag (buffer, "big",
00144 /* points times the PANGO_SCALE factor */
00145     "size", 20 * PANGO_SCALE, NULL);
00146 
00147   gtk_text_buffer_create_tag (buffer, "xx-small",
00148     "scale", PANGO_SCALE_XX_SMALL, NULL);
00149 
00150   gtk_text_buffer_create_tag (buffer, "x-large",
00151     "scale", PANGO_SCALE_X_LARGE, NULL);
00152 
00153   gtk_text_buffer_create_tag (buffer, "monospace",
00154     "family", "monospace", NULL);
00155 
00156   gtk_text_buffer_create_tag (buffer, "blue_foreground",
00157     "foreground", "blue", NULL);
00158 
00159   gtk_text_buffer_create_tag (buffer, "red_background",
00160     "background", "red", NULL);
00161 
00162   gtk_text_buffer_create_tag (buffer, "big_gap_before_line",
00163     "pixels_above_lines", 30, NULL);
00164 
00165   gtk_text_buffer_create_tag (buffer, "big_gap_after_line",
00166     "pixels_below_lines", 30, NULL);
00167 
00168   gtk_text_buffer_create_tag (buffer, "double_spaced_line",
00169     "pixels_inside_wrap", 10, NULL);
00170 
00171   gtk_text_buffer_create_tag (buffer, "not_editable",
00172     "editable", FALSE, NULL);
00173 
00174   gtk_text_buffer_create_tag (buffer, "word_wrap",
00175     "wrap_mode", GTK_WRAP_WORD, NULL);
00176 
00177   gtk_text_buffer_create_tag (buffer, "char_wrap",
00178     "wrap_mode", GTK_WRAP_CHAR, NULL);
00179 
00180   gtk_text_buffer_create_tag (buffer, "no_wrap",
00181     "wrap_mode", GTK_WRAP_NONE, NULL);
00182 
00183   gtk_text_buffer_create_tag (buffer, "center",
00184     "justification", GTK_JUSTIFY_CENTER, NULL);
00185 
00186   gtk_text_buffer_create_tag (buffer, "right_justify",
00187     "justification", GTK_JUSTIFY_RIGHT, NULL);
00188 
00189   gtk_text_buffer_create_tag (buffer, "wide_margins",
00190     "left_margin", 50, "right_margin", 50,
00191     NULL);
00192 
00193   gtk_text_buffer_create_tag (buffer, "strikethrough",
00194     "strikethrough", TRUE, NULL);
00195 
00196   gtk_text_buffer_create_tag (buffer, "underline",
00197     "underline", PANGO_UNDERLINE_SINGLE, NULL);
00198 
00199   gtk_text_buffer_create_tag (buffer, "double_underline",
00200     "underline", PANGO_UNDERLINE_DOUBLE, NULL);
00201 
00202   gtk_text_buffer_create_tag (buffer, "superscript",
00203     "rise", 10 * PANGO_SCALE,                     /* 10 pixels */
00204     "size", 8 * PANGO_SCALE,                      /* 8 points */
00205     NULL);
00206 
00207   gtk_text_buffer_create_tag (buffer, "subscript",
00208     "rise", -10 * PANGO_SCALE,                    /* 10 pixels */
00209     "size", 8 * PANGO_SCALE,                      /* 8 points */
00210     NULL);
00211 }
00212 
00213 
00214 static void display(GtkWidget * mainwindow)
00215 {
00216   GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (description));
00217 
00218   gtk_text_buffer_set_text(buffer, "", -1);
00219 
00220   if(!displayed)
00221     gtk_text_buffer_set_text(buffer, "Please select a node to display.", -1);
00222   else
00223   {
00224     create_tags(buffer);
00225 
00226     string hwpath = gethwpath(*displayed, container);
00227     printmarkup(*displayed, GTK_TEXT_VIEW(description), hwpath);
00228   }
00229 }
00230 
00231 
00232 void status(const char *message)
00233 {
00234   static guint context_id = 0;
00235 
00236   if(!GTK_IS_WIDGET(statusbar)) return;
00237 
00238   if(!context_id)
00239     context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "scanning");
00240   else
00241     gtk_statusbar_pop(GTK_STATUSBAR(statusbar), context_id);
00242 
00243   if(message) gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, message);
00244 
00245   YIELD();
00246 }
00247 
00248 
00249 void refresh(GtkWidget *mainwindow)
00250 {
00251   hwNode computer("computer", hw::system);
00252   static bool lock = false;
00253   //GtkWidget * menu = lookup_widget(mainwindow, "menu");
00254   //GtkWidget * save_menuitem = lookup_widget(menu, "save");
00255 
00256   if(lock) return;
00257 
00258   lock = true;
00259   gtk_widget_set_sensitive(save_button, FALSE);
00260   //gtk_widget_set_sensitive(save_menuitem, FALSE);
00261 
00262   populate_sublist(list1, NULL);
00263   populate_sublist(list2, NULL);
00264   populate_sublist(list3, NULL);
00265   displayed = NULL;
00266   gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(description)), "scanning...", -1);
00267 
00268   disable("output:sanitize");
00269   container = hwNode("container", hw::generic);
00270   status("Scanning...");
00271   scan_system(computer);
00272   status(NULL);
00273   displayed = container.addChild(computer);
00274 
00275   gtk_widget_set_sensitive(go_up_button, FALSE);
00276   gtk_widget_set_sensitive(save_button, TRUE);
00277   //gtk_widget_set_sensitive(save_menuitem, TRUE);
00278 
00279   selected1 = NULL;
00280   selected2 = NULL;
00281   selected3 = NULL;
00282 
00283   populate_sublist(list1, &container);
00284   populate_sublist(list2, NULL);
00285   populate_sublist(list3, NULL);
00286   display(mainwindow);
00287 
00288   lock = false;
00289 }
00290 
00291 
00292 void change_selection(unsigned list, GtkTreeView *treeview)
00293 {
00294   GtkTreeSelection *selection;
00295   GtkTreeModel *model;
00296   GtkTreeIter   iter;
00297 
00298   model = gtk_tree_view_get_model(treeview);
00299 
00300   displayed = NULL;
00301 
00302   selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
00303   if (gtk_tree_selection_get_selected(selection, &model, &iter))
00304     gtk_tree_model_get (model, &iter, COL_NODE, &displayed, -1);
00305 
00306   if(list<2) populate_sublist(list2, NULL);
00307   if(list<3) populate_sublist(list3, NULL);
00308 
00309   display(mainwindow);
00310 }
00311 
00312 
00313 static hwNode * find_parent(hwNode * n, hwNode *sub)
00314 {
00315   if(!n) return NULL;
00316 
00317   if(n == sub) return n;
00318 
00319   for(unsigned i=0; i<sub->countChildren(); i++)
00320   {
00321     if(sub->getChild(i) == n) return sub;
00322 
00323     hwNode *r = find_parent(n, sub->getChild(i));
00324     if(r) return r;
00325   }
00326 
00327   return NULL;
00328 }
00329 
00330 
00331 void browse(unsigned list, GtkTreeView *treeview)
00332 {
00333   GtkTreeSelection *selection;
00334   GtkTreeModel     *model;
00335   GtkTreeIter       iter;
00336   hwNode *n = NULL;
00337 
00338   selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
00339   if (gtk_tree_selection_get_selected(selection, &model, &iter))
00340     gtk_tree_model_get (model, &iter, COL_NODE, &n, -1);
00341 
00342   if(n)
00343     switch(list)
00344     {
00345       case 1:
00346 //if(n!=selected1)
00347         {
00348           selected1 = n;
00349           selected2 = NULL;
00350           selected3 = NULL;
00351           populate_sublist(list2, selected1, selected2);
00352           populate_sublist(list3, selected2, selected3);
00353         }
00354       break;
00355     case 2:
00356 //if(n!=selected2)
00357       {
00358         selected2 = n;
00359         selected3 = NULL;
00360         populate_sublist(list3, selected2, selected3);
00361       }
00362       break;
00363     case 3:
00364 //if(n!=selected3)
00365       {
00366         selected3 = n;
00367         if(n->countChildren()>0)
00368         {
00369           hwNode *oldselected1 = selected1;
00370           selected1 = selected2;
00371           selected2 = n;
00372           selected3 = NULL;
00373           populate_sublist(list1, oldselected1, selected1);
00374           populate_sublist(list2, selected1, selected2);
00375           populate_sublist(list3, selected2, selected3);
00376         }
00377       }
00378       break;
00379   }
00380 
00381   if(selected1 && (find_parent(selected1, &container)!= &container))
00382     gtk_widget_set_sensitive(go_up_button, 1);
00383   else
00384     gtk_widget_set_sensitive(go_up_button, 0);
00385 
00386   (void) &::id;                                   // avoid warning "id defined but not used"
00387 }
00388 
00389 
00390 void go_back(GtkWidget *mainwindow)
00391 {
00392   if(selected1 && (find_parent(selected1, &container)!= &container))
00393   {
00394     selected3 = selected2;
00395     selected2 = selected1;
00396     selected1 = find_parent(selected1, &container);
00397     if(selected1 == &container) selected1 = container.getChild(0);
00398     populate_sublist(list1, find_parent(selected1, &container), selected1);
00399     populate_sublist(list2, selected1, selected2);
00400     populate_sublist(list3, selected2, selected3);
00401 
00402     if(find_parent(displayed, &container)!= &container)
00403       displayed = find_parent(displayed, &container);
00404   }
00405 
00406   if(selected1 && (find_parent(selected1, &container)!= &container))
00407     gtk_widget_set_sensitive(go_up_button, 1);
00408   else
00409     gtk_widget_set_sensitive(go_up_button, 0);
00410 
00411   display(mainwindow);
00412 }
00413 
00414 static const char *guess_format(char *s)
00415 {
00416   char *dot = strrchr(s, '.');
00417 
00418   if(!dot)
00419     return LSHW_XML;
00420 
00421   if(!strcasecmp(".html", dot) || !strcasecmp(".htm", dot))
00422     return HTML;
00423 
00424   if(!strcasecmp(".text", dot) || !strcasecmp(".txt", dot))
00425     return PLAIN_TEXT;
00426 
00427   if(!strcasecmp(".json", dot))
00428     return JSON;
00429 
00430   return LSHW_XML;
00431 }
00432 
00433 static char *fix_filename(char *s, const char *extension)
00434 {
00435   char *dot = strrchr(s, '.');
00436 
00437   if(dot)
00438     return s;
00439 
00440   s = (char*)realloc(s, strlen(s) + 1 + strlen(extension) + 1);
00441   strcat(s, ".");
00442   strcat(s, extension);
00443 
00444   return s;
00445 }
00446 
00447 static void redirect_cout(std::ofstream &out, bool enable = true)
00448 {
00449   static std::streambuf* old_cout;
00450   
00451   if(enable)
00452   {
00453     old_cout = cout.rdbuf();
00454     cout.rdbuf(out.rdbuf());
00455   }
00456   else
00457     cout.rdbuf(old_cout);
00458 }
00459 
00460 void save_as(GtkWidget *mainwindow)
00461 {
00462   struct utsname buf;
00463   GtkWidget *dialog = NULL;
00464   GtkWidget *sanitize = NULL;
00465   GtkFileFilter *filter = NULL;
00466   bool proceed = true;
00467   hwNode *computer = container.getChild(0);
00468 
00469   if(!computer)             // nothing to save
00470     return;
00471 
00472   dialog = gtk_file_chooser_dialog_new ("Save hardware configuration",
00473                                   GTK_WINDOW(mainwindow),
00474                                   GTK_FILE_CHOOSER_ACTION_SAVE,
00475                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
00476                                   GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
00477                                   NULL);
00478   //gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
00479   sanitize = gtk_check_button_new_with_label("Anonymize output");
00480   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sanitize), enabled("output:sanitize")?TRUE:FALSE);
00481   gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), sanitize);
00482 
00483   filter = gtk_file_filter_new ();
00484   gtk_file_filter_set_name(filter, AUTOMATIC);
00485   gtk_file_filter_add_pattern(filter, "*");
00486   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
00487   filter = gtk_file_filter_new ();
00488   gtk_file_filter_set_name(filter, LSHW_XML);
00489   gtk_file_filter_add_pattern(filter, "*.lshw");
00490   gtk_file_filter_add_pattern(filter, "*.xml");
00491   gtk_file_filter_add_mime_type(filter, "text/xml");
00492   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
00493   filter = gtk_file_filter_new ();
00494   gtk_file_filter_set_name(filter, HTML);
00495   gtk_file_filter_add_pattern(filter, "*.html");
00496   gtk_file_filter_add_pattern(filter, "*.htm");
00497   gtk_file_filter_add_mime_type(filter, "text/html");
00498   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
00499   filter = gtk_file_filter_new ();
00500   gtk_file_filter_set_name(filter, PLAIN_TEXT);
00501   gtk_file_filter_add_pattern(filter, "*.text");
00502   gtk_file_filter_add_pattern(filter, "*.txt");
00503   gtk_file_filter_add_mime_type(filter, "text/plain");
00504   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
00505   filter = gtk_file_filter_new ();
00506   gtk_file_filter_set_name(filter, JSON);
00507   gtk_file_filter_add_pattern(filter, "*.json");
00508   gtk_file_filter_add_mime_type(filter, "application/json");
00509   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
00510 
00511   if(uname(&buf)==0)
00512     gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), buf.nodename);
00513 
00514   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
00515   {
00516     char *filename;
00517 
00518     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sanitize)))
00519       enable("output:sanitize");
00520     else
00521       disable("output:sanitize");
00522 
00523     filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
00524     filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog));
00525     if(filename && filter)
00526     {
00527       const gchar *filtername = gtk_file_filter_get_name(filter);
00528 
00529       if(strcmp(filtername, AUTOMATIC)==0)
00530         filtername = guess_format(filename);
00531 
00532       if(!exists(filename))        // creating a new file
00533       {
00534         if(strcmp(filtername, LSHW_XML)==0)
00535           filename = fix_filename(filename, "lshw");
00536         else
00537         if(strcmp(filtername, HTML)==0)
00538           filename = fix_filename(filename, "html");
00539         else
00540         if(strcmp(filtername, PLAIN_TEXT)==0)
00541           filename = fix_filename(filename, "txt");
00542         else
00543         if(strcmp(filtername, JSON)==0)
00544           filename = fix_filename(filename, "json");
00545       }
00546 
00547       if(exists(filename))         // existing file
00548       {
00549         char * buffer1 = g_strdup(filename);
00550         char * buffer2 = g_strdup(filename);
00551 
00552         GtkWidget *dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW(mainwindow),
00553                                   GTK_DIALOG_DESTROY_WITH_PARENT,
00554                                   GTK_MESSAGE_WARNING,
00555                                   GTK_BUTTONS_NONE,
00556                                   "A file named <i><tt>%s</tt></i> already exists in folder <tt>%s</tt>.\n\nDo you want to overwrite it?",
00557                                   basename(buffer1), dirname(buffer2));
00558         gtk_dialog_add_buttons(GTK_DIALOG(dialog), 
00559                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
00560                               "Overwrite", GTK_RESPONSE_ACCEPT,
00561                                   NULL);
00562         proceed = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT);
00563         gtk_widget_destroy (dialog);
00564         g_free(buffer1);
00565         g_free(buffer2);
00566       }
00567 
00568       if(proceed)
00569       {
00570         if(strcmp(filtername, LSHW_XML)==0)
00571         {
00572           std::ofstream out(filename);
00573           redirect_cout(out);
00574           cout << computer->asXML();
00575           redirect_cout(out, false);
00576         }
00577         else
00578         if(strcmp(filtername, HTML)==0)
00579         {
00580           std::ofstream out(filename);
00581           redirect_cout(out);
00582           print(*computer, true);
00583           redirect_cout(out, false);
00584         }
00585         else
00586         if(strcmp(filtername, PLAIN_TEXT)==0)
00587         {
00588           std::ofstream out(filename);
00589           redirect_cout(out);
00590           print(*computer, false);
00591           redirect_cout(out, false);
00592         }
00593        else
00594         if(strcmp(filtername, JSON)==0)
00595         {
00596           std::ofstream out(filename);
00597           redirect_cout(out);
00598           cout << computer->asJSON() << endl;
00599           redirect_cout(out, false);
00600         }
00601       }
00602       g_free (filename);
00603     }
00604   }
00605 
00606   gtk_widget_destroy (dialog);
00607 }