00001
00024 #include "PlayListConverter.h"
00025 #include "EmpParser.h"
00026 #include "xmlutils.h"
00027 #include "StringUtils.h"
00028 #include <errno.h>
00029 #include <sys/types.h>
00030 #include <sys/stat.h>
00031 #include <sys/wait.h>
00032 #include <unistd.h>
00033 #include <fstream>
00034 #include <sstream>
00035 #include <algorithm>
00036
00037 using namespace std;
00038 using namespace xmlpp;
00039
00040 PlayListConverter::PlayListConverter(){}
00041
00042 void PlayListConverter::OutputFormat::clear()
00043 {
00044 filename = "";
00045 begin = "";
00046 track = "";
00047 end = "";
00048 exec = "";
00049 }
00050
00052 bool PlayListConverter::load_config()
00053 {
00054 bool success(false);
00055 string filename;
00056
00057 if (get_config_file(filename))
00058 {
00059 m_config.parse_file(filename);
00060
00061 Node* root = m_config.get_root_node();
00062
00063 if (root != NULL) {
00064 Node* opts = getFirstChild(root, "options");
00065 if (opts != NULL) {
00066 m_options.workingdir = getPropValue(opts, "working-dir");
00067 const Node::NodeList& formats = opts->get_children("default-format");
00068 Node::NodeList::const_iterator format = formats.begin();
00069 for(; format != formats.end(); ++format) {
00070 string def_format = getPropValue(*format, "name");
00071 if (! def_format.empty()) {
00072 m_options.formats.push_back(def_format);
00073 }
00074 }
00075 }
00076
00077 expand_tilde(m_options.workingdir);
00078
00079 success = true;
00080 }
00081 }
00082 else
00083 {
00084 cerr << "cant find a config file" << endl;
00085 }
00086
00087 return success;
00088 }
00089
00091
00095 bool PlayListConverter::parse_rmp(const char* filename)
00096 {
00097 m_session.emp_file = filename;
00098 EmpParser parser;
00099 return parser.parse_file(filename, m_session, m_tracks);
00100 }
00101
00103 bool PlayListConverter::write_output(OutputFormat& format)
00104 {
00105 bool success(false);
00106 if (m_tracks.empty()) return success;
00107
00108
00109 StringList::const_iterator cformat = format.chained_formats.begin();
00110 for(; cformat != format.chained_formats.end(); ++cformat) {
00111 process_format(*cformat);
00112 }
00113
00114
00115 RmpTrack& firstTrack = *m_tracks.begin();
00116 m_session.output_file = make_subs(format.filename, m_session, firstTrack);
00117
00118
00119 ostream* os = &cout;
00120 ofstream ofs;
00121 if (! m_session.output_file.empty()) {
00122 open_output(ofs);
00123 os = &ofs;
00124 }
00125
00126 if (os && *os) {
00127
00128 if (! format.begin.empty()) {
00129 *os << make_subs(format.begin, m_session, firstTrack) << endl;
00130 }
00131
00132
00133 if (! format.track.empty()) {
00134 TrackList::const_iterator iter = m_tracks.begin();
00135 for(; iter != m_tracks.end(); ++iter) {
00136 *os << make_subs(format.track, m_session, *iter) << endl;
00137 }
00138 }
00139
00140
00141 if (! format.end.empty()) {
00142 *os << make_subs(format.end, m_session, firstTrack) << endl;
00143 }
00144
00145
00146 format.exec = make_subs(format.exec, m_session, firstTrack);
00147
00148 success = true;
00149 } else {
00150 cerr << "failed opening output file: " << m_session.output_file << endl;
00151 }
00152
00153 return success;
00154 }
00155
00157
00162 bool PlayListConverter::exec_output(const OutputFormat& format, bool wait_for_child)
00163 {
00164 bool success(false);
00165
00166 if (format.exec.length() > 0)
00167 {
00168 char* args[] = {"sh", "-c", const_cast<char*>(format.exec.c_str()), 0};
00169
00170 int fork_result = fork();
00171
00172 switch (fork_result) {
00173 case -1:
00174 break;
00175
00176 case 0:
00177
00178 cout << "executing: " << format.exec << endl;
00179 execvp("sh",args);
00180 break;
00181
00182 default:
00183
00184 if (wait_for_child) {
00185 int status;
00186 waitpid(fork_result, &status, 0);
00187 }
00188 success = true;
00189 }
00190 } else {
00191
00192 success = true;
00193 }
00194 return success;
00195 }
00196
00198
00202 bool PlayListConverter::get_config_file(std::string& filename)
00203 {
00204 bool success(true);
00205 string home_config("~/.empxformrc");
00206 expand_tilde(home_config);
00207 const char* configs[] = {home_config.c_str(),
00208 "/usr/share/empxform/empxformrc",
00209 "/usr/local/share/empxform/empxformrc",
00210 "/etc/empxformrc", 0};
00211
00212 struct stat st;
00213 unsigned int i;
00214 for(i=0; (configs[i] != 0) && (stat(configs[i], &st) != 0); ++i){}
00215
00216 if (i < (sizeof(configs) / sizeof(char*) - 1)) {
00217 filename = configs[i];
00218 } else {
00219 success = false;
00220 }
00221
00222 return success;
00223 }
00224
00226
00230 void PlayListConverter::add_format_line(string &entry, const string newline) {
00231 if (entry.size()) {
00232 entry += "\n";
00233 }
00234 entry += newline;
00235 }
00236
00238
00244 string PlayListConverter::make_subs(const string& templ, const RmpSession& session, const RmpTrack& trk)
00245 {
00246 string result(templ);
00247
00248 size_t pos(0);
00249
00250 while ((pos= result.find('%', pos)) != string::npos) {
00251 if (pos < result.length() -1) {
00252
00253 int offset = is_command_modifier(result[pos +1]) ? 2 : 1;
00254
00255 switch (result[pos +offset]) {
00256 case 'O':
00257 insert_subst(result, pos, m_session.output_file);
00258 break;
00259 case 'E':
00260 insert_subst(result, pos, m_session.emp_file);
00261 break;
00262 case 'P':
00263 insert_subst(result, pos, m_session.provider);
00264 break;
00265 case 'T':
00266 insert_subst(result, pos, m_session.package_title);
00267 break;
00268 case 'A':
00269 insert_subst(result, pos, m_session.package_artist);
00270 break;
00271 case 'W':
00272 insert_subst(result, pos, m_options.workingdir);
00273 break;
00274 case 't':
00275 insert_subst(result, pos, trk.title);
00276 break;
00277 case 'a':
00278 insert_subst(result, pos, trk.artist);
00279 break;
00280 case 'n':
00281 insert_subst(result, pos, trk.track_num);
00282 break;
00283 case 'f':
00284 insert_subst(result, pos, trk.file_name);
00285 break;
00286 case 'd':
00287 insert_subst(result, pos, trk.track_id);
00288 break;
00289 case 'g':
00290 insert_subst(result, pos, trk.genre);
00291 break;
00292 case 'F':
00293 insert_subst(result, pos, trk.format);
00294 break;
00295 case 'q':
00296 insert_subst(result, pos, trk.quality);
00297 break;
00298 case 'c':
00299 insert_subst(result, pos, trk.cover);
00300 break;
00301 case 'U':
00302 insert_subst(result, pos, trk.url);
00303 break;
00304 case 'D':
00305 insert_subst(result, pos, trk.duration);
00306 break;
00307 case 'C':
00308 insert_subst(result, pos, trk.channels);
00309 break;
00310 case '%':
00311 insert_subst(result, pos, "%");
00312 break;
00313 default:
00314 ++pos;
00315 }
00316 }
00317 }
00318 return result;
00319 }
00320
00321
00323
00328 void PlayListConverter::insert_subst(string& to_change, size_t& pos, const string& toinsert) {
00329
00330
00331 if (! is_command_modifier(to_change[pos+1])) {
00332 to_change.replace(pos, 2, toinsert);
00333 pos += toinsert.length();
00334
00335 } else {
00336
00337 string new_val(toinsert);
00338
00339 switch (to_change[pos+1]) {
00340 case '\\':
00341 escape_shell_chars(new_val);
00342 break;
00343
00344 case '_':
00345 replace_str(new_val, " ", "_");
00346 remove_shell_chars(new_val);
00347 break;
00348
00349 case '-':
00350 remove_shell_chars(new_val);
00351 break;
00352
00353 case '!':
00354 replace_str(new_val, "/", "-");
00355 break;
00356
00357 case '\'':
00358 replace_str(new_val, "\"", "");
00359 replace_str(new_val, "'", "");
00360 break;
00361 }
00362
00363 to_change.replace(pos, 3, new_val);
00364 pos += new_val.length();
00365 }
00366
00367 }
00368
00370 void PlayListConverter::change_working_dir()
00371 {
00372 if (m_options.workingdir.empty()) {
00373 m_options.workingdir = "~";
00374 expand_tilde(m_options.workingdir);
00375 }
00376
00377 if (! make_dir(m_options.workingdir)) {
00378 cerr << "error creating working directory: " << m_options.workingdir << endl;
00379 }
00380
00381 if (chdir(m_options.workingdir.c_str()) != 0) {
00382 cerr << "error changing to working directory: "<< m_options.workingdir << endl;
00383 }
00384
00385 }
00386
00388
00392 bool PlayListConverter::is_command_modifier(char c)
00393 {
00394 switch (c) {
00395 case '!':
00396 case '\\':
00397 case '-':
00398 case '_':
00399 case '\'':
00400 return true;
00401 default:
00402 return false;
00403 }
00404 }
00405
00406
00411 void PlayListConverter::set_options(const Options& new_opt)
00412 {
00413 m_options.wait_for_child = new_opt.wait_for_child;
00414
00415 if (! new_opt.workingdir.empty()) {
00416 m_options.workingdir = new_opt.workingdir;
00417 void change_working_dir();
00418 }
00419
00420 if (! new_opt.formats.empty()) {
00421 m_options.formats.clear();
00422 transform(new_opt.formats.begin(), new_opt.formats.end(),
00423 back_inserter(m_options.formats),
00424 bind1st(mem_fun(&PlayListConverter::copy_format_name), this));
00425 }
00426 }
00427
00428
00430
00439 string PlayListConverter::copy_format_name(string format)
00440 {
00441 if (format.length() && (format[0] != '<')) {
00442 return format;
00443 } else {
00444
00445 static int format_num = 0;
00446 stringstream tmp_name;
00447 tmp_name << "anonymous-" << format_num++ << '\0';
00448 string format_name(tmp_name.str());
00449
00450 format = "<format name=\"" + format_name + "\">" + format + "</format>";
00451
00452
00453 DomParser tree;
00454 tree.parse_memory(format);
00455 if (! tree) {
00456 cerr << "Error: cant parse anonymous format: " << format << endl;
00457 } else {
00458 Node* new_format = tree.get_root_node();
00459 Node* conf_root = m_config.get_root_node();
00460 if (conf_root && new_format) {
00461
00462 addChild(conf_root, new Node(*new_format));
00463 }
00464 }
00465
00466 return format_name;
00467 }
00468 }
00469
00474 bool PlayListConverter::process_rmp(const char* rmp_file)
00475 {
00476 bool success = parse_rmp(rmp_file);
00477 change_working_dir();
00478
00479 if (success) {
00480 StringList::const_iterator iter = m_options.formats.begin();
00481
00482 for(; iter != m_options.formats.end(); ++iter) {
00483 success &= process_format(*iter);
00484 }
00485 }
00486 return success;
00487 }
00488
00490
00494 bool PlayListConverter::process_format(const std::string& format_name)
00495 {
00496 bool success;
00497 OutputFormat format;
00498 success = load_format(format_name.c_str(), format);
00499 success &= write_output(format);
00500 success &= exec_output(format, m_options.wait_for_child);
00501 }
00502
00504
00509 bool PlayListConverter::load_format(const char* format_name, OutputFormat& oformat)
00510 {
00511 bool success(false);
00512 string filename;
00513 oformat.clear();
00514
00515 if (m_config)
00516 {
00517 Node* root = m_config.get_root_node();
00518
00519 const Node::NodeList& formats = root->get_children("format");
00520 Node::NodeList::const_iterator format = formats.begin();
00521 while ((format != formats.end()) &&
00522 static_cast<Element*>((*format))->get_attribute("name")->get_value() != format_name){++format; }
00523 if (format != formats.end()) {
00524
00525 const Node::NodeList& children = (*format)->get_children();
00526 Node::NodeList::const_iterator curr_child = children.begin();
00527 for(; curr_child != children.end(); ++curr_child) {
00528 if ((*curr_child)->get_name() == "filename")
00529 {
00530 oformat.filename = getChildContent(*curr_child);
00531 expand_tilde(oformat.filename);
00532 }
00533 else if ((*curr_child)->get_name() == "run-format")
00534 {
00535 oformat.chained_formats.push_back(getPropValue(*curr_child, "name"));
00536 }
00537 else if ((*curr_child)->get_name() == "begin")
00538 {
00539 add_format_line(oformat.begin, getChildContent(*curr_child));
00540 }
00541 else if ((*curr_child)->get_name() == "track")
00542 {
00543 add_format_line(oformat.track, getChildContent(*curr_child));
00544 }
00545 else if ((*curr_child)->get_name() == "end")
00546 {
00547 add_format_line(oformat.end, getChildContent(*curr_child));
00548 }
00549 else if ((*curr_child)->get_name() == "exec")
00550 {
00551 oformat.exec = getChildContent(*curr_child);
00552 }
00553 }
00554 success = true;
00555 } else {
00556 cerr << "cant find the requested format \"" << format_name << "\"" << endl;
00557 }
00558
00559 }
00560 else
00561 {
00562 cerr << "Cant find a config file" << endl;
00563 }
00564
00565 return success;
00566
00567 }
00568
00570
00574 int PlayListConverter::get_formats(FormatDescList& format_desc)
00575 {
00576 bool success(false);
00577 string filename;
00578 format_desc.clear();
00579
00580 if (get_config_file(filename))
00581 {
00582 DomParser config(filename);
00583
00584 Node* root = config.get_root_node();
00585
00586 Node::NodeList formats = root->get_children("format");
00587 Node::NodeList::const_iterator iter = formats.begin();
00588 for(; iter != formats.end(); ++iter) {
00589 FormatDesc a_format;
00590 a_format.name = getPropValue(*iter, "name");
00591 getChildContent(*iter, "desc", a_format.desc);
00592 format_desc.push_back(a_format);
00593 }
00594
00595 format_desc.sort();
00596 }
00597 return format_desc.size();
00598 }
00599
00601
00604 void PlayListConverter::open_output(ofstream& os)
00605 {
00606 if (! m_session.output_file.empty()) {
00607 string& filename = m_session.output_file;
00608 size_t slashpos = filename.rfind('/');
00609
00610 if (slashpos != string::npos) {
00611
00612 make_dir(filename.substr(0, slashpos));
00613 }
00614
00615 os.open(filename.c_str());
00616 }
00617 }