Rev 7 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (c) 2002-2005 The TenDRA Project <http://www.tendra.org/>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of The TenDRA Project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id$
*/
/*
Crown Copyright (c) 1997
This TenDRA(r) Computer Program is subject to Copyright
owned by the United Kingdom Secretary of State for Defence
acting through the Defence Evaluation and Research Agency
(DERA). It is made available to Recipients with a
royalty-free licence for its use, reproduction, transfer
to other parties and amendment for any purpose not excluding
product development provided that any such use et cetera
shall be deemed to be acceptance of the following conditions:-
(1) Its Recipients shall ensure that this Notice is
reproduced upon any copies or amended versions of it;
(2) Any amended version of it shall be clearly marked to
show both the nature of and the organisation responsible
for the relevant amendment or amendments;
(3) Its onward transfer from a recipient to another
party shall be deemed to be that party's acceptance of
these conditions;
(4) DERA gives no warranty or assurance as to its
quality or suitability for any purpose and DERA accepts
no liability whatsoever in relation to any use to which
it may be put.
*/
#include "config.h"
#include "tdf.h"
#include "cmd_ops.h"
#include "cons_ops.h"
#include "info_ops.h"
#include "link_ops.h"
#include "par_ops.h"
#include "sort_ops.h"
#include "spec_ops.h"
#include "error.h"
#include "input.h"
#include "lex.h"
#include "output.h"
/*
DO THE INITIAL SEGMENTS OF TWO STRINGS MATCH
This macro gives a convenient method for testing whether the first
C characters of the strings A and B are equal.
*/
#define strneq(A, B, C)\
(strncmp((A), (B), (size_t)(C)) == 0)
/*
CURRENT OUTPUT FILE
These variables describe the current output file.
*/
static FILE *output_file;
static char output_buff[512];
static int output_posn = 0;
static unsigned crt_column = 0;
/*
CURRENT LOOP VARIABLES
These variables keep track of the current state of the various
output loops.
*/
static unsigned crt_major = 0;
static unsigned crt_minor = 0;
static int crt_unique = 0;
static SORT crt_sort = NULL_sort;
static SORT_INFO crt_info = NULL_info;
static CONSTRUCT crt_cons = NULL_cons;
static PARAMETER crt_param = NULL_par;
static int crt_param_no = 0;
static int last_param_no = 0;
/*
PRINT A CHARACTER TO THE OUTPUT FILE
This routine prints the character c to the output file updating the
current column number.
*/
static void
output_char(int c)
{
int i = output_posn;
output_buff[i] = (char)c;
if (++i >= 500 || c == '\n') {
output_buff[i] = 0;
IGNORE fputs(output_buff, output_file);
i = 0;
}
if (c == '\n') {
crt_column = 0;
} else if (c == '\t') {
crt_column = 8 *(crt_column / 8 + 1);
} else {
crt_column++;
}
output_posn = i;
return;
}
/*
PRINT A STRING TO THE OUTPUT FILE
This routine prints the string s to the output file.
*/
static void
output_string(char *s)
{
char c;
while (c = *(s++), c != 0) {
output_char((int)c);
}
return;
}
/*
OUTPUT AN ENCODING STRING FOR A CONSTRUCT
This routine writes the encoding strings for the parameter sorts of
the construct cons to the output file.
*/
static void
output_cons(CONSTRUCT cons, int intro)
{
int c;
int brks = 0;
unsigned kind = DEREF_unsigned(cons_kind(cons));
LIST(PARAMETER)p = DEREF_list(cons_pars(cons));
while (!IS_NULL_list(p)) {
PARAMETER par = DEREF_par(HEAD_list(p));
SORT sort = DEREF_sort(par_type(par));
int align = DEREF_int(par_align(par));
int brk = DEREF_int(par_brk(par));
int intro2 = DEREF_int(par_intro(par));
if (align) output_char('|');
if (brk) output_char('{');
if (intro2) intro = 1;
c = output_sort(sort, intro);
if (c == '@' && kind == KIND_cond) {
/* Conditional construct */
output_char('[');
sort = DEREF_sort(cons_res(cons));
IGNORE output_sort(sort, intro);
output_char(']');
}
brks += brk;
p = TAIL_list(p);
}
while (brks--)output_char('}');
return;
}
/*
OUTPUT AN ENCODING STRING FOR A SORT
Every basic and built-in type has an associated code letter. This,
together with various control characters for lists and optional sorts,
allows every sort to be expressed as a sequence of characters. This
routine prints this encoding string for the sort sort to the output
file
*/
int
output_sort(SORT sort, int intro)
{
int c = DEREF_int(sort_code(sort));
SORT_INFO info = DEREF_info(sort_info(sort));
if (!IS_NULL_info(info)) {
switch (TAG_info(info)) {
case info_builtin_tag:
case info_basic_tag: {
if (c < 32) {
char buff[10];
sprintf_v(buff, "\\%03o",(unsigned)c);
output_string(buff);
} else {
output_char(c);
}
if (intro) {
int edge = DEREF_int(sort_edge(sort));
if (edge) output_char('&');
}
break;
}
case info_dummy_tag: {
CONSTRUCT cons = DEREF_cons(info_dummy_cons(info));
output_cons(cons, intro);
break;
}
case info_clist_tag:
case info_slist_tag:
case info_option_tag: {
sort = DEREF_sort(info_clist_etc_arg(info));
output_char(c);
output_char('[');
IGNORE output_sort(sort, intro);
output_char(']');
break;
}
}
}
return(c);
}
/*
OUTPUT A FORMAT STRING
This routine writes the format string s to the output file.
*/
static void
output(string s)
{
char c;
while (c = *(s++), c != 0) {
if (c == '%') {
char *s0 = s;
int prec = 100;
char buff[120];
int have_prec = 0;
SORT cs = crt_sort;
SORT_INFO ci = crt_info;
CONSTRUCT cc = crt_cons;
PARAMETER cp = crt_param;
c = *(s++);
if (c >= '0' && c <= '9') {
/* Read precision */
prec = (int)(c - '0');
while (c = *(s++), (c >= '0' && c <= '9')) {
prec = 10 * prec + (int)(c - '0');
}
have_prec = 1;
}
switch (c) {
case 'C':
cons_format : {
/* Construct information */
if (IS_NULL_cons(cc)) goto misplaced_arg;
c = *(s++);
switch (c) {
case 'N': {
/* '%CN' -> construct name */
string nm = DEREF_string(cons_name(cc));
sprintf_v(buff, "%.*s", prec, nm);
output_string(buff);
break;
}
case 'E': {
/* '%CE' -> construct encoding */
unsigned e;
e = DEREF_unsigned(cons_encode(cc));
sprintf_v(buff, "%u", e);
output_string(buff);
break;
}
case 'S': {
/* '%CS' -> construct result sort */
goto sort_format;
}
case 'X': {
/* '%CX' -> construct encoding string */
output_cons(cc, 0);
break;
}
default : {
goto bad_format;
}
}
break;
}
case 'P': {
/* Parameter information */
if (IS_NULL_par(cp)) goto misplaced_arg;
c = *(s++);
if (c == 'N') {
/* '%PN' -> parameter name */
string nm = DEREF_string(par_name(cp));
sprintf_v(buff, "%.*s", prec, nm);
output_string(buff);
} else if (c == 'S') {
/* '%PS' -> parameter sort */
cs = DEREF_sort(par_type(cp));
ci = DEREF_info(sort_info(cs));
goto sort_format;
} else if (c == 'E') {
/* '%PE' -> parameter number */
sprintf_v(buff, "%d", crt_param_no);
output_string(buff);
} else {
goto bad_format;
}
break;
}
case 'S':
sort_format : {
/* Sort information */
if (IS_NULL_info(ci)) goto misplaced_arg;
c = *(s++);
switch (c) {
case 'N': {
/* '%SN' -> sort name */
string nm = DEREF_string(sort_name(cs));
sprintf_v(buff, "%.*s", prec, nm);
output_string(buff);
break;
}
case 'T': {
/* '%ST' -> sort name in capitals */
string nm = DEREF_string(sort_caps(cs));
sprintf_v(buff, "%.*s", prec, nm);
output_string(buff);
break;
}
case 'L': {
/* '%SL' -> sort unit name */
string nm = DEREF_string(sort_link(cs));
if (nm) {
sprintf_v(buff, "%.*s", prec, nm);
output_string(buff);
}
break;
}
case 'U': {
/* '%SU' -> sort unit name */
string nm = DEREF_string(sort_unit(cs));
if (nm) {
sprintf_v(buff, "%.*s", prec, nm);
output_string(buff);
}
break;
}
case 'B': {
/* '%SB' -> bits in encoding */
unsigned b = 0;
if (IS_info_basic(ci)) {
b = DEREF_unsigned(info_basic_bits(ci));
}
sprintf_v(buff, "%u", b);
output_string(buff);
break;
}
case 'E': {
/* '%SE' -> extended encoding */
unsigned e = 0;
if (IS_info_basic(ci)) {
e = DEREF_unsigned(info_basic_extend(ci));
}
sprintf_v(buff, "%u", e);
output_string(buff);
break;
}
case 'M': {
/* '%SM' -> maximum encoding */
unsigned m = 0;
if (IS_info_basic(ci)) {
m = DEREF_unsigned(info_basic_max(ci));
}
if (have_prec) m += (unsigned)prec;
sprintf_v(buff, "%u", m);
output_string(buff);
break;
}
case 'C': {
/* '%SC' -> sortname information */
cc = NULL_cons;
if (IS_info_basic(ci)) {
cc = DEREF_cons(info_basic_sortname(ci));
}
goto cons_format;
}
case 'S': {
/* '%SS' -> subsort information */
if (IS_info_clist_etc(ci)) {
cs = DEREF_sort(info_clist_etc_arg(ci));
ci = DEREF_info(sort_info(cs));
}
goto sort_format;
}
case 'X': {
/* '%SX' -> construct encoding string */
IGNORE output_sort(cs, 0);
break;
}
default : {
goto bad_format;
}
}
break;
}
case 'V': {
c = *(s++);
if (c == 'A') {
/* '%VA' -> major version number */
sprintf_v(buff, "%u", crt_major);
output_string(buff);
} else if (c == 'B') {
/* '%VB' -> minor version number */
sprintf_v(buff, "%u", crt_minor);
output_string(buff);
} else {
goto bad_format;
}
break;
}
case 'Z': {
c = *(s++);
if (c == 'V') {
/* %ZV -> program version */
sprintf_v(buff, "%.*s", prec, progvers);
output_string(buff);
} else if (c == 'X') {
/* %ZX -> program name */
sprintf_v(buff, "%.*s", prec, progname);
output_string(buff);
} else {
goto bad_format;
}
break;
}
case 'b': {
/* '%b' -> backspaces */
if (!have_prec) prec = 1;
output_posn -= prec;
if (output_posn < 0) output_posn = 0;
break;
}
case 't': {
/* '%t' -> tabs */
if (have_prec) {
while (crt_column < (unsigned)prec) {
output_char('\t');
}
}
break;
}
case 'u': {
/* '%u' -> unique value */
if (have_prec) {
crt_unique = prec;
} else {
prec = crt_unique++;
sprintf_v(buff, "%d", prec);
output_string(buff);
}
break;
}
case '%': {
/* '%%' -> '%' */
output_char('%');
break;
}
case '@': {
/* '%@' -> '@' */
output_char('@');
break;
}
case '\n': {
/* Escaped newline */
break;
}
case '_': {
/* Dummy end marker */
break;
}
misplaced_arg : {
error(ERROR_SERIOUS, "Misplaced format, '%%%.2s'", s0);
output_string("<error>");
break;
}
default :
bad_format : {
error(ERROR_SERIOUS, "Unknown format, '%%%.2s'", s0);
output_string("<error>");
break;
}
}
} else {
output_char((int)c);
}
}
return;
}
/*
EVALUATE A CONDITION
This routine evaluates the condition given by the string s.
*/
static int
eval_cond(string s)
{
string s0 = s;
SORT cs = crt_sort;
SORT_INFO ci = crt_info;
CONSTRUCT cc = crt_cons;
PARAMETER cp = crt_param;
if (s[0] == '!') {
/* Negate condition */
return(!eval_cond(s + 1));
}
if (strneq(s, "sort.", 5)) {
/* Sort conditions */
s += 5;
sort_label : {
unsigned tag = 100;
if (!IS_NULL_info(ci)) tag = TAG_info(ci);
if (streq(s, "builtin")) return(tag == info_builtin_tag);
if (streq(s, "basic")) return(tag == info_basic_tag);
if (streq(s, "dummy")) return(tag == info_dummy_tag);
if (streq(s, "list")) return(tag == info_clist_tag);
if (streq(s, "slist")) return(tag == info_slist_tag);
if (streq(s, "option")) return(tag == info_option_tag);
if (streq(s, "simple")) {
return(tag == info_basic_tag || tag == info_dummy_tag);
}
if (streq(s, "compound")) {
if (tag == info_option_tag) return(1);
return(tag == info_clist_tag || tag == info_slist_tag);
}
if (streq(s, "extends")) {
if (tag == info_basic_tag) {
unsigned a = DEREF_unsigned(info_basic_extend(ci));
if (a) return(1);
}
return(0);
}
if (streq(s, "special")) {
int a = 0;
if (!IS_NULL_sort(cs)) {
a = DEREF_int(sort_special(cs));
}
return(a);
}
if (streq(s, "edge")) {
int a = 0;
if (!IS_NULL_sort(cs)) {
a = DEREF_int(sort_edge(cs));
}
return(a);
}
if (streq(s, "link")) {
if (!IS_NULL_sort(cs)) {
string nm = DEREF_string(sort_link(cs));
if (nm) return(1);
}
return(0);
}
if (streq(s, "unit")) {
if (!IS_NULL_sort(cs)) {
string nm = DEREF_string(sort_unit(cs));
if (nm) return(1);
}
return(0);
}
if (strneq(s, "name.", 5)) {
if (tag == info_basic_tag) {
cc = DEREF_cons(info_basic_sortname(ci));
} else {
cc = NULL_cons;
}
goto cons_label;
}
if (strneq(s, "sub.", 4)) {
s += 4;
if (tag == info_clist_tag || tag == info_slist_tag ||
tag == info_option_tag) {
cs = DEREF_sort(info_clist_etc_arg(ci));
ci = DEREF_info(sort_info(cs));
}
goto sort_label;
}
if (strneq(s, "eq.", 3)) {
s += 3;
if (!IS_NULL_sort(cs)) {
string nm = DEREF_string(sort_name(cs));
if (streq(nm, s)) return(1);
}
return(0);
}
}
} else if (strneq(s, "cons.", 5)) {
/* Construct conditions */
cons_label : {
unsigned kind = KIND_dummy;
s += 5;
if (strneq(s, "sort.", 5)) {
s += 5;
if (IS_NULL_cons(cc)) {
cs = NULL_sort;
ci = NULL_info;
}
goto sort_label;
}
if (!IS_NULL_cons(cc)) {
kind = DEREF_unsigned(cons_kind(cc));
}
if (streq(s, "simple")) return(kind == KIND_simple);
if (streq(s, "token")) return(kind == KIND_token);
if (streq(s, "cond")) return(kind == KIND_cond);
if (streq(s, "edge")) return(kind == KIND_edge);
if (streq(s, "foreign")) return(kind == KIND_foreign);
if (streq(s, "special")) return(kind == KIND_special);
if (streq(s, "params")) {
if (!IS_NULL_cons(cc)) {
LIST(PARAMETER)p = DEREF_list(cons_pars(cc));
if (!IS_NULL_list(p)) return(1);
}
return(0);
}
if (streq(s, "extends")) {
if (!IS_NULL_cons(cc)) {
if (!IS_NULL_info(ci) && IS_info_basic(ci)) {
unsigned b, e;
b = DEREF_unsigned(info_basic_bits(ci));
e = DEREF_unsigned(cons_encode(cc));
if (e >= ((unsigned)1 << b)) return(1);
}
}
return(0);
}
if (strneq(s, "eq.", 3)) {
s += 3;
if (!IS_NULL_cons(cc)) {
string nm = DEREF_string(cons_name(cc));
if (streq(nm, s)) return(1);
}
return(0);
}
}
} else if (strneq(s, "param.", 6)) {
/* Parameter conditions */
s += 6;
if (strneq(s, "sort.", 5)) {
s += 5;
if (!IS_NULL_par(cp)) {
cs = DEREF_sort(par_type(cp));
ci = DEREF_info(sort_info(cs));
} else {
cs = NULL_sort;
ci = NULL_info;
}
goto sort_label;
}
if (streq(s, "align")) {
int a = 0;
if (!IS_NULL_par(cp)) a = DEREF_int(par_align(cp));
return(a);
}
if (streq(s, "break")) {
int a = 0;
if (!IS_NULL_par(cp)) a = DEREF_int(par_brk(cp));
return(a);
}
if (streq(s, "intro")) {
int a = 0;
if (!IS_NULL_par(cp)) a = DEREF_int(par_intro(cp));
return(a);
}
if (streq(s, "first")) {
return(crt_param_no == 0);
}
if (streq(s, "last")) {
return(crt_param_no == last_param_no);
}
if (strneq(s, "eq.", 3)) {
s += 3;
if (!IS_NULL_par(cp)) {
string nm = DEREF_string(par_name(cp));
if (streq(nm, s)) return(1);
}
return(0);
}
} else {
/* Other conditions */
if (streq(s, "uniq")) return(crt_unique);
if (streq(s, "true")) return(1);
if (streq(s, "false")) return(0);
}
error(ERROR_SERIOUS, "Unknown condition, '%s'", s0);
return(0);
}
/*
WRITE A TEMPLATE FILE
This routine writes the template file given by the commands cmd for
the specification spec to the output file.
*/
static void
output_template(SPECIFICATION spec, COMMAND cmd)
{
if (!IS_NULL_cmd(cmd)) {
crt_line_no = DEREF_int(cmd_line(cmd));
switch (TAG_cmd(cmd)) {
case cmd_simple_tag: {
string s = DEREF_string(cmd_simple_text(cmd));
output(s);
break;
}
case cmd_compound_tag: {
LIST(COMMAND)p;
p = DEREF_list(cmd_compound_seq(cmd));
while (!IS_NULL_list(p)) {
COMMAND a = DEREF_cmd(HEAD_list(p));
output_template(spec, a);
p = TAIL_list(p);
}
break;
}
case cmd_loop_tag: {
string s = DEREF_string(cmd_loop_control(cmd));
COMMAND a = DEREF_cmd(cmd_loop_body(cmd));
if (streq(s, "sort")) {
/* Loop over all sorts */
SORT ls = crt_sort;
SORT_INFO li = crt_info;
LIST(SORT)ps = DEREF_list(spec_sorts(spec));
while (!IS_NULL_list(ps)) {
SORT cs = DEREF_sort(HEAD_list(ps));
int mark = DEREF_int(sort_mark(cs));
if (mark) {
SORT_INFO ci = DEREF_info(sort_info(cs));
if (!IS_NULL_info(ci)) {
crt_sort = cs;
crt_info = ci;
output_template(spec, a);
}
}
ps = TAIL_list(ps);
}
crt_sort = ls;
crt_info = li;
} else if (streq(s, "sort.cons")) {
/* Loop over all constructs */
CONSTRUCT lc = crt_cons;
SORT_INFO ci = crt_info;
if (!IS_NULL_info(ci)) {
if (IS_info_basic(ci)) {
LIST(CONSTRUCT)pc;
pc = DEREF_list(info_basic_cons(ci));
while (!IS_NULL_list(pc)) {
crt_cons = DEREF_cons(HEAD_list(pc));
output_template(spec, a);
pc = TAIL_list(pc);
}
} else if (IS_info_dummy(ci)) {
crt_cons = DEREF_cons(info_dummy_cons(ci));
output_template(spec, a);
}
}
crt_cons = lc;
} else if (streq(s, "cons.param")) {
/* Loop over all parameters */
int np = crt_param_no;
int mp = last_param_no;
PARAMETER lp = crt_param;
CONSTRUCT cc = crt_cons;
if (!IS_NULL_cons(cc)) {
LIST(PARAMETER)pp;
pp = DEREF_list(cons_pars(cc));
crt_param_no = 0;
last_param_no = (int)LENGTH_list(pp) - 1;
while (!IS_NULL_list(pp)) {
crt_param = DEREF_par(HEAD_list(pp));
output_template(spec, a);
crt_param_no++;
pp = TAIL_list(pp);
}
}
last_param_no = mp;
crt_param_no = np;
crt_param = lp;
} else if (streq(s, "param.prev")) {
/* Loop over all previous parameters */
int np = crt_param_no;
int mp = last_param_no;
PARAMETER lp = crt_param;
CONSTRUCT cc = crt_cons;
if (!IS_NULL_cons(cc)) {
LIST(PARAMETER)pp;
pp = DEREF_list(cons_pars(cc));
crt_param_no = 0;
last_param_no = np - 1;
while (!IS_NULL_list(pp) && crt_param_no < np) {
crt_param = DEREF_par(HEAD_list(pp));
output_template(spec, a);
crt_param_no++;
pp = TAIL_list(pp);
}
}
last_param_no = mp;
crt_param_no = np;
crt_param = lp;
} else {
error(ERROR_SERIOUS, "Unknown control, '%s'", s);
}
break;
}
case cmd_cond_tag: {
string s = DEREF_string(cmd_cond_control(cmd));
COMMAND a = DEREF_cmd(cmd_cond_true_code(cmd));
COMMAND b = DEREF_cmd(cmd_cond_false_code(cmd));
if (eval_cond(s)) {
output_template(spec, a);
} else {
output_template(spec, b);
}
break;
}
case cmd_use_tag: {
int m = 1;
string c = DEREF_string(cmd_use_cons(cmd));
string s = DEREF_string(cmd_use_sort(cmd));
while (s[0] == '!') {
m = !m;
s++;
}
if (c == NULL && streq(s, "all")) {
mark_all_sorts(m);
} else {
SORT sn = find_sort(s, 0);
if (c) {
CONSTRUCT cn = find_construct(sn, c);
mark_construct(cn, m);
} else {
mark_sort(sn, m);
}
}
break;
}
case cmd_special_tag: {
SORT sn;
int m = 1;
string c = DEREF_string(cmd_special_cons(cmd));
string s = DEREF_string(cmd_special_sort(cmd));
while (s[0] == '!') {
m = !m;
s++;
}
sn = find_sort(s, 0);
if (c) {
if (m) {
set_special(sn, c, KIND_special);
} else {
set_special(sn, c, KIND_simple);
}
} else {
COPY_int(sort_special(sn), m);
}
mark_sort(sn, 1);
break;
}
}
}
return;
}
/*
MAIN OUTPUT ROUTINE
This routine outputs all the information concerning the TDF specification
spec to the output file nm using the template cmd.
*/
void
output_spec(char *nm, SPECIFICATION spec, COMMAND cmd)
{
CONST char *tnm = crt_file_name;
crt_line_no = 1;
if (nm == NULL || streq(nm, "-")) {
crt_file_name = "<stdout>";
output_file = stdout;
nm = NULL;
} else {
crt_file_name = nm;
output_file = fopen(nm, "w");
if (output_file == NULL) {
error(ERROR_SERIOUS, "Can't open output file, '%s'", nm);
return;
}
}
output_posn = 0;
crt_column = 0;
crt_file_name = tnm;
crt_major = DEREF_unsigned(spec_major(spec));
crt_minor = DEREF_unsigned(spec_minor(spec));
output_template(spec, cmd);
if (output_posn) output_char('\n');
if (nm) fclose_v(output_file);
return;
}