#!/usr/bin/env python # 20050429 def usage(): print ''' Usage: sly.py filename.sly (Processes sly file.) sly.py filename.ly (Extracts and processes sly files.) sly.py --help (Detailed help. See below.) The filename extension is required. Sly splits filename.sly vertically into separate files. The first two bytes are the field separator. The next line begins the list of target files terminated by a field separator at the beginning of a line. A new line is a field separator also. Sly extracts sly-formatted sections from lilypond file filename.ly, names and creates sly files, and processes them. The name of the generated file contains the line number of the source file. To see detailed help, use a pager: $ sly.py --help | more or redirect the output to a file: $ sly.py --help > file.txt or scroll up with some shells and operating systems. (c)2004 David Raleigh Arnold, under GNU. 20050430 ''' sys.exit(0) def helpPage(): print ''' A sly file begins with the field separator of your choice. Any two easily typed characters will do, except you can't use white space. The remainder of the first line is not necessary in a sly file, but in an embedded file you must give the name of a file to be created. All such created files must have the .sly extension to be processed by sly immediately. For example, using !! as a separator, a first line could be: !! (rest of line discarded) Then there is a list of target files: !! (memo ad. lib.) dummy4.ly !! tpt4.ly !! vlnII4.ly kazoo4.ly etc... It is terminated by a field separator at the beginning of a line. !! Memo dummy4.ly !! tpt4.ly !! vlnII4.ly kazoo4.ly !! The first data (comment) field immediately follows. If it is not empty, it is written to all files. !! Memo dummy4.ly !! tpt4.ly !! vlnII4.ly kazoo4.ly !! %1 !! Then fields are written in turn to particular files, but for safety's sake, sly will not write files if run on a bare header. A new line separates fields, unless there is a '%%' at the end of the line. !! dummy4.ly !! tpt4.ly !! vlnII4.ly kazoo4.ly !!!! dummy notes !! tpt notes !!vlnII notes !! kazoo !! dummy notes !! tpt notes !!vlnII notes !! kazoo or !! dummy4.ly !! tpt4.ly !! vlnII4.ly kazoo4.ly !! dummy notes tpt notes vlnII notes kazoo dummy notes tpt notes vlnII notes kazoo The only reason the head layout may matter is if you want to do the process in reverse. Parts will be written laid out in the same way as the header by writesly. A line which ends with a "%" will continue to the same field. Empty fields become blank lines in the target file, except for the comment field. Example: !! dummy4.ly !! tpt4.ly !! vlnII4.ly kazoo4.ly !!!! dummy notes !! tpt notes !!vlnII notes !! kazoo % more kazoo % more kazoo % more kazoo !! dummy notes !! tpt notes !!vlnII notes !! kazoo Here are some embedded sly files. If you have them in a lilypond file and run sly on it, sly files will be written and processed. %{::!! (the rest of the line is not used) dummy3.ly !! tpt3.ly !! vlnII3.ly kazoo3.ly !!!! dummy notes !! tpt notes !!vlnII notes !! kazoo !! dummy notes !! tpt notes !!vlnII notes !! kazoo %} %{::!! dummy4.ly !! tpt4.ly !! vlnII4.ly kazoo4.ly !!!! dummy notes !! tpt notes !!vlnII notes !! kazoo !! dummy notes !! tpt notes !!vlnII notes !! kazoo %} Here is another use for sly. It can make a single file, using the comment field also to reduce typing: %{::!! file.sed !! s/this/that/g s/here/there/g %} Begin an embedded sly file at the beginning of a line. Anything before or after it on the line will be discarded. The screen output can help debug your file. If you misplace a single field separator you can get real scrambled. Sly prints the contents of the first non comment field to screen, so if you use a dummy make it the first target file. Before that first field with content there is the comment field, the zeroth so to speak, which is usually best left empty. To see detailed help, use a pager: $ sly.py --help | more or redirect the output to a file: $ sly.py --help > file.txt or scroll up with some shells and systems. ''' sys.exit(0) def inputfile(): '''Acqqire initial command line. Return file name, species.''' arg = sys.argv[1:] if not arg: usage() else: arg = arg[0] if arg=="--help": helpPage() if not os.path.isfile(arg): print "Can't find file: " + arg sys.exit(0) if arg[-3:]=='.ly': species = 0 elif arg[-4:]=='.sly': species = 1 else: print "Don't know what to do with this file: " + arg sys.exit(0) return arg, species def makesly(filename): ''' open lilypond file, create sly files, return list of sly files ''' print "starting makesly" opensep = '%{::' closesep = '%}' filelist = [] iter = 0 file = '' for line in open(filename).readlines(): iter = iter + 1 if line.find(opensep)>-1: line = line.split(opensep) fsep = line[1][:2] if fsep.strip()!=fsep: print "Bad field separator on line", iter, "of", filename, print "so nothing was written." sys.exit(0) # filename is known to have '.ly' extension slyfilename = 'sly-' + filename[:-3] + str(iter) + ".sly" # rest of 1st embedded line not used: line[1][2:].strip() file = open(slyfilename,'w') timestr = ' ' + time.asctime(time.localtime(time.time())) + '\n' headline = fsep + ' ' + slyfilename + ' from ' + filename + timestr file.write(headline) print headline, filelist.append(slyfilename) continue if line.find(closesep)==-1: if file: file.write(line) if line.find(closesep)>-1: line = line.split(closesep)[0] if file: file.write(line) file = '' return filelist def rip(file): '''file is name, not open''' print "\nRipping ", file, "::::::::::::::::::::::::::" file = open(file) comment = file.readline() fsep = comment[:2] if fsep.strip()!=fsep: # if lily file, won't get this far. print "Bad field separator at top of file", print "so nothing was written." sys.exit(0) # Put whole file in memory. lines = [] for line in file: line = line.strip() lines.append(line) lines = '\n'.join(lines) all = lines.split('\n' + fsep, 1) head, data = all[0], all[1] if len(data.split('\n'))<2 and len(data.split(fsep))<2: print "Bare header. No lilypond files were written." sys.exit(0) # Process head, get names into list filelist = fsep.join(head.split('\n')) # sub fsep for newline filelist = filelist.split(fsep) names = [] print "Writing files:" for filename in filelist: name = filename.strip() print '\t\t' + name target = open(name, 'w') names.append(target) # "names" is list of opened target files print "Data written to first file, with field numbers, except that comment" print "fields (which start at 0) were not written if they were empty." # Process data... data = data.split('\n') # arrange data in lines data = fsep.join(data).rstrip() # rstrip & make newline new field data = data.split('%' + fsep) # split at lily comment if at end of field data = '%\n'.join(data) # join so now fields can have multiple lines data = data.split(fsep) count = 0 # start number of fields at one 1st comment gets mod zero volte = len(names)+1 # constant: number of targets for field in data: opnum = 0 # index sts at 0, but 2nd iter=1st file. for target in names: if (count % volte)==0: # write to all files if opnum==0: print count, field if field.strip()!='': # but only if comment exists. target.write(field + '\n') if (count % volte)==opnum+1: # zero pass is comment names[opnum].write(field + '\n') if opnum==0: print count, "field 1:", field else: print count, opnum = opnum + 1 # index next target file count = count + 1 # next field import string, os, sys, time try: infile, species = inputfile() if species==0: for file in makesly(infile): rip(file) if species==1: rip(infile) print '\n' + 'Sly is done. Redirect the above if problems. daveA' except: sys.exit(0) #print "main-errors: ", sys.exc_type, sys.exc_value