// // Copyright 1998-2004 by Craig Stuart Sapp, All Rights Reserved. // Programmer: Craig Stuart Sapp // Creation Date: Mon May 18 13:43:47 PDT 1998 // Last Modified: Wed Nov 29 10:24:29 PST 2000 (split from HumdrumFile class) // Last Modified: Wed Mar 21 17:38:23 PST 2001 (added spine width feature) // Last Modified: Thu Nov 15 12:38:49 PST 2001 (added getTrackExInterp) // Last Modified: Sat May 8 12:57:53 PDT 2004 (fix complicated spine tracing) // Last Modified: Wed Jun 2 15:19:57 PDT 2004 (fix fix in spine trace ! lines) // Last Modified: Fri Apr 24 15:01:35 PDT 2009 (fixed contructor with char*) // Last Modified: Wed Apr 29 17:14:44 PDT 2009 (fixed *x bug in dot analysis) // Last Modified: Mon May 4 12:11:13 PDT 2009 (fixed new? bug in dot analysis) // Last Modified: Fri Jun 12 22:58:34 PDT 2009 (renamed SigCollection class) // Last Modified: Mon Jun 22 15:03:44 PDT 2009 (added Humdrum/Http URIs) // Last Modified: Wed Sep 2 21:48:23 PDT 2009 (fixed problem with *x again) // Last Modified: Wed May 12 22:29:42 PDT 2010 (added embedded PDF parsing) // Last Modified: Mon Feb 14 08:14:53 PST 2011 (added VTS functions) // Filename: ...sig/src/sigInfo/HumdrumFileBasic.cpp // Web Address: http://sig.sapp.org/src/sigInfo/HumdrumFileBasic.cpp // Syntax: C++ // // Description: Basic parsing functions for handling a Humdrum data file. // Special musical parsing of the Humdrum file is done in // the inherited class HumdrumFile. // #include "Convert.h" #include "HumdrumFileBasic.h" #include "PDFFile.h" #include "CheckSum.h" #include #include #include #include #ifndef OLDCPP #include #include using namespace std; #else #include #include #endif char HumdrumFileBasic::empty[1] = {0}; ////////////////////////////// // // HumdrumFileBasic::HumdrumFileBasic // HumdrumFileBasic::HumdrumFileBasic(void) { records.setSize(100000); // initial storage size 100000 lines records.setSize(0); records.allowGrowth(); records.setAllocSize(100000); // grow in increments of 100000 lines maxtracks = 0; trackexinterp.setSize(0); trackexinterp.allowGrowth(0); } HumdrumFileBasic::HumdrumFileBasic(const HumdrumFileBasic& aHumdrumFileBasic) { records.setSize(100000); // initial storage size 100000 lines records.setSize(0); records.allowGrowth(); records.setAllocSize(100000); // grow in increments of 100000 lines maxtracks = 0; trackexinterp.setSize(0); trackexinterp.allowGrowth(0); *this = aHumdrumFileBasic; } HumdrumFileBasic::HumdrumFileBasic(const char* filename) { records.setSize(100000); // initial storage size 100000 lines records.setSize(0); records.allowGrowth(); records.setAllocSize(100000); // grow in increments of 100000 lines maxtracks = 0; trackexinterp.setSize(0); trackexinterp.allowGrowth(0); #ifndef OLDCPP ifstream infile(filename, ios::in); #else ifstream infile(filename, ios::in | ios::nocreate); #endif if (!infile) { cerr << "Error: cannot open file: " << filename << endl; exit(1); } read(filename); /* char templine[4096]; while (!infile.eof()) { infile.getline(templine, 4096); if (infile.eof() && (strcmp(templine, "") == 0)) { break; } else { appendLine(templine); } } analyzeSpines(); analyzeDots(); */ } //////////////////////////////////////// // // HumdrumFileBasic::setAllocation -- set the expected number of // lines from the input file. // void HumdrumFileBasic::setAllocation(int allocation) { if (allocation <= 0) { return; } if (allocation > 10000000) { return; } int currentsize = records.getSize(); if (currentsize < allocation) { records.setSize(allocation); records.setSize(currentsize); records.setAllocSize(allocation); } } ////////////////////////////// // // HumdrumFileBasic::~HumdrumFileBasic // HumdrumFileBasic::~HumdrumFileBasic() { clear(); } ////////////////////////////// // // HumdrumFileBasic::analyzeSpines -- // void HumdrumFileBasic::analyzeSpines(void) { privateSpineAnalysis(); } ////////////////////////////// // // HumdrumFileBasic::analyzeDots -- // void HumdrumFileBasic::analyzeDots(void) { privateDotAnalysis(); } ////////////////////////////// // // HumdrumFileBasic::appendLine -- adds a line to a humdrum file // void HumdrumFileBasic::appendLine(const char* aLine) { HumdrumRecord* aRecord; aRecord = new HumdrumRecord; aRecord->setLine(aLine); records[records.getSize()] = aRecord; } void HumdrumFileBasic::appendLine(HumdrumRecord& aRecord) { HumdrumRecord *tempRecord; tempRecord = new HumdrumRecord; *tempRecord = aRecord; records[records.getSize()] = tempRecord; } void HumdrumFileBasic::appendLine(HumdrumRecord* aRecord) { appendLine(*aRecord); } ////////////////////////////// // // HumdrumFileBasic::clear -- removes all lines from the humdrum file // void HumdrumFileBasic::clear(void) { int i; for (i=0; igetType()) { case E_humrec_data: case E_humrec_data_comment: case E_humrec_data_kern_measure: case E_humrec_data_interpretation: if (aField < 0) { fieldNumber = records[i]->getFieldCount() + aField; if (fieldNumber < 0) { fieldNumber = 0; } } else { fieldNumber = aField; } aRecord.setLine((*records[i])[fieldNumber]); aRecord.setExInterp(0, records[i]->getExInterpNum(fieldNumber)); output.appendLine(aRecord); break; case E_humrec_empty: output.appendLine(records[i]); break; case E_humrec_none: case E_humrec_global_comment: default: output.appendLine(records[i]); } } return output; } /////////////////////////////// // // HumdrumFileBasic::getDotValue -- // const char* HumdrumFileBasic::getDotValue(int index, int spinei) { int line = (*this)[index].getDotLine(spinei); int spine = (*this)[index].getDotSpine(spinei); if (line < 0 || spine < 0) { return HumdrumFileBasic::empty; } else { return (*this)[line][spine]; } } ////////////////////////////// // // HumdrumFileBasic::getMaxTracks -- // int HumdrumFileBasic::getMaxTracks(void) { return maxtracks; } ////////////////////////////// // // HumdrumFileBasic::getTrackExInterp -- // const char* HumdrumFileBasic::getTrackExInterp(int track) { return trackexinterp[track-1]; } ////////////////////////////// // // getTracksByExInterp -- return a list of track numbers which have the // matching exclusive interpretation type. // int HumdrumFileBasic::getTracksByExInterp(Array& tracks, const char* exinterp) { int maxsize = getMaxTracks(); tracks.setSize(maxsize); tracks.setSize(0); int i; for (i=0; i= getNumLines()) { cerr << "Error: maximum line index is: " << getNumLines() - 1 << ", but you asked for line index: " << index << endl; exit(1); } return records[index]->getLine(); } ////////////////////////////// // // HumdrumFileBasic::getBibValue -- add an optional parameter later which // selects the nth occurance of the bibliographic key. // const char* HumdrumFileBasic::getBibValue(char* buffer, const char* key) { int i; buffer[0] = '\0'; HumdrumFileBasic& hfile = *this; char newkey[1024] = {0}; if (strncmp(key, "!!!", 3) == 0) { strcpy(newkey, key); } else { strcpy(newkey, "!!!"); strcat(newkey, key); } for (i=0; i= getNumLines()) { cerr << "Error: maximum line index is: " << getNumLines() - 1 << ", but you asked for line index: " << index << endl; exit(1); } return *records[index]; } ////////////////////////////// // // HumdrumFileBasic::getSegmentCount -- will eventually return the // number of independent musical segments (which each start with // /^\*\*/ and end with /^\*-/). // // also later, add a function called getSegmentStart(int index) which // returns the staring index in the file for the segment. // int HumdrumFileBasic::getSegmentCount(void) { return 1; } ////////////////////////////// // // HumdrumFileBasic::getSpineCount -- returns the record of the specified line // int HumdrumFileBasic::getSpineCount(int index) { int type = ((*this)[index]).getType(); if ((type & E_humrec_data) == E_humrec_data) { return ((*this)[index]).getFieldCount(); } else { return 0; } } ////////////////////////////// // // HumdrumFileBasic::operator= // HumdrumFileBasic& HumdrumFileBasic::operator=(const HumdrumFileBasic& aFile) { // don't copy onto self if (&aFile == this) { return *this; } // delete any current contents int i; for (i=0; igetType()) { case E_humrec_data: for (j=0; jgetFieldCount(); j++) { if (strcmp((*records[i])[j], ".") != 0) { nulltest = 0; break; } } if (nulltest) { // do nothing } else { output.appendLine(records[i]); } break; default: output.appendLine(records[i]); } } return output; } ////////////////////////////// // // HumdrumFileBasic::operator[] // HumdrumRecord& HumdrumFileBasic::operator[](int index) { if (index >= getNumLines()) { cerr << "Error: maximum line index is: " << getNumLines() - 1 << ", but you asked for line index: " << index << endl; exit(1); } return *records[index]; } ////////////////////////////// // // HumdrumFileBasic::write -- write out a humdrum file. // void HumdrumFileBasic::write(const char* filename) { #ifndef OLDCPP ofstream outfile(filename, ios::out); #else ofstream outfile(filename, ios::out | ios::noreplace); #endif if (!outfile) { cerr << "Error: could not open file for writing: " << filename << endl; cerr << "Perhaps it already exists?" << endl; exit(1); } for (int i=0; igetLine() << endl; } } void HumdrumFileBasic::write(ostream& outStream) { for (int i=0; igetLine() << endl; } } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // operator<< // ostream& operator<<(ostream& out, HumdrumFileBasic& aHumdrumFileBasic) { for (int i=0; i spineinfo; SigCollection exinterps; exinterps.setSize(100); exinterps.setAllocSize(100); exinterps.setSize(1); exinterps[0] = 0; exinterps.allowGrowth(); spineinfo.setSize(1000); spineinfo.setAllocSize(1000); spineinfo.setSize(0); spineinfo.allowGrowth(); int currentwidth = 0; int prediction = 0; int length; int tlen = 0; int i, n; int type; char* tptr = NULL; char buffer[1024] = {0}; char* bp; for (i=0; i= linecount) && prediction != 0) { cerr << "Error in termination of humdrum data" << endl; } ((*this)[n]).copySpineInfo(spineinfo, n+1); makeNewSpineInfo(spineinfo, ((*this)[n]), prediction, spineid, exinterps); if (prediction == 0) { init = 0; } } else { // plain tandem interpretation if (init == 0) { cerr << "Error on first line of data: no starting interpretation" << endl; exit(1); } ((*this)[n]).copySpineInfo(spineinfo, n+1); (*this)[n].setSpineWidth(currentwidth); } } else { // do nothing: global comment, bibliography information, or null line (*this)[n].setSpineWidth(currentwidth); } } // delete the contents of spineinfo for (i=0; i&spineinfo, HumdrumRecord& aRecord, int newsize, int& spineid, SigCollection& ex) { SigCollection newinfo; newinfo.setSize(newsize); int i, j; for (i=0; i 1) { strcat(newinfo[outindex], " "); strcat(newinfo[outindex], spineinfo[inindex+subcount-1]); simplifySpineString(newinfo[outindex]); } } else { break; } } if (subcount == 1) { cout << "Error: single *v path indicator on line: " << aRecord.getLineNum() << "\n" << aRecord.getLine() << endl; exit(1); } else { inindex += subcount-1; } // check to see if the spine info can be simplified: simplifySpineInfo(newinfo, outindex); outindex++; } else if (strncmp("**", aRecord[inindex], 2) == 0) { int value; value = Convert::exint.getValue(aRecord[inindex]); if (spineid != ex.getSize()) { cout << "Error in exclusive interpretation allocation" << endl; exit(1); } if (value == E_unknown) { value = Convert::exint.add(aRecord[inindex]); ex.append(value); } else { ex.append(value); } strcpy(newinfo[outindex], spineinfo[inindex]); outindex++; } else { strcpy(newinfo[outindex], spineinfo[inindex]); outindex++; } } if (outindex != newinfo.getSize()) { cout << "Error in HumdrumFileBasic path parsing" << endl; exit(1); } if (inindex != spineinfo.getSize()) { cout << "Error in HumdrumFileBasic path parsing" << endl; exit(1); } // delete the old information: for (i=0; i ptrs; Array lengths; ptrs.setSize(100); ptrs.setGrowth(100); ptrs.setSize(0); lengths.setSize(100); lengths.setGrowth(100); lengths.setSize(0); char* ptr = strtok(buffer, " "); while (ptr != NULL) { ptrs.append(ptr); length = strlen(ptr); lengths.append(length); ptr = strtok(NULL, " "); } int i, j; int k; for (i=0; i& info, int index) { char* temp; int length; length = strlen(info[index]); temp = new char[length + 1]; strcpy(temp, info[index]); int count = 0; int i; for (i=0; i 2 && len2 > 2) { p1[len1-2] = '\0'; p2[len2-2] = '\0'; if (strcmp(p1, p2) == 0) { strcpy(info[index], p1); } } } delete [] temp; } ////////////////////////////// // // HumdrumFileBasic::predictNewSpineCount -- // int HumdrumFileBasic::predictNewSpineCount(HumdrumRecord& aRecord) { int output = 0; int spinecount = aRecord.getFieldCount(); int subcount; int i; int j; for (i=0; i lastline; Array lastspine; int i, j; int count = 0; int newcount = 0; int length = (*this).getNumLines(); for (i=0; i& lastline, Array& lastspine, HumdrumRecord& record, int newsize) { Array newline; Array newspine; newline.setSize(newsize); newspine.setSize(newsize); newline.allowGrowth(0); newspine.allowGrowth(0); int i, j; for (i=0; i 1) { newline[outindex] = lastline[inindex+subcount-1]; newspine[outindex] = lastspine[inindex+subcount-1]; } } else { break; } } if (subcount == 1) { cout << "Error: single *v path indicator on line: " << record.getLineNum() << "\n" << record.getLine() << endl; exit(1); } else { inindex += subcount-1; } outindex++; } else if (strncmp("**", record[inindex], 2) == 0) { newline[outindex] = -1; newspine[outindex] = -1; outindex++; } else { newline[outindex] = lastline[inindex]; newspine[outindex] = lastspine[inindex]; outindex++; } } if (outindex != newline.getSize()) { cout << "Error in HumdrumFileBasic path parsing" << endl; exit(1); } if (inindex != lastline.getSize()) { cout << "Error in HumdrumFileBasic path parsing" << endl; exit(1); } // copy the new information lastline.setSize(newline.getSize()); lastspine.setSize(newspine.getSize()); for (i=0; i location; location.setSize(strlen(ptr)+1); location.allowGrowth(0); strcpy(location.getBase(), ptr); Array filename; filename.setSize(1); filename[0] = '\0'; filename.setSize(0); strcpy(location.getBase(), ptr); char* pot; if ((pot = strrchr(location.getBase(), '/')) != NULL) { *pot = '\0'; pot++; filename.setSize(strlen(pot)+1); strcpy(filename.getBase(), pot); filename.allowGrowth(0); } else { filename = location; location.setSize(2); strcpy(location.getBase(), "/"); } SSTREAM httprequest; httprequest << "http://" << "kern.humdrum.org"; httprequest << "/cgi-bin/ksdata?file="; if (filename.getSize() > 0) { httprequest << filename.getBase(); } httprequest << "&l="; if (location.getSize() > 0) { httprequest << location.getBase(); } httprequest << "&format=kern"; httprequest << ends; readFromHttpURI(httprequest.CSTRING); } ////////////////////////////// // // readFromHttpURI -- Read a Humdrum file from an http:// web address // void HumdrumFileBasic::readFromHttpURI(const char* webaddress) { Array hostname; Array location; location.setSize(0); const char* ptr = webaddress; const char* filename = NULL; if (strncmp(webaddress, "http://", strlen("http://")) == 0) { // remove the "http://" portion of the webaddress ptr += strlen("http://"); } hostname.setSize(strlen(ptr)+1); hostname.setGrowth(0); strcpy(hostname.getBase(), ptr); char* pot; if ((pot = strchr(hostname.getBase(), '/')) != NULL) { *pot = '\0'; } if ((filename = strchr(ptr, '/')) != NULL) { location.setSize(strlen(filename)+1); strcpy(location.getBase(), filename); location.allowGrowth(0); } if (location.getSize() == 0) { location.setSize(2); location.allowGrowth(0); strcpy(location.getBase(), "/"); } char newline[3] = {0x0d, 0x0a, 0}; SSTREAM request; request << "GET " << location.getBase() << " HTTP/1.1" << newline; request << "Host: " << hostname.getBase() << newline; request << "User-Agent: HumdrumFile Downloader 1.0 (" << __DATE__ << ")" << newline; request << "Connection: close" << newline; // this line is necessary request << newline; request << ends; // cout << "HOSTNAME: " << hostname.getBase() << endl; // cout << "LOCATION: " << location.getBase() << endl; // cout << request.CSTRING << endl; // cout << "-------------------------------------------------" << endl; int socket_id = open_network_socket(hostname.getBase(), 80); if (::write(socket_id, request.CSTRING, strlen(request.CSTRING)) == -1) { exit(-1); } #define URI_BUFFER_SIZE (10000) char buffer[URI_BUFFER_SIZE]; int message_len; SSTREAM inputdata; SSTREAM header; int foundcontent = 0; int i; int newlinecounter = 0; // read the response header: while ((message_len = ::read(socket_id, buffer, 1)) != 0) { header << buffer[0]; if ((buffer[0] == 0x0a) || (buffer[0] == 0x0d)) { newlinecounter++; } else { newlinecounter = 0; } if (newlinecounter == 4) { foundcontent = 1; break; } } if (foundcontent == 0) { cerr << "Funny error trying to read server response" << endl; exit(1); } // now read the size of the rest of the data which is expected int datalength = -1; // also, check for chunked transfer encoding: int chunked = 0; header << ends; // cout << header.CSTRING << endl; // cout << "-------------------------------------------------" << endl; while (header.getline(buffer, URI_BUFFER_SIZE)) { int len = strlen(buffer); for (i=0; i= 0) { // break; // } } // once the length of the remaining data is known (or not), read it: if (datalength > 0) { getFixedDataSize(socket_id, datalength, inputdata, buffer, URI_BUFFER_SIZE); } else if (chunked) { int chunksize; int totalsize = 0; do { chunksize = getChunk(socket_id, inputdata, buffer, URI_BUFFER_SIZE); totalsize += chunksize; } while (chunksize > 0); if (totalsize == 0) { cerr << "Error: no data found for URI (probably invalid)\n"; exit(1); } } else { // if the size of the rest of the data cannot be found in the // header, then just keep reading until done (but this will // probably cause a 5 second delay at the last read). while ((message_len = ::read(socket_id, buffer, URI_BUFFER_SIZE)) != 0) { if (foundcontent) { inputdata.write(buffer, message_len); } else { for (i=0; i datalength) { readsize = datalength - readcount; } message_len = ::read(socket_id, buffer, readsize); if (message_len == 0) { // shouldn't happen, but who knows... break; } inputdata.write(buffer, message_len); readcount += message_len; } return readcount; } ////////////////////////////// // // HumdrumFileBasic::prepare_address -- Store a computer name, such as // www.google.com into a sockaddr_in structure for later use in // open_network_socket. // void HumdrumFileBasic::prepare_address(struct sockaddr_in *address, const char *hostname, unsigned short int port) { memset(address, 0, sizeof(struct sockaddr_in)); struct hostent *host_entry; host_entry = gethostbyname(hostname); if (host_entry == NULL) { cerr << "Could not find address for" << hostname << endl; exit(1); } // copy the address to the sockaddr_in struct. memcpy(&address->sin_addr.s_addr, host_entry->h_addr_list[0], host_entry->h_length); // set the family type (PF_INET) address->sin_family = host_entry->h_addrtype; address->sin_port = htons(port); } ////////////////////////////// // // open_network_socket -- Open a connection to a computer on the internet. // Intended for downloading a Humdrum file from a website. // int HumdrumFileBasic::open_network_socket(const char *hostname, unsigned short int port) { int inet_socket; // socket descriptor struct sockaddr_in servaddr; // IP/port of the remote host prepare_address(&servaddr, hostname, port); // socket(domain, type, protocol) // domain = PF_INET(internet/IPv4 domain) // type = SOCK_STREAM(tcp) * // protocol = 0 (only one SOCK_STREAM type in the PF_INET domain inet_socket = socket(PF_INET, SOCK_STREAM, 0); if (inet_socket < 0) { // socket returns -1 on error cerr << "Error opening socket to computer " << hostname << endl; exit(1); } // connect(sockfd, serv_addr, addrlen) if (connect(inet_socket, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) < 0) { // connect returns -1 on error cerr << "Error opening connection to coputer: " << hostname << endl; exit(1); } return inet_socket; } #endif ////////////////////////////// // // HumdrumFileBasic::makeVts -- create a !!!VTS: record for the // given Humdrum data. The function will ignore all reference records // which start with the string "!!!VTS" when calculating the VTS data. // The data is always assumbed to be encoded with Unix newlines (0x0a). // void HumdrumFileBasic::makeVts(Array& vtsstring) { HumdrumFileBasic::makeVts(vtsstring, (*this)); } void HumdrumFileBasic::makeVts(Array& vtsstring, HumdrumFileBasic& infile) { int i; SSTREAM tstream; for (i=0; i& vtsstring) { HumdrumFileBasic::makeVtsData(vtsstring, (*this)); } void HumdrumFileBasic::makeVtsData(Array& vtsstring, HumdrumFileBasic& infile) { int i; SSTREAM tstream; for (i=0; i