1 /*
2 MusicXML Library
3 Copyright (C) Grame 2006-2013
4
5 This Source Code Form is subject to the terms of the Mozilla Public
6 License, v. 2.0. If a copy of the MPL was not distributed with this
7 file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9 Grame Research Laboratory, 11, cours de Verdun Gensoul 69002 Lyon - France
10 research@grame.fr
11 */
12
13 #include <string.h> // for strlen()
14 #include <regex>
15
16 #include <iomanip> // for setw()
17
18 #include "xml.h"
19 #include "xmlfile.h"
20 #include "xmlreader.h"
21
22 #include "messagesHandling.h"
23
24 #include "musicXML2MxmlTreeInterface.h"
25
26 #include "oahOah.h"
27 #include "generalOah.h"
28
29 #include "setTraceOahIfDesired.h"
30 #ifdef TRACE_OAH
31 #include "traceOah.h"
32 #endif
33
34 #include "musicXMLOah.h"
35
36
37 using namespace std;
38
39 namespace MusicXML2
40 {
41
42 //_______________________________________________________________________________
displayXMLDeclaration(TXMLDecl * xmlDeclaration,indentedOstream & logOstream)43 void displayXMLDeclaration (
44 TXMLDecl* xmlDeclaration,
45 indentedOstream& logOstream)
46 {
47 string xmlVersion = xmlDeclaration->getVersion ();
48 string xmlEncoding = xmlDeclaration->getEncoding ();
49 int xmlStandalone = xmlDeclaration->getStandalone ();
50
51 const int fieldWidth = 14;
52
53 logOstream <<
54 "XML Declaration:" <<
55 endl;
56
57 gIndenter++;
58
59 logOstream << left <<
60 setw (fieldWidth) <<
61 "xmlVersion" << " = \"" << xmlVersion << "\"" <<
62 endl <<
63 setw (fieldWidth) <<
64 "xmlEncoding" << " = \"" << xmlEncoding << "\"" <<
65 endl <<
66 setw (fieldWidth) <<
67 "xmlStandalone" << " = \"" << xmlStandalone << "\"" <<
68 endl <<
69 endl;
70
71 gIndenter--;
72 }
73
74 //_______________________________________________________________________________
displayDocumentType(TDocType * documentType,indentedOstream & logOstream)75 void displayDocumentType (
76 TDocType* documentType,
77 indentedOstream& logOstream)
78 {
79 const int fieldWidth = 16;
80
81 logOstream <<
82 "Document Type:" <<
83 endl;
84
85 gIndenter++;
86
87 std::string xmlStartElement = documentType->getStartElement ();
88 bool xmlPublic = documentType->getPublic ();
89 std::string xmlPubLitteral = documentType->getPubLitteral ();
90 std::string xmlSysLitteral = documentType->getSysLitteral ();
91
92 logOstream << left <<
93 setw (fieldWidth) <<
94 "xmlStartElement" << " = \"" << xmlStartElement << "\"" <<
95 endl <<
96 setw (fieldWidth) <<
97 "xmlPublic" << " = \"" << xmlPublic << "\"" <<
98 endl <<
99 setw (fieldWidth) <<
100 "xmlPubLitteral" << " = \"" << xmlPubLitteral << "\"" <<
101 endl <<
102 setw (fieldWidth) <<
103 "xmlSysLitteral" << " = \"" << xmlSysLitteral << "\"" <<
104 endl <<
105 endl;
106
107 gIndenter--;
108 }
109
110 //_______________________________________________________________________________
uncompressMXLFile(string mxlFileName,indentedOstream & logOstream)111 string uncompressMXLFile (
112 string mxlFileName,
113 indentedOstream& logOstream)
114 {
115 string fileBaseName = baseName (mxlFileName);
116
117 logOstream <<
118 "The compressed file name is '" <<
119 mxlFileName <<
120 "'" <<
121 endl <<
122 endl;
123
124 string uncompressedFileName;
125
126 #ifdef WIN32
127 // JMI
128 #else
129 {
130 // build shell command to list the contents of the uncompress file
131 stringstream s1;
132
133 s1 <<
134 "unzip -l " <<
135 mxlFileName;
136
137 string listContentsShellCommand = s1.str ();
138
139 if (true) {
140 logOstream <<
141 "Listing the contents of the compressed file '" <<
142 mxlFileName <<
143 "' with command:" <<
144 endl;
145
146 gIndenter++;
147
148 logOstream <<
149 listContentsShellCommand <<
150 endl <<
151 endl;
152
153 gIndenter--;
154 }
155
156 // create a stream to receive the result of listContentsShellCommand
157 FILE* inputStream =
158 popen (
159 listContentsShellCommand.c_str (),
160 "r");
161
162 if (inputStream == nullptr) {
163 stringstream s;
164
165 s <<
166 "Cannot list the contents of compressed file '" <<
167 mxlFileName <<
168 "' with 'popen ()'";
169
170 msrInternalError (
171 gOahOah->fInputSourceName,
172 0, // inputLineNumber
173 __FILE__, __LINE__,
174 s.str ());
175 }
176
177 else {
178 string contentsList;
179
180 // read the list from inputStream
181 char tampon [1024];
182
183 while (
184 ! feof (inputStream)
185 &&
186 ! ferror (inputStream)
187 &&
188 fgets (tampon, sizeof (tampon), inputStream) != NULL
189 ) {
190 // append the contents of tampon to contentsList
191 contentsList += tampon;
192 } // while
193 // terminate the string in tampon
194 tampon [strlen (tampon) -1] = '\0';
195
196 // close the stream
197 if (pclose (inputStream) < 0) {
198 msrInternalError (
199 gOahOah->fInputSourceName,
200 0, // inputLineNumber
201 __FILE__, __LINE__,
202 "Cannot close the input stream after 'popen ()'");
203 }
204
205 logOstream <<
206 "The contents of the compressed file '" <<
207 mxlFileName <<
208 "' is:" <<
209 endl;
210
211 gIndenter++;
212
213 logOstream <<
214 contentsList <<
215 endl;
216
217 gIndenter--;
218
219 // analyze the contents list
220 list<string> linesList;
221
222 istringstream inputStream (contentsList);
223 string currentLine;
224
225 while (getline (inputStream, currentLine)) {
226
227 if (inputStream.eof ()) break;
228
229 #ifdef TRACE_OAH
230 {
231 logOstream <<
232 "*** currentLine:" <<
233 endl;
234
235 gIndenter++;
236
237 logOstream <<
238 currentLine <<
239 endl;
240
241 gIndenter--;
242 }
243 #endif
244
245 /*
246 user@lilydev: ~/libmusicxml-git/files/samples/musicxml > unzip -l UnofficialTestSuite/90a-Compressed-MusicXML.mxl
247 Archive: UnofficialTestSuite/90a-Compressed-MusicXML.mxl
248 Length Date Time Name
249 --------- ---------- ----- ----
250 0 2007-11-14 16:04 META-INF/
251 246 2007-11-14 16:02 META-INF/container.xml
252 2494 2008-11-14 23:03 20a-Compressed-MusicXML.xml
253 30903 2007-11-14 15:51 20a-Compressed-MusicXML.pdf
254 --------- -------
255 33643 4 files
256 */
257
258 string regularExpression (
259 "[[:space:]]*"
260 ".*" // length
261 "[[:space:]]+"
262 ".*" // date
263 "[[:space:]]+"
264 ".*" // time
265 "[[:space:]]+"
266 "(.*)" // name
267 );
268
269 regex e (regularExpression);
270 smatch sm;
271
272 regex_match (currentLine, sm, e);
273
274 if (sm.size ()) {
275 #ifdef TRACE_OAH
276 if (gTraceOah->fTracePasses) { // JMI ???
277 logOstream <<
278 "There are " << sm.size () - 1 << " match(es) " <<
279 "with regex '" << regularExpression <<
280 "':" <<
281 endl;
282
283 for (unsigned i = 1; i < sm.size (); ++i) {
284 logOstream <<
285 "[" << sm [i] << "] " <<
286 endl;
287 } // for
288
289 logOstream <<
290 endl <<
291 endl;
292 }
293 #endif
294
295 string stringFromLine = sm [1];
296
297 // has stringFromLine a ".xml" suffix?
298 size_t
299 posInString =
300 stringFromLine.rfind (".xml");
301
302 // JMI if (posInString == stringFromLine.size () - 4) {
303 if (posInString != stringFromLine.npos) { // JMI STRANGISSIMO!!!
304 // if (posInString != stringFromLine.npos && stringFromLine != "files") { // JMI STRANGISSIMO!!!
305 // yes, this is a MusicXML file
306
307 // is this file part of META-INF?
308 size_t
309 posInString =
310 stringFromLine.find ("META-INF");
311
312 if (posInString == stringFromLine.npos) {
313 // no, this is an actual MusicXML file
314
315 if (uncompressedFileName.size ()) {
316 stringstream s;
317
318 s <<
319 "Compressed file '" << mxlFileName <<
320 "' contains multiple MusicMXL files" <<
321 ", found '" << uncompressedFileName <<
322 "' and then '" << stringFromLine << "'";
323
324 msrInternalError (
325 gOahOah->fInputSourceName,
326 0, // inputLineNumber
327 __FILE__, __LINE__,
328 s.str ());
329 }
330
331 else {
332 // we've got the uncompressed file name
333 uncompressedFileName = stringFromLine;
334
335 logOstream <<
336 "The uncompressed file name is '" <<
337 uncompressedFileName <<
338 "'" <<
339 endl <<
340 endl;
341 }
342 }
343 }
344 }
345 } // while
346 }
347 }
348
349 {
350 // build shell command to uncompress the file
351 stringstream s2;
352
353 s2 <<
354 "unzip -u -d /tmp " <<
355 mxlFileName;
356
357 string uncompressShellCommand = s2.str ();
358
359 if (true) {
360 logOstream <<
361 "Uncompressing '" <<
362 mxlFileName <<
363 "' into '/tmp/" <<
364 uncompressedFileName <<
365 "' with command:" <<
366 endl;
367
368 gIndenter++;
369
370 logOstream <<
371 uncompressShellCommand <<
372 endl <<
373 endl;
374
375 gIndenter--;
376 }
377
378 // create a stream to receive the result of uncompressShellCommand
379 FILE* inputStream =
380 popen (
381 uncompressShellCommand.c_str (),
382 "r");
383
384 if (inputStream == nullptr) {
385 stringstream s;
386
387 s <<
388 "Cannot uncompress the file '" <<
389 mxlFileName <<
390 "' with 'popen ()'";
391
392 msrInternalError (
393 gOahOah->fInputSourceName,
394 0, // inputLineNumber
395 __FILE__, __LINE__,
396 s.str ());
397 }
398 }
399 #endif
400
401 return uncompressedFileName;
402 }
403
404 //_______________________________________________________________________________
musicXMLFile2mxmlTree(const char * fileName,S_musicXMLOah mxmlOpts,indentedOstream & logOstream)405 EXP Sxmlelement musicXMLFile2mxmlTree (
406 const char* fileName,
407 S_musicXMLOah mxmlOpts,
408 indentedOstream& logOstream)
409 {
410 clock_t startClock = clock ();
411
412 string fileNameAsString = fileName;
413
414 #ifdef TRACE_OAH
415 if (gTraceOah->fTracePasses) {
416 string separator =
417 "%--------------------------------------------------------------";
418
419 logOstream <<
420 endl <<
421 separator <<
422 endl <<
423 gTab <<
424 "Pass 1: building the xmlelement tree from \"" << fileNameAsString << "\"" <<
425 endl <<
426 separator <<
427 endl <<
428 endl;
429 }
430 #endif
431
432 // has the input file name a ".mxl" suffix?
433 size_t
434 posInString =
435 fileNameAsString.rfind (".mxl");
436
437 if (posInString == fileNameAsString.size () - 4) {
438 // JMI if (posInString != fileNameAsString.npos) {
439 // yes, this is a compressed file
440
441 /* JMI OS dependent
442 string uncompressedFileName =
443 uncompressMXLFile (
444 fileNameAsString,
445 logOstream);
446
447 // the incompressed file in /tmp will be handled
448 // instead of the compressed one
449 fileName = uncompressedFileName.c_str ();
450 */
451
452 stringstream s;
453
454 s <<
455 "you should uncompress this file prior to running xml2ly";
456
457 msrMusicXMLError (
458 gOahOah->fInputSourceName,
459 1, // inputLineNumber,
460 __FILE__, __LINE__,
461 s.str ());
462
463 exit (38);
464 }
465
466 // read the input MusicXML data from the file
467 xmlreader r;
468
469 SXMLFile xmlFile = r.read (fileName);
470
471 // has there been a problem?
472 if (! xmlFile) {
473 return Sxmlelement (0);
474 }
475
476 #ifdef TRACE_OAH
477 if (gMusicXMLOah->fTraceEncoding) {
478 logOstream <<
479 endl <<
480 "!!!!! xmlFile contents from file:" <<
481 endl <<
482 endl;
483
484 xmlFile->print (logOstream);
485
486 logOstream <<
487 endl <<
488 endl;
489 }
490 #endif
491
492 // get the xmlDecl
493 TXMLDecl * xmlDecl = xmlFile->getXMLDecl ();
494
495 #ifdef TRACE_OAH
496 if (gMusicXMLOah->fTraceEncoding) {
497 logOstream <<
498 endl <<
499 "!!!!! xmlDecl contents from file:" <<
500 endl <<
501 endl;
502 xmlDecl->print (logOstream);
503
504 displayXMLDeclaration (
505 xmlDecl,
506 logOstream);
507 }
508 #endif
509
510 #ifdef TRACE_OAH
511 // get the docType
512 TDocType * docType = xmlFile->getDocType ();
513
514 if (gMusicXMLOah->fTraceEncoding) {
515 logOstream <<
516 endl <<
517 "!!!!! docType from file:" <<
518 endl <<
519 endl;
520 docType->print (logOstream);
521
522 displayDocumentType (
523 docType,
524 logOstream);
525 }
526 #endif
527
528 // get the encoding type
529 string encoding = xmlDecl->getEncoding ();
530
531 // build the xmlelement tree
532 Sxmlelement mxmlTree;
533
534 // should the encoding be converted to UTF-8?
535 string desiredEncoding = "UTF-8";
536
537 if (encoding == desiredEncoding) {
538 #ifdef TRACE_OAH
539 if (gTraceOah->fTracePasses) {
540 logOstream <<
541 "% MusicXML data uses \"" <<
542 desiredEncoding <<
543 "\" encoding" <<
544 endl;
545 }
546 #endif
547 }
548
549 else if (encoding.size () == 0) {
550 stringstream s;
551
552 s <<
553 "MusicXML data in this file" <<
554 " doesn't contain any encoding specification; assuming it is UTF-8";
555
556 msrMusicXMLWarning (
557 gOahOah->fInputSourceName,
558 1, // inputLineNumber,
559 s.str ());
560 }
561
562 else {
563 stringstream s;
564
565 s <<
566 "you should convert this file to " <<
567 desiredEncoding <<
568 "\" encoding prior to running xml2ly" <<
569 ", for example with iconv or using a text editor - handling it as is";
570
571 msrMusicXMLWarning (
572 gOahOah->fInputSourceName,
573 1, // inputLineNumber,
574 s.str ());
575 }
576
577 mxmlTree = xmlFile->elements ();
578
579 clock_t endClock = clock ();
580
581 // register time spent
582 timing::gTiming.appendTimingItem (
583 "Pass 1",
584 "build xmlelement tree from file",
585 timingItem::kMandatory,
586 startClock,
587 endClock);
588
589 return mxmlTree;
590 }
591
592 //_______________________________________________________________________________
musicXMLFd2mxmlTree(FILE * fd,S_musicXMLOah mxmlOpts,indentedOstream & logOstream)593 EXP Sxmlelement musicXMLFd2mxmlTree (
594 FILE* fd,
595 S_musicXMLOah mxmlOpts,
596 indentedOstream& logOstream)
597 {
598 clock_t startClock = clock ();
599
600 #ifdef TRACE_OAH
601 if (gTraceOah->fTracePasses) {
602 string separator =
603 "%--------------------------------------------------------------";
604
605 logOstream <<
606 endl <<
607 separator <<
608 endl <<
609 gTab <<
610 "Pass 1: building the xmlelement tree from standard input" <<
611 endl <<
612 separator <<
613 endl;
614 }
615 #endif
616
617 // read the input MusicXML data
618 xmlreader r;
619
620 SXMLFile xmlFile = r.read (fd);
621
622 // has there been a problem?
623 if (! xmlFile) {
624 return Sxmlelement (0);
625 }
626
627 #ifdef TRACE_OAH
628 if (gMusicXMLOah->fTraceEncoding) {
629 logOstream <<
630 "!!!!! xmlFile contents from stream:" <<
631 endl;
632 xmlFile->print (logOstream);
633 logOstream << endl;
634 }
635 #endif
636
637 // get the xmlDecl
638 TXMLDecl *xmlDecl = xmlFile->getXMLDecl ();
639
640 #ifdef TRACE_OAH
641 if (gMusicXMLOah->fTraceEncoding) {
642 logOstream <<
643 endl <<
644 "xmlDecl contents:" <<
645 endl <<
646 endl;
647 xmlDecl->print (logOstream);
648
649 displayXMLDeclaration (
650 xmlDecl,
651 logOstream);
652 }
653 #endif
654
655 #ifdef TRACE_OAH
656 // get the docType
657 TDocType * docType = xmlFile->getDocType ();
658
659 if (gMusicXMLOah->fTraceEncoding) {
660 logOstream <<
661 endl <<
662 "!!!!! docType from stream:" <<
663 endl <<
664 endl;
665 docType->print (logOstream);
666
667 displayDocumentType (
668 docType,
669 logOstream);
670 }
671 #endif
672
673 // get the encoding type
674 string encoding = xmlDecl->getEncoding ();
675
676 // should the encoding be converted to UTF-8?
677 string desiredEncoding = "UTF-8";
678
679 logOstream <<
680 "% MusicXML data uses \"" <<
681 desiredEncoding <<
682 "\" encoding" <<
683 ", desired encoding is \"" << desiredEncoding << "\"" <<
684 endl;
685
686 if (encoding != desiredEncoding) {
687 stringstream s;
688
689 s <<
690 "you should convert this stream to " <<
691 desiredEncoding <<
692 "\" encoding prior to running xml2ly" <<
693 ", for example with iconv or using a text editor - handling it as is";
694
695 msrMusicXMLWarning (
696 gOahOah->fInputSourceName,
697 1, // inputLineNumber,
698 s.str ());
699 }
700
701 clock_t endClock = clock ();
702
703 // register time spent
704 timing::gTiming.appendTimingItem (
705 "Pass 1",
706 "build xmlelement tree from standard input",
707 timingItem::kMandatory,
708 startClock,
709 endClock);
710
711 // fetch mxmlTree
712 Sxmlelement mxmlTree = xmlFile->elements ();
713
714 return mxmlTree;
715 }
716
717 //_______________________________________________________________________________
musicXMLString2mxmlTree(const char * buffer,S_musicXMLOah mxmlOpts,indentedOstream & logOstream)718 EXP Sxmlelement musicXMLString2mxmlTree (
719 const char* buffer,
720 S_musicXMLOah mxmlOpts,
721 indentedOstream& logOstream)
722 {
723 clock_t startClock = clock ();
724
725 #ifdef TRACE_OAH
726 if (gTraceOah->fTracePasses) {
727 string separator =
728 "%--------------------------------------------------------------";
729
730 logOstream <<
731 endl <<
732 separator <<
733 endl <<
734 gTab <<
735 "Pass 1: building the xmlelement tree from a buffer" <<
736 endl <<
737 separator <<
738 endl;
739 }
740 #endif
741
742 xmlreader r;
743
744 SXMLFile xmlFile = r.readbuff (buffer);
745
746 clock_t endClock = clock ();
747
748 // register time spent
749 timing::gTiming.appendTimingItem (
750 "Pass 1",
751 "build xmlelement tree from buffer",
752 timingItem::kMandatory,
753 startClock,
754 endClock);
755
756 // fetch mxmlTree
757 Sxmlelement mxmlTree = xmlFile->elements();
758
759 return mxmlTree;
760 }
761
762
763 } // namespace
764