Checking the validity of your JMD

One of the things I need to do on a regular basis is check the JMD of an installation, when things don’t seem to be running as they should. More often than not, someone edited the JMD by hand, missed a quote mark – causing havoc with the table alignments.

At other times I just want to be able to read the JMD so that I know what steps are being taken, without having to load/reload various JMDs. And trust me, reading a raw JMD is not fun…

So I came up with the following python script. I’ll be putting it on my personal website at some point, so you can use it there too. In the mean time, have a go at this, if you have a Python CGI server… TableJMD 0.2a


The following highlighting is a backup taken from the corresponging pygments.org post – in case it gets deleted from pygments.org one day…

#! /Program Files/Python25/python

import re,sys,cgi

helpnote = """Table JMD 1.1
(C) 2009 Tai Kedzierski
http://blogs.adobe.com/an_tai/

This tool reads a JMD and writes HTML to STDOUT representing the JMD in a table, as it would be parsed by Central's JfServer.
It also highlights substitution variables not surrounded by double quote marks.

This can either be used from the command line, or as a CGI script on a web site.

From the command line, type:

python tablejmd.py PATH

where PATH is the path to the JMD file
"""

class JMDInvalidLine(Exception):

	def __init__(self,message):
		Exception.__init__(self,message)# = "The line was invalid"

# don't fail silently!
sys.stderr = sys.stdout

def readjmdfile(filename):

	readjmd( open(filename,'rU') )

def readjmd(fileh):

	jmdline = '.'
	
	job_t = dict()
	task_t = dict()

	print_t = dict()
	memory_t = dict()
	
	while jmdline:

		line = fileh.readline()
		if line == '': # EOF

			break
		if re.match(r'(^#)|(^\s*$)',line): # comment or empty line

			continue
		jmdline = jmdsplit(line)
		
		if jmdline == None:

			print "<p>Error parsing line: %s</p>" % (line,)
			continue

		
		if jmdline[0] == '!f':
			if job_t.has_key(jmdline[1] ):

				job_t[jmdline[1] ].append(jmdline)
			else:

				job_t[jmdline[1] ] = [jmdline]
		elif jmdline[0] == '!x':

			if task_t.has_key(jmdline[1] ):
				task_t[jmdline[1] ].append(jmdline)

			else:
				task_t[jmdline[1] ] = [jmdline]

		elif jmdline[0] == '!p':
			if print_t.has_key(jmdline[1] ):

				print_t[jmdline[1] ].append(jmdline)
			else:

				print_t[jmdline[1] ] = [jmdline]
		elif jmdline[0] == '!m':

			if memory_t.has_key(jmdline[1] ):
				memory_t[jmdline[1] ].append(jmdline)

			else:
				memory_t[jmdline[1] ] = [jmdline]

	
	print "<h2>Job table ["+str(len(job_t))+" entries]</h2>"

	print "<table><tbody>"
	print r'<th>Job name</th> <th>Printer name</th> <th>Form file</th> <th>Preamble file</th> <th>Macro number</th> <th>Load flag</th> <th>Task id</th> <th>Input file</th> <th>Output file</th> <th>On error</th> <th>Comments</th>'

	dumptablelines(job_t)
	print "</tbody></table>"
	
	print "<h2>Task table ["+str(len(task_t))+" entries]</h2>"

	print '<p><font color="blue">Blue</font>: not surrounded by doubled double-quotes<br /><font color="red">Red</font>: substitution name does not end with "."</p>'

	print "<table><tbody>"
	print r'<th>Task id</th> <th>[This column reserved]</th> <th>Program name</th> <th>Program options</th> <th>Comments</th>'

	dumptablelines(task_t)
	print "</tbody></table>"
	
	print "<h2>Printer table ["+str(len(print_t))+" entries]</h2>"

	print "<table><tbody>"
	print r'<th>Printer name</th> <th>Printer id</th> <th>Physical device</th> <th>Print agent options</th> <th>Priority</th> <th>Comments</th>'

	dumptablelines(print_t)
	print "</tbody></table>"
	
	print "<h2>Managed Memory table ["+str(len(memory_t))+" entries]</h2>"

	print "<table><tbody>"
	print r'<th>Printer id</th> <th>Managed Memory [bytes]</th> <th>Comments</th>'

	dumptablelines(memory_t)
	print "</tbody></table>"

def dumptablelines(table_t):

	keyset = table_t.keys()
	keyset.sort()
	for entryset in keyset:

		for entry in range(len(table_t[entryset])):
			print "<tr>"

			for col in range(1,len(table_t[entryset][entry]) ):

				print "<td>",table_t[entryset][entry][col],"</td>",
			print "</tr>"

def jmdsplit(line):
	'''INPUT: a line of text from the JMD
OUTPUT: the line, tokenized into a list
'''
	if line[0] != '!':# any meaningful line must start with '!'

		return None
	oline = line
	
	if not line[1] in [

		'f', # job
		'x', # task
		'p', # printer

		'm']: # shared memory
		raise JMDInvalidLine(line)
	
	jmdline = []

	
	try:
		while line:
			token, line = readfirsttoken(line)

			token = atprocess(token) # identify substitution variables and possible errors
			jmdline.append(token)

	except JMDInvalidLine,msg:
		print msg,':',oline

	
	return jmdline

def atprocess(token):
	pat = re.compile(r'((?<!"")[a-zA-Z-]*@[a-zA-Z]+\.?|@[a-zA-Z]+\.(?!""))')

	# NO r'((?<!"")[a-zA-Z-]*@[a-zA-Z]+\.?|@[a-zA-Z]+\.?(?!""))'
	# OK r'((?<!"")[a-zA-Z-]*@[a-zA-Z]+\.?|@[a-zA-Z]+\.(?!""))'
	pat2 = re.compile(r'(@[a-zA-Z]+)(?=[^\.a-zA-Z#])')

	
	if not type(token) == str:
		print type(token)

	
	token = pat.sub(r'<font color="blue">\1</font>',token)

	token = pat2.sub(r'<font color="red">\1</font>',token)

	
	return token
	

def readfirsttoken(line):
	line = re.match(r"\s*(.*)",line).group(1) # remove leading whitespace

	
	if line == '':
		return ('','')
	
	pos = 0

	text = ''
	inquote = False
	inarg = True

	while inarg and pos < len(line):
		if line[pos] == ' ' and not inquote:

			inarg = False
		elif line[pos] == '"' and not inquote:

			inquote = True
			text += line[pos]

		elif line[pos] == '"' and inquote:
			if len(line) == pos+1:

				inarg = False
				inquote = False
			elif line[pos+1] == '"':

				text += '"'
				pos += 1
			else:

				inquote = False
				inarg = False
			text += line[pos]

		else:
			text += line[pos]
		pos += 1

	if inquote:
		raise JMDInvalidLine("Unclosed quote")
	
	return (text,line[len(text):])

# RUNTIME ================================================

def printJMD(thefile):
	print """<html><head><style type="text/css">

body {font-size:9pt}
td, th {
	border-left:2px solid #000000;
	padding: 3px;
	margin: 2px;
	font-size:9pt;
	background-color:#ffffcc;

}

th {
	background-color: #ccffcc;
}
</style>
<title>Table JMD - Done</title>
</head><body>

<h1>Table JMD</h1>"""
	print "<!-- ",helpnote," -->"
	readjmd(thefile)

	print "</body></html>"

def printform():
	print """<html>

<head><title>Table JMD</title></head>
<body>
<h1>Table JMD</h1>

<form action="tablejmd.py" method="POST" name="logprint" enctype="multipart/form-data">

<p>Check your JMD Tables.</p>
File: <input type="file" name="jmdfile" /><br />
<input type="submit" />

</form>
</body></html>
"""

# RUNTIME

#print "Content-Type: text/plain"
#print

fs = cgi.FieldStorage()

if fs.has_key('jmdfile'):
	print "Content-Type: text/html"
	print

	
	printJMD(fs['jmdfile'].file)
	
	#print '<p><b>Displaying JMD table for '+fs['jmdfile'].filename+'</b></p>'

	
elif len(sys.argv) > 2 and sys.argv[1] == '-f':

	printJMD(sys.argv[2])
elif len(sys.argv) > 1:

	if re.match(r'-h|/h|--help|/\?|-\?',sys.argv[1]):

		print helpnote
else:
	print "Content-Type: text/html"
	print
	printform()