3
Witten by joost witteveen;
4
read_pkginfo function by Tom Lees, run_menumethods by both.
2
* Debian menu system -- update-menus
3
* update-menus/update-menus.cc
5
* Copyright (C) 1996-2003 Joost Witteveen,
6
* Copyright (C) 2002-2004 Bill Allombert and Morten Brix Pedersen.
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License with
19
* the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
20
* if not, write to the Free Software Foundation, Inc., 59 Temple Place,
21
* Suite 330, Boston, MA 02111-1307 USA
24
* Written by Joost Witteveen;
25
* read_pkginfo function by Tom Lees, run_menumethods by both.
8
#include "update-menus.h"
13
32
#include <unistd.h>
15
36
#include <sys/stat.h>
16
37
#include <sys/types.h>
17
38
#include <sys/wait.h>
18
39
#include <sys/file.h>
20
40
#include <dirent.h>
21
41
#include <signal.h>
22
42
#include <syslog.h>
26
int debug=0, verbose=0;
27
set <String, less<String> > installed_packages;
28
set <String, less<String> > menufiles_processed;
30
extern char **environ;
45
#include "update-menus.h"
46
#include "stringtoolbox.h"
54
using namespace exceptions;
56
static const char * home_dir;
58
set<string> installed_packages;
59
set<string> menufiles_processed;
60
int total_menuentries;
33
62
translateinfo *transinfo;
36
DIR *open_dir_check(String dirname){
66
/** Try to open a directory. Throws dir_error_read if failed, and a DIR*
67
* descriptor if succeeded.
69
DIR *open_dir_check(const string& dirname)
40
r=stat (dirname.c_str(), &st);
41
if (r || (!S_ISDIR (st.st_mode)))
42
throw dir_error_read(dirname);
44
return opendir (dirname.c_str());
73
if (stat(dirname.c_str(), &st) || (!S_ISDIR (st.st_mode)))
74
throw dir_error_read();
76
return opendir(dirname.c_str());
47
bool executable(const String &s){
79
/** Checks whether a file is executable */
80
bool executable(const string &s)
50
r=stat (s.c_str(), &st);
83
if (stat(s.c_str(), &st)) {
54
return (((st.st_mode & S_IXOTH) ||
55
((st.st_mode & S_IXGRP) && st.st_gid == getegid ()) ||
56
((st.st_mode & S_IXUSR) && st.st_uid == geteuid ())) &&
57
(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
60
/////////////////////////////////////////////////////
64
/*void gettext_translate(menuentry &m){
70
t=String(dgettext(GETTEXTDOMAIN,m.data[TITLE_VAR].c_str()));
73
t=m.data[SECTION_VAR];
76
for(i=v.begin(); i!=v.end(); i++){
79
t=t+String(dgettext(GETTEXTDOMAIN,(*i).c_str()));
86
return (((st.st_mode & S_IXOTH) ||
87
((st.st_mode & S_IXGRP) && st.st_gid == getegid ()) ||
88
((st.st_mode & S_IXUSR) && st.st_uid == geteuid ())) &&
89
(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
81
m.data[SECTION_VAR]=t;
84
bool menuentry::test_installed(String filename){
86
if(filename.contains("local.",0))
93
/** Checks whether a package is installed */
94
bool is_pkg_installed(const string& filename)
96
if (contains(filename, "local."))
89
return installed_packages.find(filename)!=installed_packages.end();
92
bool menuentry::check_install(parsestream &i, String &name){
95
function=i.get_name();
96
if(function!=String(COND_PACKAGE))
99
return installed_packages.find(filename) != installed_packages.end();
102
menuentry::menuentry(parsestream &i, const std::string& file, const std::string& shortfile)
104
char c = i.get_char();
108
// old format menuentry
109
if (!is_pkg_installed(shortfile)) {
111
throw cond_inst_false();
113
data[PACKAGE_VAR] = shortfile;
115
data[NEEDS_VAR] = i.get_stringconst();
116
data[SECTION_VAR] = i.get_stringconst();
117
i.get_stringconst(); //id is unused
118
data[ICON_VAR] = i.get_stringconst();
119
data[TITLE_VAR] = i.get_stringconst();
121
data[COMMAND_VAR] = i.get_line();
123
check_req_tags(file);
126
/** This function checks the package name to see if it's valid and installed.
127
* Multiple package names can exist, seperated by comma. */
128
void menuentry::check_pkg_validity(parsestream &i, std::string &name)
130
string function = i.get_name();
131
if (function != COND_PACKAGE)
97
132
throw unknown_cond_package(&i, function);
100
if(!test_installed(name)){
102
throw cond_inst_false();
134
// Read an entry, such as (foo) or (foo, bar)
136
while ((c = i.get_char()))
138
if (c == ',' || c == '(') {
139
// A package name follows.
140
string pkgname = i.get_name();
146
if (!is_pkg_installed(pkgname)) {
148
throw cond_inst_false();
150
} else if (c == ')') {
151
// We are finished with the package requirements.
104
155
i.skip_char(')');
108
void menuentry::menuentry_constr_gccbug(parsestream &i)
112
//new format menuentry
114
//read available info
115
check_install(i, name);
116
data[PACKAGE_VAR]=name;
158
/** Checks whether we have all the tags we need. */
159
void menuentry::check_req_tags(const std::string& filename)
163
need.push_back(SECTION_VAR);
164
need.push_back(TITLE_VAR);
165
need.push_back(NEEDS_VAR);
167
for(vector<string>::iterator i = need.begin(); i != need.end(); ++i)
169
std::map<string, string>::const_iterator j = data.find(*i);
170
if ((j == data.end()) || j->second.empty())
171
throw missing_tag(filename, *i);
176
/** Parse a menuentry from a parsestream */
177
void menuentry::read_menuentry(parsestream &i)
179
// a new format menuentry
182
// read available info
183
check_pkg_validity(i, name);
184
data[PACKAGE_VAR] = name;
118
186
i.skip_char(':');
123
val=i.get_eq_Stringconst();
187
i.doescaping = false;
193
string key = i.get_name();
194
string value = i.get_eq_stringconst();
200
catch (endofline) { }
133
menuentry::menuentry(parsestream &i, const String &filename){
138
menuentry_constr_gccbug(i);
140
//old format menuentry
141
if(!test_installed(filename)){
143
throw cond_inst_false();
145
data[PACKAGE_VAR]=filename;
147
data[NEEDS_VAR] =i.get_Stringconst();
148
data[SECTION_VAR]=i.get_Stringconst();
149
i.get_Stringconst(); //id is unused
150
data[ICON_VAR] =i.get_Stringconst();
151
data[TITLE_VAR] =i.get_Stringconst();
153
data[COMMAND_VAR]=i.get_line();
157
void menuentry::output(vector<String> &s){
159
map<String, String, less<String> >::iterator i;
160
if((i=data.begin())!=data.end())
162
t+=String((*i).first) + "=\"" +
163
escape_String((*i).second,"\"\n") + "\"";
204
void menuentry::output(std::vector<std::string> &s)
207
std::map<string, string>::const_iterator i = data.begin();
208
if (i != data.end()) {
211
t += i->first + "=\"" + i->second + '"';
171
config.report(String("ADDING: ")+t,configinfo::report_debug);
175
ostream &menuentry::debugoutput(ostream &o){
176
map<String, String, less<String> >::iterator i;
178
o<<"MENUENTRY:"<<endl;
179
for(i=data.begin(); i!=data.end(); i++)
180
o<<" data["<<(*i).first<<"]="<<(*i).second<<endl;
184
223
/////////////////////////////////////////////////////
187
configinfo::configinfo(){
188
verbosity=report_quiet;
189
method=method_stderr;
190
compat=parsestream::eol_newline;
191
usedefaultmenufilesdirs=true;
194
void configinfo::parse_def(const String &var, parsestream &p){
196
s=p.get_Stringconst();
197
if(var==String("compat")){
198
if(s==String("menu-1")) compat=parsestream::eol_newline;
199
else if(s==String("menu-2")) compat=parsestream::eol_semicolon;
200
else throw def_error(&p, s);
201
} else if(var==String("verbosity")){
202
if (s==String("quiet")) verbosity=report_quiet;
203
else if(s==String("normal")) verbosity=report_normal;
204
else if(s==String("verbose")) verbosity=report_verbose;
205
else if(s==String("debug")) verbosity=report_debug;
206
else throw def_error(&p, s);
207
} else if(var==String("method")){
208
if (s==String("stdout")) method=method_stdout;
209
else if(s==String("stderr")) method=method_stderr;
210
else if(s==String("syslog")) {
211
method=method_syslog;
214
facility=p.get_Stringconst();
215
cerr<<"FACILITY="<<facility<<endl;
217
priority=p.get_Stringconst();
218
cerr<<"priority="<<priority<<endl;
219
if(facility==String("auth")) syslog_facility=LOG_AUTH;
220
else if(facility==String("authpriv")) syslog_facility=LOG_AUTHPRIV;
221
else if(facility==String("authcron")) syslog_facility=LOG_CRON;
222
else if(facility==String("authdaemon")) syslog_facility=LOG_AUTHPRIV;
223
else if(facility==String("authkern")) syslog_facility=LOG_KERN;
224
else if(facility==String("authlocal0")) syslog_facility=LOG_LOCAL0;
225
else if(facility==String("authlocal1")) syslog_facility=LOG_LOCAL1;
226
else if(facility==String("authlocal2")) syslog_facility=LOG_LOCAL2;
227
else if(facility==String("authlocal3")) syslog_facility=LOG_LOCAL3;
228
else if(facility==String("authlocal4")) syslog_facility=LOG_LOCAL4;
229
else if(facility==String("authlocal5")) syslog_facility=LOG_LOCAL5;
230
else if(facility==String("authlocal6")) syslog_facility=LOG_LOCAL6;
231
else if(facility==String("authlocal7")) syslog_facility=LOG_LOCAL7;
232
else if(facility==String("authlpr")) syslog_facility=LOG_LPR;
233
else if(facility==String("authmail")) syslog_facility=LOG_MAIL;
234
else if(facility==String("authnews")) syslog_facility=LOG_NEWS;
235
else if(facility==String("authsyslog")) syslog_facility=LOG_SYSLOG;
236
else if(facility==String("authuser")) syslog_facility=LOG_USER;
237
else if(facility==String("authuucp")) syslog_facility=LOG_UUCP;
238
else throw def_error(&p, facility);
240
if(priority==String("emerg")) syslog_priority=LOG_EMERG;
241
else if(priority==String("alert")) syslog_priority=LOG_ALERT;
242
else if(priority==String("crit")) syslog_priority=LOG_CRIT;
243
else if(priority==String("err")) syslog_priority=LOG_ERR;
244
else if(priority==String("warning")) syslog_priority=LOG_WARNING;
245
else if(priority==String("notice")) syslog_priority=LOG_NOTICE;
246
else if(priority==String("info")) syslog_priority=LOG_INFO;
247
else if(priority==String("debug")) syslog_priority=LOG_DEBUG;
248
else throw def_error(&p, priority);
250
else throw def_error(&p,s);
252
else throw def_error(&p, var);
255
void configinfo::update(String filename){
257
parsestream p(filename);
269
void configinfo::report(const String &message,
227
/** Handle key/value configuration pair */
228
void configinfo::parse_config(const std::string &key, const std::string& value)
231
if (value=="menu-1") compat = parsestream::eol_newline;
232
else if(value=="menu-2") compat = parsestream::eol_semicolon;
233
} else if(key=="verbosity") {
234
if (value=="quiet") verbosity=report_quiet;
235
else if(value=="normal") verbosity=report_normal;
236
else if(value=="verbose") verbosity=report_verbose;
237
else if(value=="debug") verbosity=report_debug;
238
} else if(key=="method") {
239
if (value=="stdout") method=method_stdout;
240
else if(value=="stderr") method=method_stderr;
241
else if(value=="syslog") {
243
method = method_syslog;
247
std::istringstream ss(value);
251
if(facility=="auth") syslog_facility=LOG_AUTH;
252
else if(facility=="authpriv") syslog_facility=LOG_AUTHPRIV;
253
else if(facility=="authcron") syslog_facility=LOG_CRON;
254
else if(facility=="authdaemon") syslog_facility=LOG_AUTHPRIV;
255
else if(facility=="authkern") syslog_facility=LOG_KERN;
256
else if(facility=="authlocal0") syslog_facility=LOG_LOCAL0;
257
else if(facility=="authlocal1") syslog_facility=LOG_LOCAL1;
258
else if(facility=="authlocal2") syslog_facility=LOG_LOCAL2;
259
else if(facility=="authlocal3") syslog_facility=LOG_LOCAL3;
260
else if(facility=="authlocal4") syslog_facility=LOG_LOCAL4;
261
else if(facility=="authlocal5") syslog_facility=LOG_LOCAL5;
262
else if(facility=="authlocal6") syslog_facility=LOG_LOCAL6;
263
else if(facility=="authlocal7") syslog_facility=LOG_LOCAL7;
264
else if(facility=="authlpr") syslog_facility=LOG_LPR;
265
else if(facility=="authmail") syslog_facility=LOG_MAIL;
266
else if(facility=="authnews") syslog_facility=LOG_NEWS;
267
else if(facility=="authsyslog") syslog_facility=LOG_SYSLOG;
268
else if(facility=="authuser") syslog_facility=LOG_USER;
269
else if(facility=="authuucp") syslog_facility=LOG_UUCP;
271
if(priority=="emerg") syslog_priority=LOG_EMERG;
272
else if(priority=="alert") syslog_priority=LOG_ALERT;
273
else if(priority=="crit") syslog_priority=LOG_CRIT;
274
else if(priority=="err") syslog_priority=LOG_ERR;
275
else if(priority=="warning") syslog_priority=LOG_WARNING;
276
else if(priority=="notice") syslog_priority=LOG_NOTICE;
277
else if(priority=="info") syslog_priority=LOG_INFO;
278
else if(priority=="debug") syslog_priority=LOG_DEBUG;
283
void configinfo::read_file(const std::string& filename)
285
std::ifstream config_file(filename.c_str());
291
while (getline(config_file, str))
293
// read a key and a value, seperated by '='.
294
string::size_type pos = str.find("=");
295
if (pos == string::npos)
298
// parse key and value
299
parse_config(str.substr(0, pos), str.substr(pos + 1));
304
void configinfo::report(const std::string &message, verbosity_type v)
273
308
case method_stdout:
274
cout<<"Update-menus["<<getpid()<<"]: "<<message<<endl;
309
cout << String::compose("update-menus[%1]: %2\n",getpid(),message);
276
311
case method_stderr:
277
cerr<<"Update-menus["<<getpid()<<"]: "<<message<<endl;
312
cerr << String::compose("update-menus[%1]: %2\n",getpid(),message);
279
314
case method_syslog:
280
315
openlog("update-menus",LOG_PID,syslog_facility);
286
322
/////////////////////////////////////////////////////
287
323
// translate stuff
290
trans_class::trans_class(const String &m,
298
trans_class::~trans_class(){
302
bool trans_class::check(String &s){
303
config.report(String("checking ")+match+" < "+s,configinfo::report_debug);
304
return match.contains(s,0);
307
String trans_class::debuginfo(){
308
return String(" match=")+match+", replace="+replace+", replace_var="+replace_var;
311
void translate::process( menuentry &m,
314
m.data[replace_var]=replace;
316
void subtranslate::process( menuentry &m,
318
if(v.contains(match,0)){
319
m.data[replace_var]=replace;
323
void substitute::process( menuentry &m,
326
if(v.contains(match,0)){
327
t=&(m.data[replace_var]);
328
if(t->length()>=replace.length())
329
*t=replace+t->after(match.length());
333
void translateinfo::init(parsestream &i){
334
String name, match, replace, match_var, replace_var;
335
Regex ident("[a-zA-Z_][a-zA-Z0-9_]*");
337
config.report(String("Reading translate info in ")+i.filename(),
338
configinfo::report_verbose);
340
name=i.get_name(ident);
341
config.report(String("name=")+name,
342
configinfo::report_debug);
344
match_var=i.get_name(ident);
345
config.report(String("match_var=")+match_var,
346
configinfo::report_debug);
351
replace_var=i.get_name(ident);
352
config.report(String("replace_var=")+replace_var,
353
configinfo::report_debug);
360
match=i.get_Stringconst();
361
if(match==String(ENDTRANSLATE_TRANS)){
370
replace=i.get_Stringconst();
371
if(name==TRANSLATE_TRANS)
372
trcl=new translate(match,replace,replace_var);
373
if(name==SUBTRANSLATE_TRANS)
374
trcl=new subtranslate(match,replace,replace_var);
375
if(name==SUBSTITUTE_TRANS)
376
trcl=new substitute(match,replace,replace_var);
378
pair<const String, trans_class *> p(match,trcl);
380
config.report(String("adding translate rule: [")+p.first+
381
"]"+ trcl->debuginfo(),
382
configinfo::report_debug);
383
trans[match_var].insert(p);
389
translateinfo::translateinfo(parsestream &i){
393
translateinfo::translateinfo(const String &filename){
395
config.report(String("attempting to open ")+filename+".. ",
396
configinfo::report_debug);
397
parsestream ps(filename);
402
{config.report(String("End reading translate info"),
403
configinfo::report_debug);}
407
void translateinfo::process(menuentry &m){
408
map<String, trans_map, less<String> >::iterator i;
409
trans_map::iterator j;
411
for(i=trans.begin(); i!=trans.end(); i++){
412
match=&m.data[(*i).first];
413
j=(*i).second.lower_bound(*match);
414
if((j==(*i).second.end()) ||
415
((j!=(*i).second.begin()) && ((*j).first != *match)))
418
config.report(String("translate: var[")+*match+"]"+
419
" testing trans rule match for:"+
421
configinfo::report_debug);
422
(*j).second->process(m,*match);
424
} while((j!=(*i).second.end())&&
425
(*j).second->check(*match));
429
void translateinfo::debuginfo(){
430
map<String, trans_map, less<String> >::iterator i;
431
trans_map::iterator j;
432
for(i=trans.begin(); i!=trans.end(); i++){
433
config.report(String("TRANS: [")+(*i).first+"]",
434
configinfo::report_debug);
435
for(j=(*i).second.begin();
436
j!=(*i).second.end();
438
config.report(String("key=")+(*j).first+
439
(*j).second->debuginfo()+"\n",
440
configinfo::report_debug);
326
void translate::process(menuentry &m, const std::string &search)
329
m.data[replace_var] = replace;
332
void subtranslate::process(menuentry &m, const std::string &search)
334
if (contains(search, match))
335
m.data[replace_var] = replace;
338
void substitute::process(menuentry &m, const std::string &search)
340
if (contains(search, match)) {
341
string *current = &(m.data[replace_var]);
342
if (current->length() >= match.length())
343
*current = replace + current->substr(match.length());
347
translateinfo::translateinfo(const std::string &filename)
351
i = new parsestream(filename);
353
Regex ident("[[:alpha:]][[:alnum:]_]*");
354
/*Translation here and below refer to the file
355
/etc/menu-methods/translate_menus that allow to rename and reorganize
356
menu entries automatically. It does not refer to the localisation
357
(translation to other languages).
359
config.report(String::compose(_("Reading translation rules in %1."), i->filename()),
360
configinfo::report_verbose);
363
string name = i->get_name(ident);
365
string match_var = i->get_name(ident);
370
string replace_var = i->get_name(ident);
376
string match = i->get_stringconst();
377
if (match == ENDTRANSLATE_TRANS) {
386
string replace = i->get_stringconst();
388
if (name == TRANSLATE_TRANS) {
389
trcl = new translate(match,replace,replace_var);
390
} else if (name == SUBTRANSLATE_TRANS) {
391
trcl = new subtranslate(match,replace,replace_var);
392
} else if (name == SUBSTITUTE_TRANS) {
393
trcl = new substitute(match,replace,replace_var);
399
std::pair<const string, trans_class *> p(match, trcl);
401
trans[match_var].push_back(p);
405
} catch(endoffile p) { }
409
void translateinfo::process(menuentry &m)
411
std::map<string, std::vector<trans_pair> >::const_iterator i;
412
std::vector<trans_pair>::const_iterator j;
414
for (i = trans.begin(); i != trans.end(); ++i)
416
search = &m.data[i->first];
417
for (j = i->second.begin(); j != i->second.end(); ++j)
419
j->second->process(m, *search);
446
424
/////////////////////////////////////////////////////
447
425
// Installed Package Status:
450
void read_pkginfo (void)
455
"dpkg --get-selections|sed -n -e 's/[ \t]*\\(install\\|hold\\)$//p'";
457
status=popen(getselections,"r");
460
throw pipeerror_read(getselections);
462
config.report(String("Reading installed packages..."),
463
configinfo::report_verbose);
464
while (!feof (status)){
465
fgets (tmp, MAX_LINE, status);
466
if(tmp[strlen(tmp)-1]=='\n')
467
tmp[strlen(tmp)-1]=0;
468
installed_packages.insert(tmp);
473
void read_menufile(const String &filename,
474
const String &shortfilename,
475
vector<String> &menudata){
476
bool wrote_filename=false;
479
istream *pipe_istr=NULL;
482
config.report(String("Reading menuentryfile ")+filename,
483
configinfo::report_debug);
485
if(executable(filename)){
486
pr.open(filename.c_str(),ios::in);
487
pipe_istr=new istream(&pr);
489
i=new parsestream(*pipe_istr);
490
} catch (endoffile d){
491
cerr<<"Error (or no input available from stdout) while executing "<<endl
492
<<filename<<" . Note that it is"<<endl
493
<<"a _feature_ of menu that it executes menuentryfiles that have"<<endl
494
<<"the executable bit set. See the documentation."<<endl;
499
i=new parsestream(filename);
500
} catch(endoffile p){
506
i->seteolmode(config.compat);
508
while(true){//i->good()){
510
menuentry m(*i,shortfilename);
513
transinfo->process(m);
514
//gettext_translate(m);
516
menudata.push_back(String("!F ") + filename + "\n");
520
if(i->linenumber() != linenr){
521
menudata.push_back(String("!L ") + itoString(i->linenumber())+ "\n");
522
linenr=i->linenumber();
525
catch(cond_inst_false){}
529
if(executable(filename)){
538
void read_menufilesdir(vector<String> &menudata){
542
struct dirent *entry;
546
cStrVec::iterator method_i;
549
for(method_i=config.menufilesdir.begin();
550
method_i!=config.menufilesdir.end();
553
config.report(String("Reading menuentryfiles in ")+dirname,
554
configinfo::report_verbose);
556
dir=open_dir_check(dirname);
557
while((entry=readdir(dir))){
558
name=String(entry->d_name);
559
if((name!=String("README"))&&(name!=String("core"))&&(name[0]!='.')&&
560
(name.find(".bak")==string::npos)&&
561
(!name.contains(String("menu.config")))&&
562
(name[name.length()-1]!='~'))
563
if(menufiles_processed.find(name)==menufiles_processed.end()){
564
menufiles_processed.insert(name);
566
r=stat(name.c_str(),&st);
568
if((!r)&&(S_ISREG(st.st_mode)||S_ISLNK(st.st_mode)))
569
read_menufile(name,entry->d_name,
573
cerr << "error reading " << name << endl;
577
} catch (dir_error_read p){dummy=p.name;}
580
void run_menumethod(String methodname,
581
const vector<String> &menudata){
428
/** Read in list of installed packages */
431
// Here we get the list of *installed* packages from dpkg, using sed to
432
// retrieve the package name.
433
char *pkgs = "dpkg-query --show --showformat='${status} ${package}\\n' | sed -n -e 's/.*installed *//p'";
434
FILE *status = popen(pkgs, "r");
437
throw pipeerror_read(pkgs);
439
config.report(_("Reading installed packages list..."),
440
configinfo::report_verbose);
442
while (!feof(status))
445
if (fgets(tmp, MAX_LINE, status) != NULL) {
446
if (tmp[strlen(tmp)-1] == '\n')
447
tmp[strlen(tmp)-1] = 0;
449
installed_packages.insert(tmp);
455
/** Read a menufile and create one (or more) menu entries for it.
457
* Returns the number of menu entries read. */
458
int read_menufile(const string &filename, const string &shortfilename,
459
vector<string> &menudata)
463
std::stringstream *sstream = 0;
466
// Whenever we encounter a file which has the executable bit set, we
467
// need to execute it and read its output from stdout.
469
if (executable(filename)) {
470
FILE *status = popen(filename.c_str(), "r");
472
sstream = new std::stringstream;
475
throw pipeerror_read(filename.c_str());
477
while (!feof(status))
480
if (fgets(tmp, MAX_LINE, status) != NULL)
486
ps = new parsestream(*sstream);
487
} catch (endoffile d) {
488
cerr << String::compose(_("Execution of %1 generated no output or returned an error.\n"), filename);
492
ps = new parsestream(filename);
494
} catch (endoffile p) {
500
ps->seteolmode(config.compat);
502
bool wrote_filename = false;
507
menuentry m(*ps, filename, shortfilename);
510
transinfo->process(m);
511
//gettext_translate(m);
512
if (!wrote_filename) {
513
menudata.push_back(string("!F ") + filename + '\n');
514
wrote_filename = true;
518
if (ps->linenumber() != linenr) {
519
menudata.push_back(string("!L ") + itostring(ps->linenumber())+ '\n');
520
linenr = ps->linenumber();
523
catch (cond_inst_false) { }
526
catch (endoffile p) { }
527
catch (missing_tag& exc) {
528
std::cerr << exc.message() << std::endl;
529
std::cerr << _("Skipping file because of errors...\n");
531
catch (except_pi& exc) {
533
std::cerr << _("Skipping file because of errors...\n");
541
/** Read a directory full of menu files */
542
void read_menufilesdir(vector<string> &menudata)
545
for(vector<string>::const_iterator method_i = config.menufilesdir.begin();
546
method_i != config.menufilesdir.end();
549
string dirname = *method_i;
550
config.report(String::compose(_("Reading menu-entry files in %1."), dirname),
551
configinfo::report_verbose);
553
struct dirent *entry;
554
DIR *dir = open_dir_check(dirname);
555
while((entry = readdir(dir)))
557
string name = entry->d_name;
558
if ((name != "README") && (name != "core") && (name[0] != '.') &&
559
(name.find(".bak") == string::npos) &&
560
(!contains(name, "menu.config")) &&
561
(name[name.length()-1] != '~'))
563
if (menufiles_processed.find(name) == menufiles_processed.end()) {
564
menufiles_processed.insert(name);
567
int r = stat(name.c_str(),&st);
569
if ((!r) && (S_ISREG(st.st_mode)||S_ISLNK(st.st_mode)))
570
menuentries += read_menufile(name,entry->d_name, menudata);
572
catch (endofline p) {
573
cerr << String::compose(_("Error reading %1.\n"), name);
577
} catch (dir_error_read p) { }
578
total_menuentries += menuentries;
579
config.report(String::compose(_("%1 menu entries found (%2 total)."), menuentries, total_menuentries), configinfo::report_verbose);
583
/** Run a menu method */
584
void run_menumethod(string methodname, const vector<string> &menudata)
584
const vector<String> *md;
585
const char *args[]={methodname.c_str(), "-f", "--stdin", NULL, NULL};
587
const char *args[] = { methodname.c_str(), NULL };
591
config.report(String("Running method:")+methodname,
592
configinfo::report_verbose);
596
config.report(String("Cannot create pipe"),
597
configinfo::report_quiet);
591
config.report(String::compose(_("Running method: %1"), methodname), configinfo::report_verbose);
593
if (pipe(fds) == -1) {
594
config.report(_("Cannot create pipe."), configinfo::report_quiet);
598
if (!(child=fork())) {
607
605
// The next 2 lines seem strange! But if I leave it out,
608
606
// pipes (in commands executed in the /etc/menu-method/* scripts as
616
614
open("/dev/null", O_RDWR);
617
execve(args[0],(char **)args, environ);
615
execv(args[0],(char **)args);
621
619
signal(SIGPIPE,SIG_IGN);
623
for(i=0;i!=md->size();i++){
624
str=(*md)[i].c_str();
625
write(fds[1],str,strlen(str));
622
for(vector<string>::const_iterator i = menudata.begin(); i != menudata.end(); ++i)
623
write(fds[1], i->c_str(), i->length());
628
r=wait4(child,&status, 0, NULL);
626
r = wait4(child, &status, 0, NULL);
629
627
signal(SIGPIPE,SIG_DFL);
632
config.report(String("Script ")+methodname+" could not be executed.",
633
configinfo::report_quiet);
634
if(WEXITSTATUS(status))
635
config.report(String("Script ")+methodname+" returned error status "
636
+itoString(WEXITSTATUS(status))+".",
637
configinfo::report_quiet);
638
else if(WIFSIGNALED(status))
639
config.report(String("Script ")+methodname+" recieved signal "
640
+itoString(WTERMSIG(status))+".",
641
configinfo::report_quiet);
630
config.report(String::compose(_("Script %1 could not be executed."), methodname),
631
configinfo::report_quiet);
632
if (WEXITSTATUS(status))
633
config.report(String::compose(_("Script %1 returned error status %2."), methodname, WEXITSTATUS(status)),
634
configinfo::report_quiet);
635
else if (WIFSIGNALED(status))
636
config.report(String::compose(_("Script %1 received signal %2."), methodname, WTERMSIG(status)),
637
configinfo::report_quiet);
644
void run_menumethoddir (const String &dirname,
645
const vector<String> &menudata){
640
/** Run a directory full of menu methods */
641
void run_menumethoddir(const string &dirname, const vector<string> &menudata)
648
643
struct dirent *entry;
649
char *s, tmp[MAX_LINE];
652
config.report(String("Running menu-methods in ")+dirname,
653
configinfo::report_verbose);
654
dir=open_dir_check(dirname);
655
while ((entry = readdir (dir)) != NULL){
656
if (!strcmp(entry->d_name, "README") ||
657
!strcmp(entry->d_name, "core"))
646
config.report(String::compose(_("Running menu-methods in %1."), dirname), configinfo::report_verbose);
647
DIR *dir = open_dir_check(dirname);
648
while ((entry = readdir (dir)) != NULL) {
649
if (!strcmp(entry->d_name, "README") || !strcmp(entry->d_name, "core"))
659
for (s = entry->d_name; *s != '\0'; s++){
651
for (s = entry->d_name; *s != '\0'; s++)
660
653
if (!(isalnum (*s) || (*s == '_') || (*s == '-')))
666
sprintf (tmp, "%s/%s", dirname.c_str(), entry->d_name);
669
// Do we have execute permissions?
671
(((st.st_mode & S_IXOTH) ||
672
((st.st_mode & S_IXGRP) && st.st_gid == getegid ()) ||
673
((st.st_mode & S_IXUSR) && st.st_uid == geteuid ())) &&
674
(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))))
675
run_menumethod(tmp,menudata);
659
string method = dirname + entry->d_name;
661
if (executable(method))
662
run_menumethod(method, menudata);
667
/** Try to create a lock file for update-menus */
681
670
// return lock fd if succesful, false if unsuccesfull.
685
fd=open(UPMEN_LOCKFILE,O_WRONLY|O_CREAT,00644);
687
if(flock(fd,LOCK_EX|LOCK_NB)){
688
if(errno==EWOULDBLOCK){
689
config.report(String("Other update-menus processes are already "
690
"locking " UPMEN_LOCKFILE ", quitting."),
691
configinfo::report_verbose);
693
config.report(String("Cannot lock "UPMEN_LOCKFILE": ")+
694
strerror(errno)+ " Aborting.",
695
configinfo::report_quiet);
675
fd = open(UPMEN_LOCKFILE,O_WRONLY|O_CREAT,00644);
677
if (flock(fd,LOCK_EX|LOCK_NB)) {
678
if (errno == EWOULDBLOCK || errno == EAGAIN) {
679
config.report(String::compose(_("Other update-menus processes are already locking %1, quitting."), UPMEN_LOCKFILE),
680
configinfo::report_verbose);
682
config.report(String::compose(_("Cannot lock %1: %2 - Aborting."), UPMEN_LOCKFILE, strerror(errno)),
683
configinfo::report_quiet);
704
config.report("cannot write to lockfile "
705
UPMEN_LOCKFILE". Aborting.",
706
configinfo::report_quiet);
689
sprintf(buf, "%d", getpid());
690
if (write(fd, buf, sizeof(buf) < 1)) {
691
config.report(String::compose(_("Cannot write to lockfile %1 - Aborting."), UPMEN_LOCKFILE),
692
configinfo::report_quiet);
715
if(unlink(UPMEN_LOCKFILE))
716
config.report("Cannot remove lockfile "UPMEN_LOCKFILE,
717
configinfo::report_normal);
699
/** Try to remove update-menus lock */
703
if (unlink(UPMEN_LOCKFILE))
704
config.report(String::compose(_("Cannot remove lockfile %1."), UPMEN_LOCKFILE),
705
configinfo::report_normal);
721
int check_dpkglock(){
722
//return 1 if DPKG_LOCKFILE is locked and we should wait
723
//return 0 if we don't need to wait (not root, or no dpkg lock)
709
/** Check whether dpkg is locked
710
* return 1 if DPKG_LOCKFILE is locked and we should wait
711
* return 0 if we don't need to wait (not root, or no dpkg lock)
712
* when in doubt return 0 to avoid deadlocks.
729
config.report("update-menus run by user -- cannot determine if dpkg is "
730
"locking "DPKG_LOCKFILE": assuming there is no lock",
731
configinfo::report_verbose);
720
config.report(_("Update-menus is run by user."), configinfo::report_verbose);
735
fd=open(DPKG_LOCKFILE, O_RDWR|O_CREAT|O_TRUNC, 0660);
737
return 0; // used to be 1, but why??? (Should not happen, anyway)
739
fl.l_whence= SEEK_SET;
742
if (fcntl(fd,F_SETLK,&fl) == -1) {
724
fl.l_whence = SEEK_SET;
727
fd = open(DPKG_LOCKFILE, O_RDWR|O_CREAT|O_TRUNC, 0660);
729
/* Probably /var/lib/dpkg does not exist.
730
* Most probably dpkg is not running.
733
if (fcntl(fd,F_GETLK,&fl) == -1)
735
/* Probably /var/lib/dpkg filesystem does not support
745
if (er == EWOULDBLOCK || er == EAGAIN || er == EACCES)
747
cerr<<"update-menus: Encountered an unknown errno (="
749
<<"update-menus: Could you please be so kind as to email joostje@debian.org"<<endl
750
<<"update-menus: the errno (="<<er<<") with a discription of what you did to"<<endl
751
<<"update-menus: trigger this. Thanks very much."<<endl
752
<<"Press enter"<<endl;
753
cin.get(buf,sizeof(buf));
757
fl.l_whence= SEEK_SET;
760
if (fcntl(fd,F_SETLK,&fl) == -1){
762
cerr<<"update-menus: ?? Just locked the dpkg status database to see if another dpkg"<<endl
763
<<"update-menus: Was running. Now I cannot unlock it! Aborting"<<endl;
742
return fl.l_type!=F_UNLCK;
770
void exit_on_signal(int signr){
745
void exit_on_signal(int signr)
774
void wait_dpkg(String &stdoutfile){
750
/** Check whether dpkg is running, and fork into background waiting if it is
752
void wait_dpkg(string &stdoutfile)
792
771
// have started writing stuff to the console. To prevent that,
793
772
// I use signals: the `background' process sents a signal to the
794
773
// parent once it's written everything it wants to stdout,
795
// and only after the parent receved the signal it wil exit(0);
774
// and only after the parent received the signal it will exit(0);
775
// [Oh god! (added by Bill trying to understand the fork() business)]
797
if(check_dpkglock()){
777
if (check_dpkglock()) {
798
778
sigset_t sig,oldsig;
800
780
// Apparently libc2 on 2.0 kernels, with threading on, blocks
801
// SIGUSR1. This blocking would be inhereted by children, so
781
// SIGUSR1. This blocking would be inherited by children, so
802
782
// as apt was compiled with -lpthread, this caused problems in
803
783
// update-menus. I now get rid of that by using
804
784
// - SIGUSR2 instead of SIGUSR1,
805
785
// - sigprocmask to unblock SIGUSR2.
806
// Eighter one of those solutions should be enough, though.
786
// Either one of those solutions should be enough, though.
808
788
sigemptyset(&sig);
809
789
sigaddset(&sig,SIGUSR2);
810
790
sigprocmask(SIG_UNBLOCK,&sig,&oldsig);
812
signal(SIGUSR2,exit_on_signal);
792
signal(SIGUSR2, exit_on_signal);
813
793
parentpid=getpid();
816
perror("update-menus: fork");
794
if ((child=fork())) {
796
perror("update-menus: fork");
823
stdoutfile=String("/tmp/update-menus.")+itoString(getpid());
824
config.report("waiting for dpkg to finish (forking to background)\n"
825
"(checking " DPKG_LOCKFILE ")",
826
configinfo::report_normal);
827
config.report(String("further output (if any) will appear in ")
829
configinfo::report_normal);
830
// Close all fd's except the lock fd, for daemon mode.
835
kill(parentpid, SIGUSR2);
803
stdoutfile = string("/tmp/update-menus.")+itostring(getpid());
804
config.report(String::compose(_("Waiting for dpkg to finish (forking to background).\n"
805
"(checking %1)"), DPKG_LOCKFILE),
806
configinfo::report_normal);
807
config.report(String::compose(_("Further output (if any) will appear in %1."), stdoutfile),
808
configinfo::report_normal);
809
// Close all fd's except the lock fd, for daemon mode.
814
kill(parentpid, SIGUSR2);
837
while(check_dpkglock())
816
while(check_dpkglock())
840
// Exit without doing anything. Kill parent too!
841
kill(parentpid,SIGUSR2);
819
// Exit without doing anything. Kill parent too!
820
kill(parentpid,SIGUSR2);
829
config.report(_("Dpkg is not locking dpkg status area, good."),
830
configinfo::report_verbose);
834
/** Print usage information */
835
void usage(ostream &c)
838
_( /* This is the update-menus --help message*/
839
"Usage: update-menus [options] \n"
840
"Gather packages data from the menu database and generate menus for\n"
841
"all programs providing menu-methods, usually window-managers.\n"
842
" -d Output debugging messages.\n"
843
" -v Be verbose about what is going on.\n"
844
" -h, --help This message.\n"
845
" --menufilesdir=<dir> Add <dir> to the lists of menu directories to search.\n"
846
" --menumethod=<method> Run only the menu method <method>.\n"
847
" --nodefaultdirs Disable the use of all the standard menu directories.\n"
848
" --stdout Output menu list in format suitable for piping to\n"
850
<< _( /* This is the end of the update-menus --help message*/
851
" --version Output version information and exit.\n" );
854
struct option long_options[] = {
855
{ "debug", no_argument, NULL, 'd' },
856
{ "verbose", no_argument, NULL, 'v' },
857
{ "help", no_argument, NULL, 'h' },
858
{ "menufilesdir", required_argument, NULL, 'f'},
859
{ "menumethod", required_argument, NULL, 'm'},
860
{ "nodefaultdirs", no_argument, NULL, 'n'},
861
{ "stdout", no_argument, NULL, 's'},
862
{ "version", no_argument, NULL, 'V'},
863
{ NULL, 0, NULL, 0 } };
866
/** Parse commandline parameters */
867
void parse_params(int argc, char **argv)
871
int c = getopt_long (argc, argv, "hvd", long_options, NULL);
877
config.set_verbosity(configinfo::report_verbose);
880
config.set_verbosity(configinfo::report_verbose);
883
config.usedefaultmenufilesdirs = false;
886
config.onlyoutput_to_stdout = true;
889
config.menufilesdir.push_back(optarg);
892
config.menumethod = optarg;
895
cout << "update-menus "VERSION << std::endl;
850
config.report("Dpkg not locking dpkg status area. Good.",
851
configinfo::report_verbose);
855
void parse_params(char **argv){
857
if(String("-d")==String(*argv))
858
config.set_verbosity(configinfo::report_debug);
859
if(String("-v")==String(*argv))
860
config.set_verbosity(configinfo::report_verbose);
861
if(String("--nodefaultdirs")==String(*argv))
862
config.usedefaultmenufilesdirs=false;
863
if(String("--menufiledir")==String(*argv)){
866
(config.menufilesdir).push_back(String(*argv));
868
cerr<<_("directory expected after --menufilesdir option")<<endl;
869
throw informed_fatal();
872
if(String("--menumethod")==String(*argv)){
875
config.menumethod=String(*argv);
877
cerr<<_("filename expected after --menumethod option")<<endl;
878
throw informed_fatal();
881
if(String("-h")==String(*argv)){
883
_("update-menus: update the various window-manager config files (and\n"
884
" dwww, and pdmenu) Usage: update-menus [options] \n"
885
" -v be verbose about what is going on\n"
886
" -d debugging (loads of unintelligible output)")<<endl;
892
void read_userconfiginfo(){
893
//Because of gcc-2.7.2.1 internal compiler errors, had to split this up.
894
//(well, not sure of the configinfo stuff, but translateinfo failed)
897
config.update(String(getenv("HOME"))+"/"+USERCONFIG);
898
} catch(ferror_open d){};
902
void read_rootconfiginfo(){
907
/** Read users configuration file */
908
void read_userconfiginfo()
912
config.read_file(string(home_dir)+"/"+USERCONFIG);
913
} catch(ferror_open d) { };
917
/** Read roots configuration file */
918
void read_rootconfiginfo()
905
config.update(CONFIG_FILE);
922
config.read_file(CONFIG_FILE);
906
923
} catch (ferror_open d){};
910
void read_usertranslateinfo(){
911
//Because of gcc-2.7.2.1 internal compiler errors, had to split this up.
914
transinfo=new translateinfo(String(getenv("HOME"))+"/"+USERTRANSLATE);
915
} catch(ferror_open d){};
919
void read_roottranslateinfo(){
922
transinfo=new translateinfo(TRANSLATE_FILE);
923
}catch (ferror_open d){};
927
int main (int, char **argv){
928
vector<String> menudata;
927
/** Read users translate information */
928
void read_usertranslateinfo()
932
transinfo = new translateinfo(string(home_dir)+"/"+USERTRANSLATE);
933
} catch(ferror_open d) { };
937
/** Read roots translate information */
938
void read_roottranslateinfo()
942
transinfo = new translateinfo(TRANSLATE_FILE);
943
} catch (ferror_open d) { };
947
/** Find our home directory */
948
void read_homedirectory()
950
struct passwd *pwentry = getpwuid(getuid());
953
home_dir = pwentry->pw_dir;
955
home_dir = getenv("HOME");
958
int main (int argc, char **argv)
960
is_root = (getuid() == 0);
961
read_homedirectory();
963
vector<string> menudata;
934
setlocale (LC_MESSAGES, "");
967
setlocale (LC_ALL, "");
935
968
bindtextdomain (PACKAGE, LOCALEDIR);
936
969
textdomain (PACKAGE);
941
// read_userconfiginfo();
942
972
read_rootconfiginfo();
973
parse_params(argc, argv);
944
974
wait_dpkg(stdoutfile);
945
if(stdoutfile.length()){
975
if(!stdoutfile.empty()) {
947
open(stdoutfile.c_str(),
948
O_WRONLY|O_CREAT|O_SYNC|O_EXCL, 0666);
977
open(stdoutfile.c_str(), O_WRONLY|O_CREAT|O_SYNC|O_EXCL, 0666);
955
984
read_usertranslateinfo();
956
985
read_roottranslateinfo();
958
transinfo->debuginfo();
959
if(config.usedefaultmenufilesdirs){
961
(config.menufilesdir).push_back(String(getenv("HOME"))+
963
(config.menufilesdir).push_back(String(CONFIGMENUS));
964
(config.menufilesdir).push_back(String(PACKAGEMENUS));
965
(config.menufilesdir).push_back(String(MENUMENUS));
987
if (config.usedefaultmenufilesdirs) {
989
config.menufilesdir.push_back(string(home_dir)+"/"+USERMENUS);
990
config.menufilesdir.push_back(CONFIGMENUS);
991
config.menufilesdir.push_back(PACKAGEMENUSLIB);
992
config.menufilesdir.push_back(PACKAGEMENUS);
993
config.menufilesdir.push_back(MENUMENUS);
968
996
read_menufilesdir(menudata);
970
if(config.menumethod.length())
971
run_menumethod(config.menumethod,menudata);
975
run_menumethoddir(String(getenv("HOME"))+"/"+USERMETHODS,
978
catch(dir_error_read d){
980
run_menumethoddir(MENUMETHODS, menudata);
983
run_menumethoddir(MENUMETHODS, menudata);
998
if (config.onlyoutput_to_stdout) {
999
for(vector<string>::const_iterator i = menudata.begin(); i != menudata.end(); ++i)
1002
} else if (!config.menumethod.empty()) {
1003
if (executable(config.menumethod))
1004
run_menumethod(config.menumethod, menudata);
1006
config.report(String::compose(_("Script %1 could not be executed."),
1007
config.menumethod), configinfo::report_quiet);
1011
run_menumethoddir(string(home_dir)+"/"+USERMETHODS, menudata);
1013
catch(dir_error_read d) {
1014
run_menumethoddir(MENUMETHODS, menudata);
1017
run_menumethoddir(MENUMETHODS, menudata);
986
catch(genexcept& p){p.report();}
987
/* catch(except_String& p){p.report();}
988
catch(except_pi& p){p.report();}
989
catch(unknown_cond_package p){ p.report(); }
990
catch(informed_fatal p){};
992
/* catch(endoffile p){ p.report(cerr); }
993
catch(endofline p){ p.report(cerr); }
994
catch(char_expected p){ p.report(cerr); }
995
catch(char_unexpected p){ p.report(cerr); }
996
catch(def_error p){ p.report(cerr); }
997
catch(unknown_compat p){ p.report(cerr); }
1000
// catch (ferror_read f){
1001
// cerr<<"Cannot open file "<<f.name<<" for reading"<<endl;
1003
// catch (dir_error_read d){
1004
// cerr<<"Cannot open directory "<<d.name<<" for reading"<<endl;
1006
//catch (informed_fatal){
1007
// cerr<<"Aborting."<<endl;
1021
catch(genexcept& p) { p.report(); }
1012
if(stdoutfile.length()){
1013
r=stat(stdoutfile.c_str(),&st);
1016
unlink(stdoutfile.c_str());
1025
if(!stdoutfile.empty())
1026
if (!stat(stdoutfile.c_str(),&st))
1028
unlink(stdoutfile.c_str());