diff -u'Nrpk~' menu-2.1.35~/update-menus/common.h menu-2.1.35/update-menus/common.h --- menu-2.1.35~/update-menus/common.h 2007-08-29 14:52:41 +0400 +++ menu-2.1.35/update-menus/common.h 2007-08-29 14:53:10 +0400 @@ -32,6 +32,9 @@ const int MAX_LINE = 10240; #define PACKAGEMENUS "/usr/share/menu/" #define MENUMENUS "/usr/share/menu/default/" #define USERMENUS ".menu/" +#define XDGMENUS "/usr/share/applications/" +#define XDGMENUS_GNOME "/usr/share/applications/gnome/" +#define XDGMENUS_KDE "/usr/share/applications/kde/" #define MENUMETHODS "/etc/menu-methods/" #define USERMETHODS ".menu-methods/" diff -u'Nrpk~' menu-2.1.35~/update-menus/Makefile.am menu-2.1.35/update-menus/Makefile.am --- menu-2.1.35~/update-menus/Makefile.am 2004-01-29 12:34:09 +0300 +++ menu-2.1.35/update-menus/Makefile.am 2007-08-29 14:53:10 +0400 @@ -5,4 +5,6 @@ update_menus_SOURCES = update-menus.cc u parsestream.cc parsestream.h \ exceptions.h \ common.h \ + xdg_categories_translator.cc \ + xdg_categories_translator.h \ compose.hpp diff -u'Nrpk~' menu-2.1.35~/update-menus/update-menus.cc menu-2.1.35/update-menus/update-menus.cc --- menu-2.1.35~/update-menus/update-menus.cc 2007-08-29 14:52:41 +0400 +++ menu-2.1.35/update-menus/update-menus.cc 2007-07-30 18:49:48 +0400 @@ -45,6 +45,7 @@ #include "config.h" #include "update-menus.h" #include "stringtoolbox.h" +#include "xdg_categories_translator.h" using std::set; using std::vector; @@ -58,6 +59,8 @@ static const char * home_dir; set installed_packages; set menufiles_processed; +set desktopfiles_processed; +XdgCtgTranslator xdg_ctg_trans; int total_menuentries; int verbose=0; @@ -178,6 +181,7 @@ void menuentry::check_req_tags(const std throw missing_tag(filename, *i); } + data["categories"] = xdg_ctg_trans.translate_section((data.find(SECTION_VAR))->second); } /** Parse a menuentry from a parsestream */ @@ -468,6 +472,269 @@ void read_pkginfo() pclose(status); } +/** Read a XDG desktopfile and create one menu entry for it. + * + * Returns 0 or 1 of menu entries read. */ +int read_desktopfile(const string &filename, const string &shortfilename, + vector &menudata) +{ + FILE *desktop_file = fopen(filename.c_str(), "r"); + if (desktop_file == NULL) return 0; + + { + // lookup Desktop Entry + bool desktop_entry_found = false; + char firstline[MAX_LINE]; + while (!feof(desktop_file)) + { + if (fgets(firstline, MAX_LINE, desktop_file) == NULL) + { + pclose(desktop_file); + return 0; + } + if (strcmp(firstline, "[Desktop Entry]\n") == 0) + { + desktop_entry_found = true; + break; + } + } + if (!desktop_entry_found) + { + pclose(desktop_file); + return 0; + } + } +// cerr << __FUNCTION__ << " read " << filename << std::endl; + + bool have_section = false; + bool have_title = false; + std::string d_onlyshowin; + std::string d_terminal; + std::string d_extra; + std::string menu_line; + + // create menu entry start + menu_line += "package=\"local.xdg_desktop_files\" xdg_desktopfile=\"true\""; +// int n_line = 2; + while (!feof(desktop_file)) + { + std::string line; + char tmp[MAX_LINE]; + if( !fgets(tmp, MAX_LINE, desktop_file) ) continue; + int len = strlen(tmp); + + // ignore empty lines + if ( len <= 1) continue; + + // quote + for(int i=0; i < MAX_LINE; i++) + { + if( tmp[i] == '\"' ) + line += "\\\""; + else if( tmp[i] == '\n' || tmp[i] == '\0' ) + break; + else + line += tmp[i]; + } + +// if( tmp[len-1] == '\n' ) +// line = std::string(tmp, 0, len-1); +// else +// line = tmp; +// cerr << " line " << n_line++ << ":" << line << std::endl; + + int sz = line.size(); + + if( line.compare(0, 5, "Name=") == 0 ) + { + // take title +// cerr << __FUNCTION__ << " take title" << std::endl; + if(sz > 5) + { + menu_line += " title=\""+line.substr(5, sz-5)+"\""; + have_title = true; + } + } + else if( line.compare(0, 11, "Categories=") == 0 ) + { + // take section +// cerr << __FUNCTION__ << " take section" << std::endl; + if(sz > 11) + { + std::string categories( line.substr(11, sz-11) ); + if( categories.size() > 0 ) + { + std::string section = xdg_ctg_trans.translate(categories); + if( section.size() > 0 ) + { + menu_line += " section=\""+section+"\""; + have_section = true; + } + } + } + } + else if( line.compare(0, 8, "Comment=") == 0 ) + { + // take longtitle + if(sz > 8) + { + menu_line += " longtitle=\""+line.substr(8, sz-8)+"\""; + } + } + else if( line.compare(0, 5, "Icon=") == 0 ) + { + // take icon + if(sz > 5) + { + string icon = line.substr(5, sz-5); + int icon_sz = sz-5; + if( icon_sz > 4 + && icon.substr(icon_sz-4,1) != "." ) + { + icon += ".png"; + } + else if( icon_sz > 0 && icon_sz <= 4 ) + { + icon += ".png"; + } + menu_line += " icon=\""+icon+"\""; + } + } + else if( line.compare(0, 9, "MimeType=") == 0 ) + { + // take mimetypes + // FIXME? replace ; by , + if(sz > 9) + { + menu_line += " mimetypes=\""+line.substr(9, sz-9)+"\""; + } + } + else if( line.compare(0, 5, "Exec=") == 0 ) + { + // take command && extra_command + if(sz > 5) + { + menu_line += " extra_command=\""+line.substr(5, sz-5)+"\""; + // cleanup for command + int sps_pos = line.find(' '); + int cur_pos = line.find('%'); + if( cur_pos > 5 && sps_pos >= 5) + { + cur_pos = line.rfind(' ', cur_pos - 1); + if( cur_pos > 5 ) + { + sps_pos = cur_pos; + cur_pos = line.rfind(' ', cur_pos - 1); + if( cur_pos > 5 && line[cur_pos+1] == '-' ) + sps_pos = cur_pos; + } + } + else + sps_pos = sz - 1; + while( line[sps_pos] == ' ' ) + sps_pos--; + menu_line += " command=\""+line.substr(5, sps_pos+1-5)+"\""; + } + } + else if( line.compare(0, 12, "GenericName=") == 0 ) + { + if(sz > 12) + { + menu_line += " genericname=\""+line.substr(12, sz-12)+"\""; + } + } + else if( line.compare(0, 9, "Terminal=") == 0 ) + { + // fill d_terminal + if(sz > 9) + { + d_terminal = line.substr(9, sz-9); + } + } + else if( line.compare(0, 11, "OnlyShowIn=") == 0 ) + { + // fill d_onlyshowin + if(sz > 11) + { + int start = sz - 3; + int end = sz - 1 ; + while( line[start] != ';' && line[start] != '=' && start >= 0 ) + start--; + start++; + end = start; + while( line[end] != ';' && line[end] != '\n' && end < sz ) + end++; + std::string onlyshowin(line.substr(start, end-start)); + d_onlyshowin = onlyshowin; + } + } + else if( line.compare(0, 5, "Name[") == 0 + || line.compare(0, 8, "Comment[") == 0 + || line.compare(0, 12, "GenericName[") == 0 ) + { + xdg_ctg_trans.addI18n(line); + } + else if(line == "NoDisplay=true") + { + pclose(desktop_file); + return 0; + } + else if( line.compare(0, 9, "Encoding=") != 0 + && line.compare(0, 5, "Type=") != 0 ) + { + // fill extra_opt + d_extra += line+"\\n"; + } + } +// cerr << __FUNCTION__ << " end while" << std::endl; + + // skip wrong entries + if ( !have_section || !have_title ) + { +// cerr << __FUNCTION__ << " !have_section || !have_title " << filename << std::endl; + pclose(desktop_file); + return 0; + } + + // take needs + if( d_terminal.size() > 0 || d_onlyshowin.size() > 0 ) + { + if( d_terminal == "1" || d_terminal == "true" ) + menu_line += " needs=\"text\""; + else if( d_onlyshowin.size() > 0 ) + menu_line += " needs=\""+d_onlyshowin+"\""; + else + menu_line += " needs=\"X11\""; + } + else + menu_line += " needs=\"X11\""; + + // take extra_filename + { + int slash_pos = shortfilename.rfind("/"); + if( slash_pos < 0 ) + slash_pos = 0; + else + slash_pos += 1; + int dot_pos = shortfilename.rfind("."); + if( dot_pos <= slash_pos ) + dot_pos = shortfilename.size()-1; + menu_line += " extra_filename=\""+shortfilename.substr(slash_pos, dot_pos)+"\""; + } + + // add extra_opt + if( d_extra.size() > 0 ) + menu_line += " extra_opt=\""+d_extra+"\""; + +// cerr << __FUNCTION__ << " add " << filename << std::endl; + menudata.push_back(string("!F ") + filename + '\n'); + + menudata.push_back(menu_line + '\n'); + + pclose(desktop_file); + return 1; +} + /** Read a menufile and create one (or more) menu entries for it. * * Returns the number of menu entries read. */ @@ -596,6 +863,45 @@ void read_menufilesdir(vector &m } } +/** Read a XDG directory full of desktop files */ +void read_desktopfilesdir(vector &menudata) +{ + int menuentries = 0; + for(vector::const_iterator method_i = config.desktopfilesdir.begin(); + method_i != config.desktopfilesdir.end(); + ++method_i) + { + string dirname = *method_i; + config.report(String::compose(_("Reading menu-entry files in %1."), dirname), + configinfo::report_verbose); + try { + struct dirent *entry; + DIR *dir = open_dir_check(dirname); + while((entry = readdir(dir))) + { + string name = entry->d_name; + if (name.find(".desktop") == name.length()-8 && + desktopfiles_processed.find(name) == desktopfiles_processed.end()) + { + desktopfiles_processed.insert(name); + name = dirname+name; + struct stat st; + int r = stat(name.c_str(),&st); + try { + if ((!r) && (S_ISREG(st.st_mode)||S_ISLNK(st.st_mode)) && (st.st_size < 30720)) + menuentries += read_desktopfile(name,entry->d_name, menudata); + } + catch (endofline p) { + cerr << String::compose(_("Error reading %1.\n"), name); + } + } + } + } catch (dir_error_read p) { } + total_menuentries += menuentries; + config.report(String::compose(_("%1 menu entries found (%2 total)."), menuentries, total_menuentries), configinfo::report_verbose); + } +} + /** Run a menu-method with --remove*/ void run_menumethod_remove(string methodname) { @@ -1092,7 +1398,12 @@ int main (int argc, char **argv) config.menufilesdir.push_back(MENUMENUS); } + config.desktopfilesdir.push_back(XDGMENUS); + config.desktopfilesdir.push_back(XDGMENUS_KDE); + //config.desktopfilesdir.push_back(XDGMENUS_GNOME); + read_menufilesdir(menudata); + read_desktopfilesdir(menudata); if (config.onlyoutput_to_stdout) { for(vector::const_iterator i = menudata.begin(); i != menudata.end(); ++i) diff -u'Nrpk~' menu-2.1.35~/update-menus/update-menus.h menu-2.1.35/update-menus/update-menus.h --- menu-2.1.35~/update-menus/update-menus.h 2007-06-06 13:39:41 +0400 +++ menu-2.1.35/update-menus/update-menus.h 2007-08-29 14:53:10 +0400 @@ -122,6 +122,7 @@ public: typedef enum { report_quiet, report_normal, report_verbose, report_debug} verbosity_type; parsestream::eol_type compat; std::vector menufilesdir; + std::vector desktopfilesdir; std::string menumethod; bool usedefaultmenufilesdirs; bool onlyoutput_to_stdout; diff -u'Nrpk~' menu-2.1.35~/update-menus/xdg_categories_translator.cc menu-2.1.35/update-menus/xdg_categories_translator.cc --- menu-2.1.35~/update-menus/xdg_categories_translator.cc 1970-01-01 03:00:00 +0300 +++ menu-2.1.35/update-menus/xdg_categories_translator.cc 2007-07-20 20:54:13 +0400 @@ -0,0 +1,179 @@ + +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "xdg_categories_translator.h" + +XdgCtgTranslator::XdgCtgTranslator() +{ + load(MENUMETHODS"/translate_xdg_categories"); + load(MENUMETHODS"/translate_menu_sections", true); +} + +XdgCtgTranslator::~XdgCtgTranslator(){} + +void XdgCtgTranslator::load(const std::string &translation_file, bool is_sections) +{ + FILE *transfile = fopen(translation_file.c_str(), "r"); + if(!transfile) return; + while(!feof(transfile)) + { + char tmp[MAX_LINE]; + std::string line; + struct XdgCtgCondition condition; + + if( !fgets(tmp, MAX_LINE, transfile) ) continue; + if( tmp[0] == '#' ) + continue; + int len = strlen(tmp); + if( len < 5 ) + continue; + if( tmp[len-1] == '\n' ) + line = std::string(tmp, 0, len-1); + else + line = tmp; + + // take section name + int eq_pos = line.find('='); + if( eq_pos < 2 ) + continue; + condition.section = std::string(line, 0, eq_pos); + line = std::string(line, eq_pos+1, line.size()-eq_pos-1); + //std::cerr << "section<" << condition.section << "> line<" << line << ">" << std::endl; + + if(is_sections) + { + section_to_category_map[condition.section] = line; + } + else + { + // take conditions + std::list condlist = split(line, ';'); + for(std::list::iterator it = condlist.begin(); it != condlist.end(); ++it) + { + std::string subcondition = *it; + if( subcondition[0] == '!' ) + { + condition.conflicts.push_back(subcondition.substr(1, subcondition.size()-1)); + //std::cerr << " conflict<" << subcondition.substr(1, subcondition.size()-1) << ">" << std::endl; + } + else + { + condition.requires.push_back(subcondition); + //std::cerr << " require<" << subcondition << ">" << std::endl; + } + } + // add condition to list + if( condition.requires.size() > 0 || condition.conflicts.size() > 0 ) + { + conditions.push_back(condition); + } + } + } +} + +std::string XdgCtgTranslator::translate(const std::string &categories) +{ + std::string section; + std::list ctglist = split(categories, ';'); + for(std::list::iterator cond_it = conditions.begin(); cond_it != conditions.end(); ++cond_it) + { + XdgCtgCondition cond = *cond_it; +// find conflicts + unsigned int conflicts_found = 0; + for(std::list::iterator ctg_it = ctglist.begin(); ctg_it != ctglist.end(); ++ctg_it) + { + std::string category = *ctg_it; + for(std::list::iterator conflict_it = cond.conflicts.begin(); + conflict_it != cond.conflicts.end(); ++conflict_it) + { + if( category == *conflict_it ) + { + conflicts_found++; + break; + } + } + } +// find requires + unsigned int requires_found = 0; + for(std::list::iterator ctg_it = ctglist.begin(); ctg_it != ctglist.end(); ++ctg_it) + { + std::string category = *ctg_it; + for(std::list::iterator require_it = cond.requires.begin(); + require_it != cond.requires.end(); ++require_it) + { + if( category == *require_it ) + { + requires_found++; + break; + } + } + } + if( cond.requires.size() == requires_found && cond.conflicts.size() == conflicts_found ) + { + section = cond.section; + break; + } + } + return section; +} + +std::string XdgCtgTranslator::translate_section(const std::string §ion) +{ + std::string categories = section_to_category_map[section]; + if( categories.size() <= 0 ) + { + categories = "UnknownCategory;"; + } + return categories; +} + +std::list XdgCtgTranslator::split(std::string textline, const char separator) +{ + std::list newlist; + for(std::string::iterator it = textline.begin(); it != textline.end(); ++it) + { + std::string substring; + while( it != textline.end() && *it != separator ) + { + substring += *it; + ++it; + } + if( it == textline.end() ) --it; + if( substring.size() > 0 ) + newlist.push_back(substring); + } + return newlist; +} + +void XdgCtgTranslator::saveI18n() +{ + const char* home_dir; + struct passwd *pwentry = getpwuid(getuid()); + if (pwentry != NULL) + home_dir = pwentry->pw_dir; + else + home_dir = getenv("HOME"); + + std::ofstream i18n_file((std::string(home_dir)+"/.menu-updates.i18n").c_str(), std::ios::trunc); + + for(std::map >::iterator it = i18n.begin(); it != i18n.end(); ++it) + { + std::string lng = (*it).first; + std::map lng_strings = (*it).second; + for(std::map::iterator it2 = lng_strings.begin(); it2 != lng_strings.end(); ++it2) + { + i18n_file << lng << "|" << ((*it2).first).size() << "|" << (*it2).first << (*it2).second << std::endl; + } + } +} + +void XdgCtgTranslator::addI18n(const std::string &str) +{ +} diff -u'Nrpk~' menu-2.1.35~/update-menus/xdg_categories_translator.h menu-2.1.35/update-menus/xdg_categories_translator.h --- menu-2.1.35~/update-menus/xdg_categories_translator.h 1970-01-01 03:00:00 +0300 +++ menu-2.1.35/update-menus/xdg_categories_translator.h 2007-07-20 20:52:58 +0400 @@ -0,0 +1,35 @@ +#ifndef __XDG_CATEGORIES_TRANSLATOR_H +#define __XDG_CATEGORIES_TRANSLATOR_H + +#include +#include +#include + +struct XdgCtgCondition +{ + std::string section; + std::list requires; + std::list conflicts; +}; + +class XdgCtgTranslator +{ +public: + XdgCtgTranslator(); + ~XdgCtgTranslator(); + + std::string translate(const std::string &categories); + std::string translate_section(const std::string §ion); + void addI18n(const std::string&); + void saveI18n(); + static std::list split(std::string textline, const char separator); + +private: + std::list conditions; + std::map section_to_category_map; + std::map > i18n; + + void load(const std::string &translation_file, bool is_sections = false); +}; + +#endif