jrweave 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. #! /usr/local/bin/gawk -f
  2. #
  3. # Copyright (C) 2013, 2014, 2015 Arnold David Robbins
  4. #
  5. # This file is part of TexiWeb Jr., a literate programming system.
  6. #
  7. # TexiWeb Jr. is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # TexiWeb Jr. is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  20. #
  21. # Up-to-date source code for TexiWeb Jr. can be obtained via
  22. # Git from github:
  23. #
  24. # git clone http://github.com/arnoldrobbins/texiwebjr
  25. #
  26. BEGIN {
  27. TRUE = 1
  28. FALSE = 0
  29. File_chunk_pattern = "^@\\(([^)]+)@\\)[[:space:]]*=[[:space:]]*$"
  30. Code_chunk_pattern = "^@" "<(.+)" "@>[[:space:]]*=[[:space:]]*$"
  31. Chunk_name_pattern = "@<[^>\n]+@>"
  32. }
  33. # Error checking:
  34. # Use brackets to avoid triggering the warning on ourselves!
  35. /(^<[@])|(>[@]([[:space:]]*=[[:space:]]*)?$)/ {
  36. # Ditto, with concatenation
  37. warning("<" "@ or >" "@ used instead of @" "< or @" ">\n\t%s\n",
  38. $0)
  39. }
  40. END {
  41. check_unfinished()
  42. }
  43. BEGIN {
  44. if (ARGC < 2)
  45. fatal(_"usage: jrweave file.twjr [...]\n")
  46. Pass = 1
  47. n = ARGC
  48. ARGV[ARGC++] = "Pass=2"
  49. for (i = 1; i < n; i++) {
  50. if (ARGV[i] == "-" || ARGV[i] == "/dev/stdin")
  51. fatal(_"jrweave: standard input not allowed\n")
  52. ARGV[ARGC++] = ARGV[i]
  53. }
  54. }
  55. Pass == 2 && FNR == 1 && Debug ~ /pass2/ {
  56. junk++
  57. }
  58. BEGIN {
  59. print_do_not_edit(ARGV[1])
  60. }
  61. # print_do_not_edit --- create and print warning
  62. function print_do_not_edit(filename, i, pl, pr, l, s, t)
  63. {
  64. t = _"DO NOT EDIT THIS FILE!!!!"
  65. if (ARGC > 4) # more than one file
  66. s = sprintf(_"It was created by jrweave from `%s' (or maybe others).",
  67. filename)
  68. else
  69. s = sprintf(_"It was created by jrweave from `%s'.", filename)
  70. l = length(s)
  71. pl = (l - length(t)) / 2 # padding on left side
  72. pr = l - (pl + length(t)) # padding on right side
  73. if (pl * 2 < l) # account for odd lengths
  74. pr++
  75. for (i = 1; i <= l + 4; i++)
  76. printf("%%")
  77. printf "\n"
  78. # print the titles with their padding
  79. printf("%% %*s%s%*s %%\n", pl, " ", t, pr, " ")
  80. printf("%% %s %%\n", s)
  81. for (i = 1; i <= l + 4; i++)
  82. printf("%%")
  83. printf "\n"
  84. }
  85. # check_unfinished --- print a fatal error when an unfinished code or
  86. # file chunk is detected. Also ifweave / iftangle.
  87. function check_unfinished()
  88. {
  89. if (Flags["file chunk"])
  90. fatal(_"unfinished file chunk (started at %s)\n",
  91. Line_numbers["file chunk"])
  92. else if (Flags["code chunk"])
  93. fatal(_"unfinished code chunk (started at %s)\n",
  94. Line_numbers["code chunk"])
  95. if ("ifweave" in Line_numbers)
  96. fatal(_"unfinished @ifweave section (started at %s)\n",
  97. Line_numbers["ifweave"])
  98. if ("iftangle" in Line_numbers)
  99. fatal(_"unfinished @iftangle section (started at %s)\n",
  100. Line_numbers["iftangle"])
  101. }
  102. # strip_out_name --- get the name from name
  103. function strip_out_name(name, l)
  104. {
  105. l = length(name)
  106. name = substr(name, 3, l - 4)
  107. return name
  108. }
  109. # Helper functions
  110. # message --- write a particular kind of message out to stderr
  111. function message(msg, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
  112. {
  113. printf("%s:%d: %s: " format, FILENAME, FNR, msg,
  114. a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) > "/dev/stderr"
  115. if (substr(format, length(format), 1) != "\n")
  116. printf("\n") > "/dev/stderr"
  117. }
  118. # fatal --- print a fatal error message and exit.
  119. # No varargs, so fake it with lots of parameters.
  120. function fatal(format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
  121. {
  122. message(_"fatal", format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
  123. exit 1
  124. }
  125. # warning --- print a warning message to stderr
  126. # No varargs, so fake it with lots of parameters.
  127. function warning(format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
  128. {
  129. message(_"warning", format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
  130. }
  131. # join.awk --- join an array into a string
  132. #
  133. # Arnold Robbins, arnold@skeeve.com, Public Domain
  134. # May 1993
  135. function join(array, start, end, sep, result, i)
  136. {
  137. if (sep == "")
  138. sep = " "
  139. else if (sep == SUBSEP) # magic value
  140. sep = ""
  141. result = array[start]
  142. for (i = start + 1; i <= end; i++)
  143. result = result sep array[i]
  144. return result
  145. }
  146. # iftangle lines should be removed
  147. /^@iftangle[[:space:]]*$/, /^@end iftangle[[:space:]]*$/ {
  148. if (Pass == 1)
  149. next
  150. if ("ifweave" in Line_numbers)
  151. fatal(_"cannot nest @iftangle inside @ifweave\n")
  152. # start of construct, save line number
  153. if (/^@iftangle[[:space:]]*$/)
  154. Line_numbers["iftangle"] = (FILENAME ":" FNR)
  155. # end of construct, delete line number
  156. if (/^@end iftangle[[:space:]]*$/)
  157. delete Line_numbers["iftangle"]
  158. # simply skip these lines, this is weaving
  159. next
  160. }
  161. # For weaving we remove the bracketing control lines and let anything
  162. # in between fall through.
  163. /^@ifweave[[:space:]]*$/, /^@end ifweave[[:space:]]*$/ {
  164. if (Pass == 1)
  165. next
  166. if ("iftangle" in Line_numbers)
  167. fatal(_"cannot nest @ifweave inside @iftangle\n")
  168. # start of construct, save line number, skip this line
  169. if (/^@ifweave[[:space:]]*$/) {
  170. Line_numbers["ifweave"] = (FILENAME ":" FNR)
  171. next
  172. }
  173. # end of construct, delete line number, skip this line
  174. if (/^@end ifweave[[:space:]]*$/) {
  175. delete Line_numbers["ifweave"]
  176. next
  177. }
  178. # otherwise fall through into the rest of the code
  179. }
  180. /^@file_update_recipe[[:space:]]*$/,
  181. /^@end file_update_recipe[[:space:]]*$/ {
  182. next
  183. }
  184. /^@file_update[[:space:]]/ {
  185. next
  186. }
  187. /^@post_create[[:space:]]+/ {
  188. next
  189. }
  190. BEGIN {
  191. Example_start = "@example"
  192. Example_end = "@end example"
  193. }
  194. Pass == 2 && /^@use_smallexample[[:space:]]*$/ {
  195. Example_start = "@smallexample"
  196. Example_end = "@end smallexample"
  197. next
  198. }
  199. Pass == 2 && /^@use_example[[:space:]]*$/ {
  200. Example_start = "@example"
  201. Example_end = "@end example"
  202. next
  203. }
  204. Pass == 2 && /^@titlepage/ {
  205. print "@c Let texinfo.tex give us full section titles"
  206. print "@xrefautomaticsectiontitle on"
  207. print ""
  208. print "@c Start extra commands added by jrweave\n"
  209. print "@c For HTML, spell out email addresses, to avoid problems with"
  210. print "@c address harvesters for spammers."
  211. print "@ifhtml"
  212. print "@macro EMAIL{real,spelled}"
  213. print "``\\spelled\\''"
  214. print "@end macro"
  215. print "@end ifhtml"
  216. print "@ifnothtml"
  217. print "@macro EMAIL{real,spelled}"
  218. print "@email{\\real\\}"
  219. print "@end macro"
  220. print "@end ifnothtml"
  221. print ""
  222. print "@macro FIXME{text}"
  223. print "@strong{FIXME}: \\text\\"
  224. print "@end macro"
  225. print ""
  226. print "@macro oldnum{value}"
  227. print "\\value\\"
  228. print "@end macro"
  229. print ""
  230. print "@tex"
  231. print "\\gdef\\oldnum#1{\\begingroup\\oldstyle #1\\endgroup}%"
  232. print "@end tex"
  233. print "\n@c End extra commands added by jrweave"
  234. print ""
  235. print # print the line
  236. next
  237. }
  238. /^@numberedchunks[[:space:]]*$/ {
  239. if (Pass == 1)
  240. Numbered_chunks = TRUE
  241. next
  242. }
  243. /^@file_chunk_full_defs[[:space:]]*$/ {
  244. if (Pass == 1)
  245. Print_file_full_defs = TRUE
  246. next
  247. }
  248. Pass == 2 && /^@sidebar[[:space:]]+/ {
  249. sub(/^@sidebar[[:space:]]+/, "", $0)
  250. Sidebar_title = $0
  251. Sidebar_body = ""
  252. Collecting_sidebar = TRUE
  253. next
  254. }
  255. Pass == 2 && /^@end[[:space:]]+sidebar[[:space:]]*$/ {
  256. Collecting_sidebar = FALSE
  257. printf "@cindex sidebar, %s\n", Sidebar_title
  258. printf "@ifdocbook\n"
  259. printf "@docbook\n"
  260. printf "<sidebar><title>%s</title>\n", Sidebar_title
  261. printf "@end docbook\n"
  262. print Sidebar_body
  263. print ""
  264. printf "@docbook\n"
  265. printf "</sidebar>\n"
  266. printf "@end docbook\n"
  267. printf "@end ifdocbook\n\n"
  268. printf "@ifnotdocbook\n"
  269. printf "@cartouche\n"
  270. printf "@center @b{%s}\n\n", Sidebar_title
  271. print "@noindent"
  272. sub(/^\n*/, "", Sidebar_body) # remove initial newlines
  273. print Sidebar_body
  274. printf "@end cartouche\n"
  275. printf "@end ifnotdocbook\n"
  276. Sidebar_body = ""
  277. next
  278. }
  279. Pass == 2 && Collecting_sidebar {
  280. Sidebar_body = Sidebar_body "\n" $0
  281. next
  282. }
  283. Pass == 2 && /^@dquotexrefs[[:space:]]*$/ {
  284. print "@tex"
  285. # print "%\\gdef\\xrefprintnodename#1{{\\it #1}}"
  286. print "\\gdef\\xrefprintnodename#1{``#1''}"
  287. print "@end tex"
  288. next
  289. }
  290. Pass == 2 && /^@pdflinkcolor[[:space:]]*.*$/ {
  291. if (NF != 1 && NF != 4)
  292. fatal(_"@pdflinkcolor: wrong number of arguments\n")
  293. if (NF == 1)
  294. Link_color = Dark_red
  295. else {
  296. $1 = ""
  297. $0 = $0
  298. Link_color = $0
  299. }
  300. print "@tex"
  301. print "\\gdef\\linkcolor{" Link_color "}"
  302. print "@end tex"
  303. next
  304. }
  305. Pass == 2 && /^@urllinkcolor[[:space:]]*.*$/ {
  306. if (NF != 1 && NF != 4)
  307. fatal(_"@urllinkcolor: wrong number of arguments\n")
  308. if (NF == 1)
  309. URL_color = Dark_red
  310. else {
  311. $1 = ""
  312. $0 = $0
  313. URL_color = $0
  314. }
  315. print "@tex"
  316. print "\\gdef\\urlcolor{" URL_color "}"
  317. print "@end tex"
  318. next
  319. }
  320. Pass == 2 && /^@hideurls[[:space:]]*$/ {
  321. print "@tex"
  322. print "\\global\\urefurlonlylinktrue" # NOTE: *not* \gdef
  323. print "@end tex"
  324. next
  325. }
  326. Pass == 2 && /^@allowindexbraces[[:space:]]*$/ {
  327. print "@tex"
  328. print "\\global\\usebracesinindexestrue" # NOTE: *not* \gdef
  329. print "@end tex"
  330. next
  331. }
  332. BEGIN {
  333. Dark_red = "0.5 0.09 0.12"
  334. }
  335. Pass == 2 && /^@c %\*\*end of header/ {
  336. print # print the line
  337. print "\n@c Extra indices added by jrweave"
  338. print "@defindex cd @c chunk definition"
  339. print "@defindex cr @c chunk reference"
  340. next
  341. }
  342. $0 ~ File_chunk_pattern {
  343. Chunk_type = "file chunk"
  344. Pattern = File_chunk_pattern
  345. Debug_pat = "filename"
  346. new_chunk = gensub(Pattern, "\\1", 1)
  347. if (Flags[Chunk_type]) {
  348. fatal(_"%s start of %s found while still collecting %s\n",
  349. Chunk_type, new_chunk, Current_chunk)
  350. }
  351. check_unfinished()
  352. Flags[Chunk_type] = TRUE
  353. Line_numbers[Chunk_type] = (FILENAME ":" FNR)
  354. Current_chunk = new_chunk
  355. Chunk_info[Current_chunk]["type"] = Chunk_type
  356. if (Debug ~ Debug_pat)
  357. printf("saw new %s %s\n", Debug_pat, Current_chunk) > "/dev/stderr"
  358. next
  359. }
  360. $0 ~ Code_chunk_pattern {
  361. Chunk_type = "code chunk"
  362. Pattern = Code_chunk_pattern
  363. Debug_pat = "code"
  364. new_chunk = gensub(Pattern, "\\1", 1)
  365. if (Flags[Chunk_type]) {
  366. fatal(_"%s start of %s found while still collecting %s\n",
  367. Chunk_type, new_chunk, Current_chunk)
  368. }
  369. check_unfinished()
  370. Flags[Chunk_type] = TRUE
  371. Line_numbers[Chunk_type] = (FILENAME ":" FNR)
  372. Current_chunk = new_chunk
  373. Chunk_info[Current_chunk]["type"] = Chunk_type
  374. if (Debug ~ Debug_pat)
  375. printf("saw new %s %s\n", Debug_pat, Current_chunk) > "/dev/stderr"
  376. next
  377. }
  378. /^@[[:space:]]*$/ {
  379. if (Flags["file chunk"])
  380. end_file_gathering()
  381. else if (Flags["code chunk"])
  382. end_code_gathering()
  383. else
  384. warning(_"unmatched terminating @-sign: ignored\n")
  385. Chunk_lines = ""
  386. Flags[Chunk_type] = FALSE
  387. Line_numbers[Chunk_type] = ""
  388. Chunk_type = ""
  389. next
  390. }
  391. Flags["file chunk"] || Flags["code chunk"] {
  392. if (Chunk_lines == "")
  393. Chunk_lines = $0
  394. else
  395. Chunk_lines = Chunk_lines "\n" $0
  396. next
  397. }
  398. function end_chunk_gathering()
  399. {
  400. if (Pass == 1) {
  401. collect_chunk_info()
  402. Chunk_lines = ""
  403. Flags[Chunk_type] = FALSE
  404. } else
  405. print_out_chunk()
  406. }
  407. function end_file_gathering()
  408. {
  409. end_chunk_gathering()
  410. }
  411. function collect_chunk_info( i, n, x, called, junk)
  412. {
  413. # Current_chunk, Chunk_type already set by initial code
  414. # Chunk number:
  415. if (! ("chunk number" in Chunk_info[Current_chunk])) {
  416. Chunk_info[Current_chunk]["chunk number"] = \
  417. ++Chunk_numbers[Chunk_type]
  418. }
  419. # Definition instance
  420. Chunk_info[Current_chunk]["defn"]++
  421. # Get names of called chunks into called
  422. n = split(Chunk_lines, junk, Chunk_name_pattern, called)
  423. # Add ourselves to the callers
  424. for (i in called) {
  425. x = strip_out_name(called[i])
  426. Chunk_info[x]["callers"][Current_chunk] = TRUE
  427. }
  428. }
  429. function print_out_chunk( x, y, n, i, parts, names,
  430. name, anchor, chunk_being_used)
  431. {
  432. # Redefinition instance
  433. Chunk_info[Current_chunk]["redefn"]++
  434. print "@need 400"
  435. anchor = format_anchor(Current_chunk,
  436. Chunk_info[Current_chunk]["redefn"],
  437. Chunk_info[Current_chunk]["defn"])
  438. printf("%s\n", anchor)
  439. x = expand_tabs(Chunk_lines, Tabstop)
  440. # extract code chunks
  441. n = split(x, parts, Chunk_name_pattern, names)
  442. # escape special chars in parts of code that aren't chunk names
  443. for (i = 1; i in parts; i++)
  444. gsub(/[@{}]/, "@&", parts[i])
  445. y = parts[1]
  446. if (n > 1) { # embedded chunk names
  447. for (i = 1; i in names; i++) {
  448. name = strip_out_name(names[i])
  449. chunk_being_used = \
  450. format_chunk_name(name, Chunk_info[name]["chunk number"],
  451. Chunk_info[name]["type"])
  452. printf("@crindex %s, use\n", chunk_being_used)
  453. y = y chunk_being_used
  454. y = y parts[i+1]
  455. }
  456. }
  457. chunk_being_defined = \
  458. format_chunk_name(Current_chunk,
  459. Chunk_info[Current_chunk]["chunk number"],
  460. Chunk_type)
  461. printf("@cdindex %s, definition\n", chunk_being_defined)
  462. printf("@noindent\n%s %s@equiv{}\n",
  463. chunk_being_defined,
  464. Chunk_info[Current_chunk]["redefn"] == 1 ? "" : "+")
  465. print Example_start
  466. printf("%s\n", y)
  467. print Example_end
  468. for (i in names)
  469. names[i] = strip_out_name(names[i]) # remove delimiters
  470. print "@iftex"
  471. print "@smallfonts @rm"
  472. print "@end iftex"
  473. # Print other definition sites for code chunks, or for file
  474. # chunk if Print_file_full_defs is true
  475. if (Chunk_type == "code chunk" || Print_file_full_defs) {
  476. print_other_defns(Current_chunk,
  477. Chunk_info[Current_chunk]["defn"],
  478. Chunk_info[Current_chunk]["redefn"])
  479. }
  480. # Print callers for code chunks
  481. if (Chunk_type == "code chunk") {
  482. if ("callers" in Chunk_info[Current_chunk]) {
  483. print ""
  484. asorti(Chunk_info[Current_chunk]["callers"], my_callers)
  485. if (length(my_callers) > 1) {
  486. print "@noindent"
  487. print "This chunk is called by the following chunks:\n"
  488. print_ref_table(my_callers)
  489. } else {
  490. n = Chunk_info[my_callers[1]]["defn"]
  491. print "@noindent"
  492. printf("This chunk is called by %s; see its first definition at %s.\n",
  493. format_chunk_name(my_callers[1],
  494. Chunk_info[my_callers[1]]["chunk number"],
  495. Chunk_info[my_callers[1]]["type"]),
  496. format_xref(my_callers[1], n > 1 ? 1 : 0))
  497. }
  498. } else
  499. warning(_"chunk %s has no callers\n", Current_chunk)
  500. }
  501. sort_and_remove_duplicates(names)
  502. switch (length(names)) {
  503. case 0:
  504. break
  505. case 1:
  506. print "\n@noindent"
  507. printf("The called chunk %s is first defined at\n%s.\n",
  508. format_chunk_name(names[1],
  509. Chunk_info[names[1]]["chunk number"],
  510. Chunk_info[names[1]]["type"]),
  511. format_xref(names[1],
  512. (Chunk_info[names[1]]["defn"] > 1) ? 1 : 0))
  513. break;
  514. default:
  515. print "\n@noindent"
  516. printf("The following table lists called chunk definition points.\n")
  517. print_ref_table(names)
  518. break;
  519. }
  520. print "@iftex"
  521. print "@textfonts @rm"
  522. print "@end iftex"
  523. }
  524. function print_other_defns(chunk, total_defns, current_defn,
  525. other_defns, i, j) # locals
  526. {
  527. if (total_defns == 1)
  528. return
  529. print ""
  530. print "@noindent"
  531. print "This chunk is also defined in"
  532. for (i = j = 1; i <= total_defns; i++) {
  533. if (i == current_defn)
  534. continue
  535. other_defns[j++] = i
  536. }
  537. for (i = 1; i < j; i++) {
  538. printf("%s", format_xref(chunk, other_defns[i]))
  539. if (i + 2 == j)
  540. print ", and"
  541. else if (i + 2 < j)
  542. print ","
  543. }
  544. print "."
  545. }
  546. function print_ref_table(chunklist, i, x, n)
  547. {
  548. print "@multitable @columnfractions .35 .65"
  549. print "@headitem Chunk name @tab First definition point"
  550. for (i = 1; i in chunklist; i++) {
  551. x = chunklist[i]
  552. n = (Chunk_info[x]["defn"] > 1) ? 1 : 0
  553. printf("@item %s @tab See %s.\n",
  554. format_chunk_name(x,
  555. Chunk_info[x]["chunk number"],
  556. Chunk_info[x]["type"]),
  557. format_xref(x, n))
  558. }
  559. print "@end multitable"
  560. }
  561. function sort_and_remove_duplicates(names, i, dups)
  562. {
  563. for (i in names)
  564. dups[names[i]] = 1
  565. asorti(dups)
  566. delete names
  567. for (i in dups)
  568. names[i] = dups[i]
  569. }
  570. function end_code_gathering()
  571. {
  572. end_chunk_gathering()
  573. if (Debug ~ /code/)
  574. printf("finished formatting code %s\n",
  575. Code_chunk) > "/dev/stderr"
  576. }
  577. Pass == 2 && /^@print_file_defs[[:space:]]*$/ {
  578. delete Sorted_file_names
  579. j = 1
  580. for (i in Chunk_info) {
  581. if (Chunk_info[i]["type"] == "file chunk")
  582. Sorted_file_names[j++] = i
  583. }
  584. asort(Sorted_file_names) # Sorted by value
  585. print "@table @asis"
  586. for (i = 1; i in Sorted_file_names; i++) {
  587. name = Sorted_file_names[i]
  588. x = format_chunk_name(name,
  589. Chunk_info[name]["chunk number"],
  590. Chunk_info[name]["type"])
  591. printf("@item %s\n", x)
  592. n = Chunk_info[name]["defn"]
  593. if (n == 1) {
  594. printf("This chunk is defined in\n")
  595. printf("%s.\n", format_xref(name, 0))
  596. } else {
  597. printf("Multiple definitions occur in\n")
  598. for (j = 1; j <= n; j++) {
  599. printf("%s", format_xref(name, j))
  600. if (j == n - 1)
  601. printf(",\nand\n")
  602. else if (j < n - 1)
  603. printf(",\n")
  604. }
  605. print ".\n"
  606. }
  607. }
  608. print "@end table"
  609. next
  610. }
  611. Pass == 2 && /^@print_code_defs[[:space:]]*$/ {
  612. delete Sorted_code_names
  613. j = 1
  614. for (i in Chunk_info) {
  615. if (Chunk_info[i]["type"] == "code chunk")
  616. Sorted_code_names[j++] = i
  617. }
  618. asort(Sorted_code_names) # Sorted by value
  619. print "@table @asis"
  620. for (i = 1; i in Sorted_code_names; i++) {
  621. name = Sorted_code_names[i]
  622. x = format_chunk_name(name,
  623. Chunk_info[name]["chunk number"],
  624. Chunk_info[name]["type"])
  625. printf("@item %s\n", x)
  626. n = Chunk_info[name]["defn"]
  627. if (n == 1) {
  628. printf("This chunk is defined in\n")
  629. printf("%s.\n", format_xref(name, 0))
  630. } else {
  631. printf("Multiple definitions occur in\n")
  632. for (j = 1; j <= n; j++) {
  633. printf("%s", format_xref(name, j))
  634. if (j == n - 1)
  635. printf(",\nand\n")
  636. else if (j < n - 1)
  637. printf(",\n")
  638. }
  639. print ".\n"
  640. }
  641. }
  642. print "@end table"
  643. next
  644. }
  645. Pass == 2 && /^@print_code_refs[[:space:]]*$/ {
  646. delete Sorted_code_names
  647. j = 1
  648. for (i in Chunk_info) {
  649. if (Chunk_info[i]["type"] == "code chunk")
  650. Sorted_code_names[j++] = i
  651. }
  652. asort(Sorted_code_names) # Sorted by value
  653. print "@table @asis"
  654. for (i = 1; i in Sorted_code_names; i++) {
  655. name = Sorted_code_names[i]
  656. n = Chunk_info[name]["defn"]
  657. if (n == 0) # warning printed elsewhere
  658. continue
  659. fmt_name = format_chunk_name(name, Chunk_info[name]["chunk number"],
  660. Chunk_info[name]["type"])
  661. printf("@item %s\n", fmt_name)
  662. Current_chunk = name # for use by next chunk
  663. if ("callers" in Chunk_info[Current_chunk]) {
  664. print ""
  665. asorti(Chunk_info[Current_chunk]["callers"], my_callers)
  666. if (length(my_callers) > 1) {
  667. print "@noindent"
  668. print "This chunk is called by the following chunks:\n"
  669. print_ref_table(my_callers)
  670. } else {
  671. n = Chunk_info[my_callers[1]]["defn"]
  672. print "@noindent"
  673. printf("This chunk is called by %s; see its first definition at %s.\n",
  674. format_chunk_name(my_callers[1],
  675. Chunk_info[my_callers[1]]["chunk number"],
  676. Chunk_info[my_callers[1]]["type"]),
  677. format_xref(my_callers[1], n > 1 ? 1 : 0))
  678. }
  679. } else
  680. warning(_"chunk %s has no callers\n", Current_chunk)
  681. }
  682. print "@end table"
  683. next
  684. }
  685. /^@print_initial_setup([[:space:]]+.*|[[:space:]]*)$/ {
  686. Print_initial_setup = TRUE
  687. if (NF > 1) {
  688. $1 = ""
  689. $0 = $0
  690. Initial_setup_name = $0
  691. }
  692. else
  693. Initial_setup_name = "Initial setup"
  694. next
  695. }
  696. /^@initial_setup[[:space:]]*$/, /^@end initial_setup[[:space:]]*$/ {
  697. if (Pass == 1 || ! Print_initial_setup)
  698. next
  699. Chunk_info[Initial_setup_name]["type"] = "code chunk"
  700. Chunk_info[Initial_setup_name]["defn"] = 1
  701. if (/^@initial_setup[[:space:]]*$/) {
  702. print "@need 400"
  703. printf("%s\n", format_anchor(Initial_setup_name, 0))
  704. printf("@noindent\n%s @equiv{}\n",
  705. format_chunk_name(Initial_setup_name, 0, "code chunk"))
  706. print Example_start
  707. } else if (/^@end initial_setup[[:space:]]*$/) {
  708. print Example_end
  709. } else {
  710. x = expand_tabs($0, Tabstop)
  711. gsub(/[@{}]/, "@&", x)
  712. print x
  713. }
  714. next
  715. }
  716. Pass == 2 { print }
  717. BEGIN {
  718. if (Tabstop == 0)
  719. Tabstop = 4 # default tab stops
  720. }
  721. # expand_tabs --- expand tabs in the string
  722. function expand_tabs(string, tabstop, chars, out, i, j, k, n)
  723. {
  724. if (tabstop < 2)
  725. fatal(_"expand_tabs: tabstop %d < 2\n", tabstop)
  726. n = split(string, chars, "")
  727. j = k = 0
  728. for (i = 1; i <= n;) {
  729. if (chars[i] == "\n") {
  730. out[j++] = chars[i++]
  731. k = 0
  732. continue
  733. }
  734. if (chars[i] != "\t") {
  735. out[j++] = chars[i++]
  736. k++
  737. continue
  738. }
  739. i++ # skip the tab
  740. do {
  741. out[j++] = " "
  742. k++
  743. } while (and(k, tabstop-1) != 0)
  744. }
  745. return join(out, 0, j, SUBSEP)
  746. }
  747. function sanitize_name(name)
  748. {
  749. gsub(/[^[:alnum:]]/, "-", name)
  750. return name
  751. }
  752. function format_anchor_or_ref(type, name, defn,
  753. clean_name, result) # locals
  754. {
  755. clean_name = sanitize_name(name)
  756. if (defn > 0)
  757. result = sprintf("@%s{%s-%d}", type, clean_name, defn)
  758. else
  759. result = sprintf("@%s{%s}", type, clean_name)
  760. return result
  761. }
  762. function format_xref(name, defn)
  763. {
  764. return format_anchor_or_ref("ref", name, defn)
  765. }
  766. function format_anchor(name, cur_defn, total_defns, defn)
  767. {
  768. if (total_defns == 1)
  769. defn = 0
  770. else if (cur_defn <= total_defns)
  771. defn = cur_defn
  772. return format_anchor_or_ref("anchor", name, defn)
  773. }
  774. function format_chunk_name(name, count, type,
  775. result, left, right, style) # locals
  776. {
  777. if (type == "file chunk") {
  778. left = "@{"
  779. right = "@}"
  780. style = "file"
  781. } else if (type == "code chunk") {
  782. left = "<"
  783. right = ">"
  784. style = "i"
  785. } else
  786. fatal(_"format_chunk_name: Unknown chunk type `%s'\n", type)
  787. if (count > 0 && Numbered_chunks)
  788. result = sprintf("@r{%s@%s{%s} @oldnum{%d}%s}",
  789. left, style, name, count, right)
  790. else
  791. result = sprintf("@r{%s@%s{%s}%s}", left, style, name, right)
  792. return result
  793. }