dump_selects.pl 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. #!/usr/bin/perl
  2. #
  3. # Generate select lists from ser/sip-router select initializations structs.
  4. # (run on files generated by gcc -fdump-translation-unit -c file.c,
  5. # try -h for help)
  6. # E.g.: dump_selects.pl --file cfg_core.c --defs="-DUSE_SCTP ..."
  7. #
  8. # Note: uses GCC::TranslationUnit (see cpan) with the following patch:
  9. #@@ -251,6 +251,8 @@
  10. # $node->{vector}[$key] = $value;
  11. # } elsif($key =~ /^op (\d+)$/) {
  12. # $node->{operand}[$1] = $value;
  13. #+ } elsif ($key eq "val") {
  14. #+ push @{$node->{$key}}, ($value) ;
  15. # } else {
  16. # $node->{$key} = $value;
  17. # }
  18. #
  19. #
  20. # Assumptions:
  21. # - the first array of type select_row_t with an initializer is the array
  22. # with the select definitions. Only one select_row_t array per file is
  23. # supported.
  24. #
  25. # Output notes:
  26. #
  27. use strict;
  28. use warnings;
  29. use Getopt::Long;
  30. use File::Temp qw(:mktemp);
  31. use File::Basename;
  32. use GCC::TranslationUnit;
  33. # text printed if we discover that GCC::TranslationUnit is unpatched
  34. my $patch_required="$0 requires a patched GCC:TranslationUnit, see the " .
  35. "comments at the beginning of the file or try --patch\n";
  36. # gcc name
  37. my $gcc="gcc";
  38. # default defines
  39. my $c_defs="DNAME='\"kamailio\"' -DVERSION='\"5.1.0-dev3\"' -DARCH='\"x86_64\"' -DOS='linux_' -DOS_QUOTED='\"linux\"' -DCOMPILER='\"gcc 4.9.2\"' -D__CPU_x86_64 -D__OS_linux -DSER_VER=5001000 -DCFG_DIR='\"/usr/local/etc/kamailio/\"' -DRUN_DIR='\"/run/kamailio/\"' -DPKG_MALLOC -DSHM_MEM -DSHM_MMAP -DDNS_IP_HACK -DUSE_MCAST -DUSE_TCP -DDISABLE_NAGLE -DHAVE_RESOLV_RES -DUSE_DNS_CACHE -DUSE_DNS_FAILOVER -DUSE_DST_BLOCKLIST -DUSE_NAPT -DMEM_JOIN_FREE -DF_MALLOC -DQ_MALLOC -DTLSF_MALLOC -DDBG_SR_MEMORY -DUSE_TLS -DTLS_HOOKS -DUSE_CORE_STATS -DSTATISTICS -DMALLOC_STATS -DWITH_AS_SUPPORT -DUSE_SCTP -DFAST_LOCK -DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=1024 -DCC_GCC_LIKE_ASM -DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD -DHAVE_MSG_NOSIGNAL -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H -DHAVE_TIMEGM -DHAVE_SCHED_SETSCHEDULER -DHAVE_IP_MREQN -DHAVE_EPOLL -DHAVE_SIGIO_RT -DSIGINFO64_WORKAROUND -DUSE_FUTEX -DHAVE_SELECT";
  40. # file with gcc syntax tree
  41. my $file;
  42. my $core_file;
  43. my $src_fname;
  44. # type to look for
  45. my $var_type="select_row_t";
  46. my $tu;
  47. my $node;
  48. my $i;
  49. my @sel_exports; # filled with select definitions (select_row_t)
  50. my @core_exports; # filled with select definitions from core (if -c is used)
  51. my ($sel_grp_name, $sel_var_name);
  52. my ($opt_help, $opt_txt, $opt_is_tu, $dbg, $opt_grp_name, $opt_patch);
  53. my ($opt_force_grp_name, $opt_docbook);
  54. # default output formats
  55. my $output_format_header="HEADER";
  56. my $output_format_footer="FOOTER";
  57. my $output_format_selline="SELLINE";
  58. sub show_patch
  59. {
  60. my $patch='
  61. --- GCC/TranslationUnit.pm.orig 2009-10-16 17:57:51.275963053 +0200
  62. +++ GCC/TranslationUnit.pm 2009-10-16 20:17:28.128455959 +0200
  63. @@ -251,6 +251,8 @@
  64. $node->{vector}[$key] = $value;
  65. } elsif($key =~ /^op (\d+)$/) {
  66. $node->{operand}[$1] = $value;
  67. + } elsif ($key eq "val") {
  68. + push @{$node->{$key}}, ($value) ;
  69. } else {
  70. $node->{$key} = $value;
  71. }
  72. ';
  73. print $patch;
  74. }
  75. sub help
  76. {
  77. $~ = "USAGE";
  78. write;
  79. format USAGE =
  80. Usage @* -f filename | --file filename [options...]
  81. $0
  82. Options:
  83. -f filename - use filename for input (see also -T/--tu).
  84. --file filename - same as -f.
  85. -c | --core filename - location of core selects (used to resolve
  86. module selects that refer in-core functions).
  87. -h | -? | --help - this help message.
  88. -D | --dbg | --debug - enable debugging messages.
  89. -d | --defs - defines to be passed on gcc's command line
  90. (e.g. --defs="-DUSE_SCTP -DUSE_TCP").
  91. -g | --grp name
  92. --group name - select group name used if one cannot be
  93. autodetected (e.g. no default value
  94. initializer present in the file).
  95. -G | --force-grp name
  96. --force-group name - force using a select group name, even if one
  97. is autodetected (see also -g).
  98. --gcc gcc_name - run gcc_name instead of gcc.
  99. -t | --txt - text mode output.
  100. --docbook | --xml - docbook output (xml).
  101. -T | --tu - the input file is in raw gcc translation
  102. unit format (as produced by
  103. gcc -fdump-translation-unit -c ). If not
  104. present it's assumed that the file contains
  105. C code.
  106. -s | --src | --source - name of the source file, needed only if
  107. the input file is in "raw" translation
  108. unit format (--tu) and useful to restrict
  109. and speed-up the search.
  110. --patch - show patches needed for the
  111. GCC::TranslationUnit package.
  112. .
  113. }
  114. # escape a string for xml use
  115. # params: string to be escaped
  116. # return: escaped string
  117. sub xml_escape{
  118. my $s=shift;
  119. my %escapes = (
  120. '"' => '"',
  121. "'" => ''',
  122. '&' => '&',
  123. '<' => '&lt;',
  124. '>' => '&gt;'
  125. );
  126. $s=~s/(["'&<>])/$escapes{$1}/g;
  127. return $s;
  128. }
  129. # escape a string according with the output requirements
  130. # params: string to be escaped
  131. # return: escaped string
  132. sub output_esc{
  133. return xml_escape(shift) if defined $opt_docbook;
  134. return shift;
  135. }
  136. # eliminate casts and expressions.
  137. # (always go on the first operand)
  138. # params: node (GCC::Node)
  139. # result: if node is an expression it will walk on operand(0) until first non
  140. # expression element is found
  141. sub expr_op0{
  142. my $n=shift;
  143. while(($n->isa('GCC::Node::Expression') || $n->isa('GCC::Node::Unary')) &&
  144. defined $n->operand(0)) {
  145. $n=$n->operand(0);
  146. }
  147. return $n;
  148. }
  149. # constants (from select.h)
  150. use constant {
  151. MAX_SELECT_PARAMS => 32,
  152. MAX_NESTED_CALLS => 4,
  153. };
  154. use constant DIVERSION_MASK => 0x00FF;
  155. use constant {
  156. DIVERSION => 1<<8,
  157. SEL_PARAM_EXPECTED => 1<<9,
  158. CONSUME_NEXT_STR => 1<<10,
  159. CONSUME_NEXT_INT => 1<<11,
  160. CONSUME_ALL => 1<<12,
  161. OPTIONAL => 1<<13,
  162. NESTED => 1<<14,
  163. FIXUP_CALL => 1<<15,
  164. };
  165. use constant {
  166. SEL_PARAM_INT => 0,
  167. SEL_PARAM_STR => 1,
  168. SEL_PARAM_DIV => 2,
  169. SEL_PARAM_PTR => 3,
  170. };
  171. # Select rules (pasted from one email from Jan):
  172. # Roughly the rules are as follows:
  173. # * The first component of the row tells the select compiler in what state the
  174. # row can be considered.
  175. # * The second component tells the compiler what type of components is expected
  176. # for the row to match. SEL_PARAM_STR means that .foo should follow,
  177. # SEL_PARAM_INT means that [1234] should follow.
  178. # * The third component is the string to be matched for string components and
  179. # STR_NULL if the next expected component is an integer.
  180. # * The fourth component is a function name. This is either the function to be
  181. # called if this is the last rule all constrains are met, or it is the next
  182. # state to transition into if we are not processing the last component of the
  183. # select identifier.
  184. #
  185. # * The fifth rule are flags that can impose further constrains on how the
  186. # given line is to be used. Some of them are:
  187. #
  188. # - CONSUME_NEXT_INT - This tells the compiler that there must be at least one
  189. # more component following the current one, but it won't transition into the
  190. # next state, instead the current function will "consume" the integer as
  191. # parameters.
  192. #
  193. # - CONSUME_NEXT_STR - Same as previous, but the next component must be a
  194. # string.
  195. # - SEL_PARAM_EXPECTED - The current row must not be last and there must be
  196. # another row to transition to.
  197. #
  198. # - OPTIONAL - There may or may not be another component, but in any case the
  199. # compiler does not transition into another state (row). This can be used
  200. # together with CONSUME_NEXT_{STR,INT} to implement optional parameters, for
  201. # example .param could return a string of all parameters, while .param.abc
  202. # will only return the value of parameter abc.
  203. #
  204. # - NESTED - When this flag is present in a rule then it means that the
  205. # previous function should be called before the current one. The original
  206. # idea was that all select identifiers would only resolve to one function
  207. # call, however, it turned out to be inconvenient in some cases so we added
  208. # this. This is typically used in selects that have URIs as components. In
  209. # that case it is desirable to support all subcomponents for uri selects, but
  210. # it does not make sense to reimplement them for every single case. In that
  211. # case the uri components sets NESTED flags which causes the "parent"
  212. # function to be called first. The "parent" function extracts only the URI
  213. # which is then passed to the corresponding URI parsing function. The word
  214. # NESTED here means "nested function call".
  215. #
  216. # - CONSUME_ALL - Any number of select identifier components may follow and
  217. # they may be of any types. This flag causes the function on the current row
  218. # to be called and it is up to the function to handle the remainder of the
  219. # select identifier.
  220. # generate all select strings starting with a specific "root" function
  221. # params:
  222. # $1 - root
  223. # $2 - crt_label/skip (if !="" skip print and use it to search the next valid
  224. # sel. param)
  225. sub gen_selects
  226. {
  227. my $root=shift;
  228. my $crt_label=shift;
  229. my $skip_next;
  230. my @matches;
  231. my ($prev, $type, $name, $new_f, $flags);
  232. my $m;
  233. my @ret=();
  234. my @sel;
  235. @matches = grep((${$_}[0] eq $root) &&
  236. (!defined $crt_label || $crt_label eq "" ||
  237. ${$_}[2] eq "" ||
  238. $crt_label eq ${$_}[2]) , @sel_exports);
  239. if ((@matches == 0) && (@core_exports > 0)) {
  240. @matches = grep((${$_}[0] eq $root) &&
  241. (!defined $crt_label || $crt_label eq "" ||
  242. ${$_}[2] eq "" ||
  243. $crt_label eq ${$_}[2]), @core_exports);
  244. }
  245. for $m (@matches) {
  246. my $s="";
  247. ($prev, $type, $name, $new_f, $flags)=@$m;
  248. if (($flags & (NESTED|CONSUME_NEXT_STR|CONSUME_NEXT_INT)) == NESTED){
  249. $skip_next=$name;
  250. }
  251. if (!($flags & NESTED) ||
  252. (($flags & NESTED) && ($type !=SEL_PARAM_INT))){
  253. # Note: unnamed NESTED params are not allowed --andrei
  254. if ($type==SEL_PARAM_INT){
  255. $s.="[integer]";
  256. }else{
  257. if ($name ne "") {
  258. if (!defined $crt_label || $crt_label eq "") {
  259. $s.=(($prev eq "0" || $prev eq "")?"@":".") . $name;
  260. }
  261. }elsif (!($flags & NESTED) &&
  262. (!defined $crt_label || $crt_label eq "")){
  263. $s.=".<string>";
  264. }
  265. }
  266. }
  267. if ( !($flags & NESTED) &&
  268. ($flags & (CONSUME_NEXT_STR|CONSUME_NEXT_INT|CONSUME_ALL)) ){
  269. #process params
  270. if ($flags & OPTIONAL){
  271. $s.="{";
  272. }
  273. # add param name
  274. if ($flags & CONSUME_NEXT_STR){
  275. $s.="[\"string\"]";
  276. }elsif ($flags & CONSUME_NEXT_INT){
  277. $s.="[integer]";
  278. }else{
  279. $s.=".*"; # CONSUME_ALL
  280. }
  281. if ($flags & OPTIONAL){
  282. $s.="}";
  283. }
  284. }
  285. if (!($flags & SEL_PARAM_EXPECTED)){
  286. # if optional terminal add it to the list along with all the
  287. # variants
  288. if ($new_f eq "" || $new_f eq "0"){
  289. # terminal
  290. push @ret, $s;
  291. }else{
  292. @sel=map("$s$_", gen_selects($new_f, $skip_next));
  293. if (@sel > 0) {
  294. push(@ret, $s) if (!($s eq "") && !($flags & NESTED));
  295. push @ret, @sel;
  296. }else{
  297. if ($flags & NESTED) {
  298. $s.="*";
  299. }
  300. push @ret, $s;
  301. }
  302. }
  303. }else{
  304. # non-terminal
  305. if (!($new_f eq "" || $new_f eq "0")){
  306. @sel=map("$s$_", gen_selects($new_f, $skip_next));
  307. if (@sel > 0) {
  308. push @ret, @sel;
  309. }elsif ($flags & NESTED){
  310. $s.="*";
  311. push @ret, $s;
  312. }
  313. } # else nothing left, but param expected => error
  314. }
  315. }
  316. return @ret;
  317. }
  318. # parse the select declaration from a file into an array.
  319. # params:
  320. # $1 - file name
  321. # $2 - ref to result list (e.g. \@res)
  322. # $3 - boolean - true if filename is a translation-unit dump.
  323. # cmd. line global options used:
  324. # $src_fname - used only if $3 is true (see --src)
  325. # $gcc
  326. # $c_defs
  327. # $dbg
  328. #
  329. #
  330. sub process_file
  331. {
  332. my $file=shift;
  333. my $sel=shift; # ref to result array
  334. my $file_is_tu=shift;
  335. my $tmp_file;
  336. my $i;
  337. my $node;
  338. my $tu;
  339. my @res; # temporary hold the result here
  340. if (! $file_is_tu){
  341. # file is not a gcc translation-unit dump
  342. # => we have to create one
  343. $src_fname=basename($file);
  344. $tmp_file = "/tmp/" . mktemp ("dump_translation_unit_XXXXXX");
  345. # Note: gcc < 4.5 will produce the translation unit dump in a file in
  346. # the current directory. gcc 4.5 will write it in the same directory as
  347. # the output file.
  348. system("$gcc -fdump-translation-unit $c_defs -c $file -o $tmp_file") == 0
  349. or die "$gcc -fdump-translation-unit $c_defs -c $file -o $tmp_file" .
  350. " failed to generate a translation unit dump from $file";
  351. if (system("if [ -f \"$src_fname\".001t.tu ]; then \
  352. mv \"$src_fname\".001t.tu $tmp_file; \
  353. else mv /tmp/\"$src_fname\".001t.tu $tmp_file; fi ") != 0) {
  354. unlink($tmp_file, "$tmp_file.o");
  355. die "could not find the gcc translation unit dump file" .
  356. " ($src_fname.001t.tu) neither in the current directory" .
  357. " or /tmp";
  358. };
  359. $tu=GCC::TranslationUnit::Parser->parsefile($tmp_file);
  360. print(STDERR "src name $src_fname\n") if $dbg;
  361. unlink($tmp_file, "$tmp_file.o");
  362. }else{
  363. $tu=GCC::TranslationUnit::Parser->parsefile($file);
  364. }
  365. print(STDERR "Parsing file $file...\n") if $dbg;
  366. print(STDERR "Searching...\n") if $dbg;
  367. $i=0;
  368. # iterate on the entire nodes array (returned by gcc), but skipping node 0
  369. SEARCH: for $node (@{$tu}[1..$#{$tu}]) {
  370. $i++;
  371. while($node) {
  372. if (
  373. @res == 0 && # parse it only once
  374. $node->isa('GCC::Node::var_decl') &&
  375. $node->type->isa('GCC::Node::array_type') # &&
  376. #(! defined $src_fname || $src_fname eq "" ||
  377. # $node->source=~"$src_fname")
  378. ){
  379. # found a var decl. that it's an array
  380. # check if it's a valid array type
  381. next if (!( $node->type->can('elements') &&
  382. defined $node->type->elements &&
  383. $node->type->elements->can('name') &&
  384. defined $node->type->elements->name &&
  385. $node->type->elements->name->can('name') &&
  386. defined $node->type->elements->name->name)
  387. );
  388. my $type_name= $node->type->elements->name->name->identifier;
  389. if ($type_name eq $var_type) {
  390. if ($node->can('initial') && defined $node->initial) {
  391. my %c1=%{$node->initial};
  392. $sel_var_name=$node->name->identifier;
  393. if (defined $c1{val}){
  394. my $c1_el;
  395. die $patch_required if (ref($c1{val}) ne "ARRAY");
  396. # iterate on array elem., level 1( top {} )
  397. # each element is a constructor.
  398. for $c1_el (@{$c1{val}}) {
  399. # finally we are a the lower {} initializer:
  400. # { prev_f, type, name, new_f, flags }
  401. my %c2=%{$c1_el};
  402. my @el=@{$c2{val}};
  403. my ($prev_f_n, $type_n, $name_n, $new_f_n,
  404. $flags_n)=@el;
  405. my ($prev_f, $type, $name, $new_f, $flags);
  406. my $str;
  407. if ($prev_f_n->isa('GCC::Node::integer_cst') &&
  408. $new_f_n->isa('GCC::Node::integer_cst') &&
  409. $prev_f_n->low==0 && $new_f_n->low==0) {
  410. last SEARCH;
  411. }
  412. $prev_f=
  413. ($prev_f_n->isa('GCC::Node::integer_cst'))?
  414. $prev_f_n->low:
  415. expr_op0($prev_f_n)->name->identifier;
  416. $type=$type_n->low;
  417. $str=${${$name_n}{val}}[0];
  418. $name=($str->isa('GCC::Node::integer_cst'))?"":
  419. expr_op0($str)->string;
  420. $new_f=
  421. ($new_f_n->isa('GCC::Node::integer_cst'))?
  422. $new_f_n->low:
  423. expr_op0($new_f_n)->name->identifier;
  424. $flags= (defined $flags_n)?$flags_n->low:0;
  425. push @res, [$prev_f, $type, $name,
  426. $new_f, $flags];
  427. }
  428. }
  429. }
  430. }
  431. }
  432. } continue {
  433. if ($node->can('chain')){
  434. $node = $node->chain;
  435. }else{
  436. last;
  437. }
  438. }
  439. }
  440. push @$sel, @res;
  441. }
  442. # main:
  443. # read command line args
  444. if ($#ARGV < 0 || ! GetOptions( 'help|h|?' => \$opt_help,
  445. 'file|f=s' => \$file,
  446. 'core|c=s' => \$core_file,
  447. 'txt|t' => \$opt_txt,
  448. 'docbook|xml' => \$opt_docbook,
  449. 'tu|T' => \$opt_is_tu,
  450. 'source|src|s=s' => \$src_fname,
  451. 'defs|d=s'=>\$c_defs,
  452. 'group|grp|g=s'=>\$opt_grp_name,
  453. 'force-group|force-grp|G=s' =>
  454. \$opt_force_grp_name,
  455. 'dbg|debug|D'=>\$dbg,
  456. 'gcc=s' => \$gcc,
  457. 'patch' => \$opt_patch) ||
  458. defined $opt_help) {
  459. do { show_patch(); exit 0; } if (defined $opt_patch);
  460. select(STDERR) if ! defined $opt_help;
  461. help();
  462. exit((defined $opt_help)?0:1);
  463. }
  464. do { show_patch(); exit 0; } if (defined $opt_patch);
  465. do { select(STDERR); help(); exit 1 } if (!defined $file);
  466. if (defined $opt_txt){
  467. $output_format_header="HEADER";
  468. $output_format_footer="FOOTER";
  469. $output_format_selline="SELLINE";
  470. }elsif (defined $opt_docbook){
  471. $output_format_header="DOCBOOK_HEADER";
  472. $output_format_footer="DOCBOOK_FOOTER";
  473. $output_format_selline="DOCBOOK_SELLINE";
  474. }
  475. process_file($file, \@sel_exports, defined $opt_is_tu);
  476. process_file($core_file, \@core_exports, 0) if (defined $core_file);
  477. print(STDERR "Done.\n") if $dbg;
  478. my ($prev, $type, $name, $next, $flags, $desc);
  479. my $extra_txt;
  480. if (@sel_exports > 0){
  481. my $s;
  482. $i=0;
  483. # dump the configuration in txt mode
  484. if (defined $opt_force_grp_name) {
  485. $sel_grp_name=output_esc($opt_force_grp_name);
  486. }elsif (!defined $sel_grp_name && defined $opt_grp_name) {
  487. $sel_grp_name=output_esc($opt_grp_name);
  488. }
  489. print(STDERR "Generating select list...\n") if $dbg;
  490. my @sels = gen_selects "0";
  491. $~ = $output_format_header; write;
  492. $~ = $output_format_selline ;
  493. for $s (@sels){
  494. $extra_txt=output_esc("");
  495. $desc=output_esc("");
  496. $name=output_esc($s);
  497. $i++;
  498. #$extra_txt.=output_esc("Returns an array.") if ($flags & 1 );
  499. # generate txt description
  500. write;
  501. }
  502. $~ = $output_format_footer; write;
  503. }else{
  504. die "no selects found in $file\n";
  505. }
  506. sub valid_grp_name
  507. {
  508. my $name=shift;
  509. return defined $name && $name ne "";
  510. }
  511. format HEADER =
  512. Selects@*
  513. (valid_grp_name $sel_grp_name) ? " for " . $sel_grp_name : ""
  514. =======@*
  515. "=" x length((valid_grp_name $sel_grp_name)?" for " . $sel_grp_name : "")
  516. @||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  517. "[ this file is autogenerated, do not edit ]"
  518. .
  519. format FOOTER =
  520. .
  521. format SELLINE =
  522. @>>. @*
  523. $i, $name
  524. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  525. $desc
  526. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  527. $extra_txt
  528. .
  529. format DOCBOOK_HEADER =
  530. <?xml version="1.0" encoding="UTF-8"?>
  531. <!-- this file is autogenerated, do not edit! -->
  532. <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
  533. "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
  534. <chapter id="select_list@*">
  535. (valid_grp_name $sel_grp_name) ? "." . $sel_grp_name : ""
  536. <title>Selects@*</title>
  537. (valid_grp_name $sel_grp_name) ? " for " . $sel_grp_name : ""
  538. <orderedlist>
  539. .
  540. format DOCBOOK_FOOTER =
  541. </orderedlist>
  542. </chapter>
  543. .
  544. format DOCBOOK_SELLINE =
  545. <listitem><simpara>@*</simpara>
  546. $name
  547. ~~<para>^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< </para>
  548. $desc
  549. ~~<para>^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< </para>
  550. $extra_txt
  551. </listitem>
  552. .