1* snopea -- a tiny "Plain Old Document" formatter for SNOBOL4
2	sp.version = '$Id: snopea.sno,v 1.34 2020-12-16 22:19:03 phil Exp $'
3
4* NOTE!  The directives are only *inspired* by "POD" format
5
6-include 'basename.sno'
7-include 'host.sno'
8
9*	snopea.debug = 1
10
11* character entity handling... A mishmash of troff and HTML!
12	snopea.charpats = TABLE()
13
14	snopea.charmaps = TABLE()
15	snopea.charpats['roff'] = "''" | "``" | '\=' |
16+			'&' ANY(&LCASE) SPAN(&LCASE) ';'
17*	Turn \- into \[en]?
18	snopea.charmaps['roff'] = snopea.charmap.roff = TABLE()
19	snopea.charmap.roff["``"] = '\*(lq'
20	snopea.charmap.roff["''"] = '\*(rq'
21	snopea.charmap.roff["&lt;"] = '<'
22	snopea.charmap.roff["&gt;"] = '>'
23	snopea.charmap.roff["&pi;"] = '\*(pi'
24	snopea.charmap.roff["&amp;"] = '&'
25	snopea.charmap.roff["\="] = '='
26
27	snopea.charpats['html'] = "''" | "``"  | '\' ANY("e-&|~'`=")
28	snopea.charmaps['html'] = snopea.charmap.html = TABLE()
29	snopea.charmap.html["``"] = '&ldquo;'
30	snopea.charmap.html["''"] = '&rdquo;'
31	snopea.charmap.html["\e"] = '\'
32	snopea.charmap.html["\-"] = '&ndash;'
33	snopea.charmap.html["\&"] = ''
34	snopea.charmap.html["\|"] = '&thinsp;'
35	snopea.charmap.html["\~"] = '&nbsp;'
36	snopea.charmap.html["\'"] = "'"
37	snopea.charmap.html["\`"] = '`'
38	snopea.charmap.html["\="] = '='
39
40	snopea.tab = CHAR(9)
41	snopea.ws = SPAN(' ' snopea.tab)
42	snopea.bws = BREAK(' ' snopea.tab)
43
44	HOST() POS(0) BREAK(':') ':' BREAK(':') ':' REM . snopea.snovers
45
46	DEFINE('snopea(ifile,ofile,page,sect,format)'
47+			'font,efont,endlist,lsep,pre,chr,ctab,numb,t1,t2,pat,'
48+			'date,text,snomod,line,inpea,needspace,after,'
49+			'oline,text,iunit,ounit,lbl,cmd') :(snopea.end)
50snopea
51	iunit = IO_FINDUNIT()
52	INPUT(.in, iunit,, ifile)			:S(sp.oo)
53	TERMINAL = 'snopea: could not open ' ifile	:(FRETURN)
54
55sp.oo	DELETE(ofile)
56	ounit = IO_FINDUNIT()
57	OUTPUT(.out, ounit,, ofile)			:S(sp.begin)
58	TERMINAL = 'snopea: could not open ' ofile
59	ENDFILE(iunit)
60	DETACH(.in)					:(FRETURN)
61
62sp.begin
63	TERMINAL = IDENT(page) "snopea: need page name"	:S(FRETURN)
64	TERMINAL = IDENT(sect) "snopea: need section"	:S(FRETURN)
65	format = IDENT(format) 'roff'
66
67	page = REPLACE(page, &LCASE, &UCASE)
68	date = HOST(HOST_VERSION_DATE)
69	cmd = 'head'					:(sp.dispatch2)
70
71****************
72sp.top	line = in					:F(sp.eof)
73* strip leading stars
74	line POS(0) SPAN('*') =
75* check for =cmd
76	cmd = 'XXX'
77	line POS(0) '=' SPAN(&LCASE) . cmd REM . rest	:S(sp.cmd)
78* no command:
79	IDENT(inpea)					:S(sp.top)
80* in pea mode, no command:
81	IDENT(line)					:S(sp.eop)
82* non-empty line, output paragraph break if needed
83	cmd = DIFFER(needspace) 'para'			:S(sp.cmd)
84* handle text, clear output line holder
85sp.text	oline =
86* font loop: here back from 'font' dispatch
87sp.floop
88	line POS(0) BREAKX('BICL') . pre
89+			ANY('BICL') . font '<' BREAK('>') . txt '>' =
90+							:f(sp.fdone)
91* here with pre, font & txt:
92	cmd = 'font'					:(sp.dispatch)
93
94* done with fonts, perform character substitutions
95sp.fdone
96	line = oline line
97	oline =
98
99* loop for character substitutions
100sp.cloop
101	line POS(0) ARB . pre snopea.charpats[format] . chr = :f(sp.cdone)
102	oline = oline pre snopea.charmaps[format][chr]	:(sp.cloop)
103
104* done with character substitutions
105sp.cdone
106	cmd = 'oline'
107	oline = oline line				:(sp.dispatch2)
108
109* here after 'oline' command
110sp.out	out = vdiffer(lsep)
111	text = 1
112	:(sp.top)
113
114****
115* saw end of input file
116sp.eof	cmd = 'eof'					:(sp.dispatch)
117
118****
119* saw end of paragraph (blank line)
120sp.eop	needspace = text
121	text =						:(sp.top)
122
123****************
124* here to dispatch to a command, clears argument
125sp.dispatch
126	rest =
127* here with user command, remove leading spaces on argument
128sp.cmd	rest POS(0) snopea.ws =
129	inpea = 1
130
131**** dispatch a command without altering "inpea"
132* NOTE!! commands may return to someplace OTHER than sp.top!!!
133sp.dispatch2
134	lsep =
135	TERMINAL = DIFFER(snopea.debug) '>>> ' cmd
136	lbl = 'sp.' format '.' cmd
137	LABEL(lbl)					:S($lbl)
138	TERMINAL = 'Unknown command ' cmd		:(sp.top)
139
140****
141* here from 'eof' command processing
142sp.close
143	ENDFILE(iunit)
144	ENDFILE(ounit)
145	DETACH(.in)
146	DETACH(.out)					:(return)
147
148****************
149* command processing for *roff:
150
151sp.roff.head
152	out = '.\" generated by ' sp.version
153
154*	if nroff, give "ragged right" output':
155	out = '.if n .ad l'
156	out = ".ie '\*[.T]'ascii' \{\"
157	out = '.	ds lq \&"\"'
158	out = '.	ds rq \&"\"'
159	out = ".	ds pi \fIpi\fP"
160	out = ".\}"
161	out = ".el \{\"
162	out = ".	ds rq ''"
163	out = ".	ds lq ``"
164	out = ".	ds pi \[*p]"
165	out = ".\}"
166
167*	disable hyphenation:
168	out = '.nh'
169
170	out = '.TH ' page ' ' sect ' "' date '" "' snopea.snovers '"'
171+		' "CSNOBOL4 Manual"'
172
173	indent = 4
174	text =
175	regfont = '\fR'					:(sp.top)
176
177sp.roff.pea						:(sp.top)
178
179* internal (blank line seen)
180sp.roff.para
181	out = ".PP"
182	needspace = numb =				:(sp.text)
183
184sp.roff.cut
185	inpea =						:(sp.top)
186
187sp.roff.font
188	oline = IDENT(font, 'L') oline pre txt		:s(sp.floop)
189	font = (IDENT(font, 'C') '\f(CW', '\f' font)
190	oline = oline pre font txt '\fP'		:(sp.floop)
191
192sp.roff.oline
193	oline = DIFFER(oline ? POS(0) ANY(".'")) '\&' oline
194	out = oline					:(sp.out)
195
196sp.roff.sect
197	rest POS(0) snopea.ws =
198	out = '.SH "' rest '"'
199	out = '.nh'
200	needspace =					:(sp.top)
201
202sp.roff.subsect
203	rest POS(0) snopea.ws =
204	out = '.SS "' rest '"'
205	out = '.nh'
206	needspace =					:(sp.top)
207
208* set indent level for items
209sp.roff.indent
210	indent = rest
211	needspace =					:(sp.top)
212
213* unadorned item
214sp.roff.item
215	needspace =
216	rest POS(0) snopea.ws =
217	out = IDENT(rest) '.IP'				:s(sp.top)
218	out = '.TP ' indent
219	line = rest					:(sp.text)
220
221* bullet item
222sp.roff.bull
223	out = '.IP \(bu'
224	needspace =
225	line = rest					:(sp.text)
226
227* numbered item
228sp.roff.nitem
229	numb = (VDIFFER(numb), 1)
230	out = '.IP ' numb
231	numb = numb + 1
232	needspace =
233	line = rest					:(sp.text)
234
235sp.roff.code
236* constant width font:
237	out = '.ft CW'
238* fresh line
239	out = '.br'
240* soft page break: need 10 lines
241	out = '.ne 10'
242* indent:
243	out = '.RS ' indent
244* no hyphenation, formatting:
245	out = '.nh'
246	out = '.nf'					:(sp.top)
247
248* back to "Roman" typeface:
249sp.roff.ecode
250	out = '.ft R'
251* fill, no hyphenation:
252	out = '.fi'
253	out = '.nh'
254* unindent:
255	out = '.RE'					:(sp.top)
256
257sp.roff.break
258	out = '.br'					:(sp.top)
259
260sp.roff.table
261	out = '.PP'
262	rest POS(0) snopea.ws =
263	out = DIFFER(rest) '.ta ' rest
264	out = '.nf'					:(sp.top)
265
266sp.roff.row
267	rest POS(0) snopea.ws =
268	line = rest					:(sp.text)
269
270sp.roff.etable
271	out = '.fi'					:(sp.top)
272
273sp.roff.anchor						:(sp.top)
274
275sp.roff.link
276	rest = rest ' '
277	rest POS(0) snopea.ws =
278	rest POS(0) snopea.bws . t1 =
279	rest POS(0) snopea.ws =
280	line = (VDIFFER(rest), t1)			:s(sp.text)
281
282* ADD NEW OPS ABOVE THIS LINE
283
284sp.roff.eof						:(sp.close)
285
286****************
287* command processing for html
288
289sp.html.head
290	out = '<!-- generated by ' sp.version ' -->'
291
292	out = '<html>'
293	out = '<head>'
294	htext =  page '(' sect ') | ' snopea.snovers ' | ' date
295	out = ' <title>' htext '</title>'
296* _COULD_ output <style> here, but trying to work with lynx!
297	out = '</head>'
298	out = '<body>'
299	out = '<h1>' htext '</h1>'
300	indent = 4
301	text =
302
303sp.html.pea						:(sp.top)
304
305* internal (blank line seen)
306sp.html.para
307	out = VDIFFER(endlist)
308	out = '<p>'
309	needspace = endlist =				:(sp.text)
310
311sp.html.cut						:(sp.roff.cut)
312
313sp.html.font
314	oline = IDENT(font, 'L') oline pre '<a href="' txt '">' txt '</a>' :s(sp.floop)
315	font = ident(font, 'R')				:s(sp.html.font2)
316	font = ident(font, 'C') 'TT'
317sp.html.font2
318	efont = ident(font)				:s(sp.html.font3)
319	efont = '</' font '>'
320	font = '<' font '>'
321
322sp.html.font3
323	oline = oline pre font txt efont		:(sp.floop)
324
325* MASSIVE CROCK!!! detect snobol4 man page refs, and turn into links!!
326sp.html.oline
327+	t1 =
328	pat = POS(0) BREAKX('<') . pre
329+	       ('<B>'
330+	         (('snobol4' | 'sdb' | 'snolib' | 'snopea') BREAK('<')) . pg
331+               '</B>'
332+	        '(' ANY('137') . sec ')'
333+              ) . full
334sp.html.oline1
335+	IDENT(oline)					:s(sp.html.oline2)
336	oline pat =					:f(sp.html.oline2)
337	t1 = t1 pre '<a href="' pg '.' sec '.html">' full '</a>'
338	:(sp.html.oline1)
339sp.html.oline2
340+	out = t1 oline
341	out = VDIFFER(after)
342	after =						:(sp.out)
343
344sp.html.sect
345	out = vdiffer(endlist)
346	endlist =
347	out = '<h2>' rest '</h2>'
348	needspace =					:(sp.top)
349
350sp.html.subsect
351	out = vdiffer(endlist)
352	endlist =
353	out = '<h3>' rest '</h3>'
354	needspace =					:(sp.top)
355
356* set indent level for items
357sp.html.indent
358	indent = rest
359	needspace =					:(sp.top)
360
361* definition list
362sp.html.item
363	differ(endlist)					:s(sp.html.item2)
364	out = '<dl>'
365	endlist = '</dl>'
366sp.html.item2
367* don't REALLY want whitespace immediately after a (sub)section header:
368	out = '<p>'
369	out = '<dt>'
370	after = '<dd>'
371	needspace =
372	line = rest					:(sp.text)
373
374* bullet item
375sp.html.bull
376	differ(endlist)					:s(sp.html.bull2)
377	out = '<p>'
378	out = '<ul>'
379	endlist = '</ul>'
380sp.html.bull2
381	out = '<li>'
382	needspace =
383	line = rest					:(sp.text)
384
385* numbered item
386sp.html.nitem
387	differ(endlist)					:s(sp.html.bull2)
388	out = '<ol>'
389	endlist = '</ol>'				:(sp.html.bull2)
390
391sp.html.code
392	out = '<pre>'					:(sp.top)
393
394sp.html.ecode
395	out = '</pre>'					:(sp.top)
396
397sp.html.break
398	out = '<br>'					:(sp.top)
399
400sp.html.table
401	out = '<p>'
402	out = '<table>'					:(sp.top)
403
404sp.html.row
405	rest = '<tr>' snopea.tab rest
406sp.html.rowloop
407	rest snopea.tab = '<td>'			:s(sp.html.rowloop)
408	line = rest					:(sp.text)
409
410sp.html.etable
411	out = '</table>'				:(sp.top)
412
413sp.html.anchor
414	rest POS(0) snopea.ws =
415	out = '<a name="' rest '"></a>'			:(sp.top)
416
417sp.html.link
418	rest = rest ' '
419	rest POS(0) snopea.ws =
420	rest POS(0) snopea.bws . t1 =
421	rest POS(0) snopea.ws =
422	line = '<a href="' t1 '">' (VDIFFER(rest), t1) '</a>' :(sp.text)
423
424* ADD NEW OPS ABOVE THIS LINE
425
426sp.html.eof
427	out = vdiffer(endlist)
428*	output footer?  link to csnobol4 home page??
429	out = '</body>'
430	out = '</html>'					:(sp.close)
431
432****************
433
434snopea.end
435