diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/funnelAC/sources/weave.c |
Initial commit
Diffstat (limited to 'web/funnelAC/sources/weave.c')
-rw-r--r-- | web/funnelAC/sources/weave.c | 976 |
1 files changed, 976 insertions, 0 deletions
diff --git a/web/funnelAC/sources/weave.c b/web/funnelAC/sources/weave.c new file mode 100644 index 0000000000..ad3ab27113 --- /dev/null +++ b/web/funnelAC/sources/weave.c @@ -0,0 +1,976 @@ +/*############################################################################## + +FUNNNELWEB COPYRIGHT +==================== +FunnelWeb is a literate-programming macro preprocessor. + +Copyright (C) 1992 Ross N. Williams. + + Ross N. Williams + ross@spam.adelaide.edu.au + 16 Lerwick Avenue, Hazelwood Park 5066, Australia. + +This program is free software; you can redistribute it and/or modify +it under the terms of Version 2 of the GNU General Public License as +published by the Free Software Foundation. + +This program is distributed WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See Version 2 of the GNU General Public License for more details. + +You should have received a copy of Version 2 of the GNU General Public +License along with this program. If not, you can FTP the license from +prep.ai.mit.edu/pub/gnu/COPYING-2 or write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Section 2a of the license requires that all changes to this file be +recorded prominently in this file. Please record all changes here. + +Programmers: + RNW Ross N. Williams ross@spam.adelaide.edu.au + +Changes: + 07-May-1992 RNW Program prepared for release under GNU GPL V2. + +##############################################################################*/ + + +/******************************************************************************/ +/* WEAVE.C */ +/******************************************************************************/ + +#include <ctype.h> +#include "style.h" + +#include "as.h" +#include "data.h" +#include "lister.h" +#include "machin.h" +#include "misc.h" +#include "table.h" +#include "texhead.h" +#include "weave.h" + +/* IMPORTANT WARNING: The Macintosh THINK C compiler sets up strings that */ +/* start with \p or \P as PASCAL strings rather than C strings. So be very */ +/* careful not to start any strings with \p or \P. */ + +/******************************************************************************/ + +LOCVAR wf_t f_t; /* Variable representing documentation file. */ +LOCVAR bool literal; /* True if we are inside a literal @{. */ + +/******************************************************************************/ + +LOCAL void wv_ex P_((p_ells_t)); + +/******************************************************************************/ + +LOCAL char * fn_nodir P_((char *)); +LOCAL char * fn_nodir (p) +/* Given a pointer to a string, returns a pointer to the character just past */ +/* the last occurrance of FN_DELIM, or a pointer to the start of the string */ +/* if it does not occur. In other words, given a filename, return a pointer */ +/* to the start of the name field. */ +char *p; +{ + char *q = strrchr(p,FN_DELIM); + if (q==NULL) + return p; + else + return q+1; +} + +/******************************************************************************/ + +LOCAL void wf_dec P_((p_wf_t,ulong)); +LOCAL void wf_dec(p_wf,n) +/* Writes the unsigned number n to the specified output stream as a decimal. */ +/* This function has been named wf_dec so that it can be moved to the wf */ +/* package if it turns out to be popular. */ +p_wf_t p_wf; +ulong n; +{ + char t[20]; + sprintf(t,"%lu",n); + wf_wr(p_wf,t); +} + +/******************************************************************************/ + +LOCAL void wv_ugch P_((int)); +LOCAL void wv_ugch(ch) +/* WeaVe UGly CHaracter. */ +/* Sometimes it is necessary to output text without causing the typesetting */ +/* program (TeX) to interpret it as control sequences and generate errors. */ +/* There are two ways of doing this. First, we can get the typesetter to do */ +/* the work by making it less sensitive. This has been done with the */ +/* typesetting of the main macro text. The other approach is to convert each */ +/* offending character into the typesetter control sequence that will simply */ +/* output the character. For example, in TeX, "$" becomes "\$". This function */ +/* accepts a single character and writes out its safe equivalent. */ +/* The term "ugly" is used to describe potentially harmful text/characters. */ +int ch; +{ + /* End of lines are the only non-printable allowed. */ + if (ch==EOL) {wf_chr(&f_t,EOL); return;} + + /* All other non-printables result in a bullet. In the absence of bugs, the */ + /* only way a non-printable can get to us here is as a result of the user */ + /* explicitly inserting one using the scanner's @^ control sequence. */ + if (!isascprn(ch)) + { + wf_wr(&f_t,"$\\bullet$"); + return; + } + /* If it IS printable, then print it ... carefully! */ + switch (ch) + { + /* Go through the printables in ASCII order. */ + /* ' ': is an ordinary character. */ + /* '!': is an ordinary character. */ + case '\"': wf_wr(&f_t,"\\char`\\\""); break; + case '#': wf_wr(&f_t,"\\#"); break; + case '$': wf_wr(&f_t,"\\$"); break; + /* Note: The string "\\%" triggers a unknown sequence "\%" in GNUC. */ + case '%': wf_chr(&f_t,'\\'); wf_chr(&f_t,'%'); break; + case '&': wf_wr(&f_t,"\\&"); break; + /* ''': is an ordinary character. */ + /* '(': is an ordinary character. */ + /* ')': is an ordinary character. */ + /* '*': is an ordinary character. */ + /* '+': is an ordinary character. */ + /* ',': is an ordinary character. */ + /* '-': is an ordinary character. */ + /* '.': is an ordinary character. */ + /* '/': is an ordinary character. */ + /* '0-9': are ordinary characters.*/ + /* ':': is an ordinary character. */ + /* ';': is an ordinary character. */ + /* Note: Some \chars work well in roman. Others don't. */ + case '<': if (literal) + wf_wr(&f_t,"\\char`\\<"); + else + wf_wr(&f_t,"$<$"); + break; + /* '=': is an ordinary character. */ + case '>': if (literal) + wf_wr(&f_t,"\\char`\\>"); + else + wf_wr(&f_t,"$>$"); + break; + /* '?': is an ordinary character. */ + /* '@': is an ordinary character. */ + /* 'A-Z': are ordinary characters.*/ + /* '[': is an ordinary character. */ + case '\\': if (literal) + wf_wr(&f_t,"\\char`\\\\"); + else + wf_wr(&f_t,"$\\backslash$"); + break; + /* ']': is an ordinary character. */ + case '^': wf_wr(&f_t,"\\char`\\^"); break; + case '_': wf_wr(&f_t,"\\_"); break; + /* '`': is an ordinary character. */ + /* 'a-z': are ordinary characters.*/ + case '{': if (literal) + wf_wr(&f_t,"\\char`\\{"); + else + wf_wr(&f_t,"$\\{$"); + break; + case '|': if (literal) + wf_wr(&f_t,"\\char`\\|"); + else + wf_wr(&f_t,"$|$"); + break; + case '}': if (literal) + wf_wr(&f_t,"\\char`\\}"); + else + wf_wr(&f_t,"$\\}$"); + break; + case '~': wf_wr(&f_t,"\\char`\\~"); break; + default: wf_chr(&f_t,ch); break; + } +} + +/******************************************************************************/ + +LOCAL void wv_ugstr P_((char *)); +LOCAL void wv_ugstr(s) +/* Writes out a potentially ugly string. */ +char *s; +{ + while (*s != EOS) + wv_ugch(*s++); +} + +/******************************************************************************/ + +LOCAL void wv_ugsc P_((p_sc_t)); +LOCAL void wv_ugsc(p_sc) +/* Writes out a potentially ugly text scrap. */ +p_sc_t p_sc; +{ + char *p = p_sc->sc_first; + char *p_last = p_sc->sc_last; + + while (p<=p_last) + wv_ugch(*p++); +} + +/******************************************************************************/ + +LOCAL void wv_ugscls P_((p_scls_t)); +LOCAL void wv_ugscls(p_scls) +/* This function writes the specified text list to the output stream. It */ +/* writes it cleaning up all ugly characters. */ +p_scls_t p_scls; +{ + ls_fir(p_scls); + while (TRUE) + { + p_sc_t p_sc; + ls_nxt(p_scls,PPV &p_sc); + if (p_sc==NULL) break; + wv_ugsc(p_sc); + } +} + +/******************************************************************************/ + +LOCAL void wv_manm P_((p_ma_t,ulong)); +LOCAL void wv_manm(p_ma,part) +/* This function writes the specified macro name and number. */ +/* The part number is the part to write out. */ +p_ma_t p_ma; +ulong part; +{ + p_bp_t p_bp; + + if (p_ma->ma_defn.md_isfil) + wf_wr(&f_t,"\\fwfilename{"); + else + wf_wr(&f_t,"\\fwmacroname{"); + wv_ugstr(p_ma->ma_name); + wf_wr(&f_t,"}{"); + ls_loo(p_ma->ma_defn.md_body,part,PPV &p_bp); + wf_dec(&f_t,(ulong) p_bp->bp_seq); + wf_wr(&f_t,"}"); +} + +/******************************************************************************/ + +LOCAL void wv_secnn P_((p_ty_t)); +LOCAL void wv_secnn(p_ty) +/* Write out the section number and name, each in curly braces. */ +p_ty_t p_ty; +{ + /* Write as arguments the section number and name. */ + wf_chr(&f_t,'{'); + sn_str(&p_ty->ty_sn,linet1); + wf_wr(&f_t,linet1); + wf_wr(&f_t,"}{"); + wv_ugstr(p_ty->ty_name); + wf_chr(&f_t,'}'); +} + +/******************************************************************************/ + +LOCAL void wv_echo P_((p_scls_t)); +LOCAL void wv_echo(p_scls) +/* This function writes the specified text list to the output stream. It */ +/* writes it just as it is, with no modifications whatsoever. */ +p_scls_t p_scls; +{ + ls_fir(p_scls); + while (TRUE) + { + p_sc_t p_sc; + ls_nxt(p_scls,PPV &p_sc); + if (p_sc==NULL) break; + wf_blk(&f_t,p_sc->sc_first,(size_t) + (p_sc->sc_last-p_sc->sc_first+1)); + } +} + +/******************************************************************************/ + +LOCAL void wv_verb P_((p_scls_t)); +LOCAL void wv_verb(p_scls) +/* Writes a text list verbetim, but brackets it with sequences that instruct */ +/* the typesetter not to interpret the text in any special way. */ +p_scls_t p_scls; +{ + if (ls_len(p_scls) > 0) + { + wf_wr(&f_t,"\\fwbtx["); + wv_echo(p_scls); + wf_wl(&f_t,"]fwetx=%"); + } +} + +/******************************************************************************/ + +LOCAL void wv_head P_((void)); +LOCAL void wv_head() +{ + wf_wl(&f_t,""); + wf_wl(&f_t,"%*******************************************************************************"); + wf_wl(&f_t,"%* START OF AUTOMATICALLY GENERATED TEX FILE *"); + wf_wl(&f_t,"%*******************************************************************************"); + wf_wl(&f_t,"%* *"); + wf_wl(&f_t,"%* This TeX file was automatically generated by the FunnelWeb preprocessor. *"); + wf_wl(&f_t,"%* You can typeset this file to produce printed documentation by running it *"); + wf_wl(&f_t,"%* through the TeX typesetter using a command such as: *"); + wf_wl(&f_t,"%* tex thisfilename *"); + wf_wl(&f_t,"%* The resultant file thisfilename.dvi can be printed using a command such as: *"); + wf_wl(&f_t,"%* lpr -Pcslw -d thisfilename.dvi *"); + wf_wl(&f_t,"%* *"); + wf_wl(&f_t,"%* FunnelWeb is a preprocessor that allows programmers to weave programs and *"); + wf_wl(&f_t,"%* their documentation together in a single document. The FunnelWeb program *"); + wf_wl(&f_t,"%* analyses such documents producing both program files and typeset *"); + wf_wl(&f_t,"%* documentation such as this TeX file. *"); + wf_wl(&f_t,"%* FunnelWeb was created by Ross Williams. *"); + wf_wl(&f_t,"%* *"); + wf_wl(&f_t,"%* For more information on FunnelWeb look in the following FTP archive: *"); + wf_wl(&f_t,"%* Machine : sirius.itd.adelaide.edu.au [IP=129.127.40.3]. *"); + wf_wl(&f_t,"%* Directory: ~pub/funnelweb/ *"); + wf_wl(&f_t,"%* (or some other appropriately named directory). *"); + wf_wl(&f_t,"%* or email Ross Williams at ross@spam.adelaide.edu.au *"); + wf_wl(&f_t,"%* *"); + wf_wl(&f_t,"%*******************************************************************************"); + wf_wl(&f_t,""); + wf_wl(&f_t,""); +} + +/******************************************************************************/ + +LOCAL void wv_tail P_((void)); +LOCAL void wv_tail() +{ + wf_wl(&f_t,""); + wf_wl(&f_t,""); + wf_wl(&f_t,"%*******************************************************************************"); + wf_wl(&f_t,"%* END OF AUTOMATICALLY GENERATED TEX FILE *"); + wf_wl(&f_t,"%*******************************************************************************"); + wf_wl(&f_t,""); +} + +/******************************************************************************/ + +LOCAL void wv_nsec P_((p_ty_t)); +LOCAL void wv_nsec(p_ty) +/* Weaves the start of a new section. */ +p_ty_t p_ty; +{ + as_cold(p_ty->ty_kind==TY_NSEC,"wv_nsec: Not a section!"); + + /* Work out what kind of section macro should be used. */ + switch (sn_lev(&p_ty->ty_sn)) + { + case 1: wf_wr(&f_t,"\\fwseca"); break; + case 2: wf_wr(&f_t,"\\fwsecb"); break; + case 3: wf_wr(&f_t,"\\fwsecc"); break; + case 4: wf_wr(&f_t,"\\fwsecd"); break; + case 5: wf_wr(&f_t,"\\fwsece"); break; + default: as_bomb("wv_nsec:case defaulted."); + } + + /* Write out the section number and name. */ + wv_secnn(p_ty); +} + +/******************************************************************************/ + +LOCAL void wv_olit P_((p_ty_t)); +LOCAL void wv_olit(p_ty) +p_ty_t p_ty; +{ + as_cold(p_ty->ty_kind==TY_OLIT,"wv_olit: Not an open literal!"); + wf_wr(&f_t,"\\fwlit{"); + literal=TRUE; +} + +/******************************************************************************/ + +LOCAL void wv_clit P_((p_ty_t)); +LOCAL void wv_clit(p_ty) +p_ty_t p_ty; +{ + as_cold(p_ty->ty_kind==TY_CLIT,"wv_clit: Not a close literal!"); + wf_wr(&f_t,"}"); + literal=FALSE; +} + +/******************************************************************************/ + +LOCAL void wv_oemp P_((p_ty_t)); +LOCAL void wv_oemp(p_ty) +p_ty_t p_ty; +{ + as_cold(p_ty->ty_kind==TY_OEMP,"wv_oemp: Not an open emphasize!"); + wf_wr(&f_t,"\\fwemp{"); +} + +/******************************************************************************/ + +LOCAL void wv_cemp P_((p_ty_t)); +LOCAL void wv_cemp(p_ty) +p_ty_t p_ty; +{ + as_cold(p_ty->ty_kind==TY_CEMP,"wv_cemp: Not a close emphasize!"); + wf_wr(&f_t,"}"); +} + +/******************************************************************************/ + +LOCAL void wv_npag P_((p_ty_t)); +LOCAL void wv_npag(p_ty) +p_ty_t p_ty; +{ + as_cold(p_ty->ty_kind==TY_NPAG,"wv_npag: Not a new page!"); + wf_wl(&f_t,""); + wf_wl(&f_t,"\\fwnewpage"); + wf_wl(&f_t,""); +} + +/******************************************************************************/ + +LOCAL void wv_tocs P_((p_ty_t)); +LOCAL void wv_tocs(p_ty) +/* Writes a table of contents to the weave output file. */ +p_ty_t p_ty; +{ + /* We need to save the document list position as the rest of the weaver is */ + /* in the process of traversing it!!! */ + p_void p_mark = ls_mar(document_list); + + as_cold(p_ty->ty_kind==TY_TOCS,"wv_tocs: Not a table of contents!"); + + wf_wl(&f_t,""); + wf_wl(&f_t,"\\fwtocstart{}"); + ls_fir(document_list); + while (TRUE) + { + p_dc_t p_dc; + ls_nxt(document_list,PPV &p_dc); + if (p_dc == NULL) break; + if (p_dc->dc_kind==DC_TYPE && p_dc->dc_ty.ty_kind==TY_NSEC) + { + switch(sn_lev(&p_dc->dc_ty.ty_sn)) + { + case 1: wf_wr(&f_t,"\\fwtoca"); break; + case 2: wf_wr(&f_t,"\\fwtocb"); break; + case 3: wf_wr(&f_t,"\\fwtocc"); break; + case 4: wf_wr(&f_t,"\\fwtocd"); break; + case 5: wf_wr(&f_t,"\\fwtoce"); break; + default: as_bomb("wv_toc: switch defaulted."); + } + wv_secnn(&p_dc->dc_ty); + wf_wl(&f_t,""); + } + } + wf_wl(&f_t,"\\fwtocfinish{}"); + wf_wl(&f_t,""); + + /* Restore the document list position. */ + ls_set(document_list,p_mark); +} + +/******************************************************************************/ + +LOCAL void wv_skip P_((p_ty_t)); +LOCAL void wv_skip(p_ty) +/* Writes a vertical space to the output file. */ +p_ty_t p_ty; +{ + as_cold(p_ty->ty_kind==TY_SKIP,"wv_skip: Not a skip directive!"); + + sprintf(linet1,"\\fwvskip{%lu}",(ulong) p_ty->ty_mm); + wf_wl(&f_t,linet1); +} + +/******************************************************************************/ + +LOCAL void wv_titl P_((p_ty_t)); +LOCAL void wv_titl(p_ty) +/* Writes a title line to the output file. */ +p_ty_t p_ty; +{ + as_cold(p_ty->ty_kind==TY_TITL,"wv_titl: Not a title!"); + + switch(p_ty->ty_align) + { + case LR_LEFT: wf_wr(&f_t,"\\fwleftline{"); break; + case LR_RIGH: wf_wr(&f_t,"\\fwrightline{"); break; + case LR_CENT: wf_wr(&f_t,"\\fwcenterline{"); break; + default: as_bomb("wv_title: Alignment switch defaulted."); + } + switch(p_ty->ty_font) + { + case FT_NORM: wf_wr(&f_t,"\\fwfontnormal{"); break; + case FT_TITL: wf_wr(&f_t,"\\fwfonttitle{"); break; + case FT_STIT: wf_wr(&f_t,"\\fwfontsmalltitle{"); break; + default: as_bomb("wv_title: Font switch defaulted."); + } + wv_ugsc(&p_ty->ty_sc); + wf_wl(&f_t,"}}"); +} + +/******************************************************************************/ + +LOCAL void wv_dcty P_((p_dc_t)); +LOCAL void wv_dcty(p_dc) +/* Weaves a document typesetter element. */ +p_dc_t p_dc; +{ + p_ty_t p_ty = &p_dc->dc_ty; + + as_cold(p_dc->dc_kind==DC_TYPE,"wv_dcty: Not a typesetter component!"); + + switch (p_ty->ty_kind) + { + case TY_NSEC: wv_nsec(p_ty); break; + case TY_OLIT: wv_olit(p_ty); break; + case TY_CLIT: wv_clit(p_ty); break; + case TY_OEMP: wv_oemp(p_ty); break; + case TY_CEMP: wv_cemp(p_ty); break; + case TY_NPAG: wv_npag(p_ty); break; + case TY_TOCS: wv_tocs(p_ty); break; + case TY_SKIP: wv_skip(p_ty); break; + case TY_TITL: wv_titl(p_ty); break; + default : as_bomb("wv_dcty: Switch defaulted."); + } +} + +/******************************************************************************/ + +LOCAL void wv_dctx P_((p_dc_t)); +LOCAL void wv_dctx(p_dc) +/* Write out a document text item. */ +p_dc_t p_dc; +{ + as_cold(p_dc->dc_kind==DC_TEXT,"wv_dctx: Not text."); + + if (literal || tr_codes==TR_NONE) + wv_ugscls(p_dc->dc_text); + else + wv_echo(p_dc->dc_text); +} + +/******************************************************************************/ + +LOCAL void wv_eltx P_((p_el_t)); +LOCAL void wv_eltx(p_el) +/* Weaves a text element. */ +p_el_t p_el; +{ + as_cold(p_el->el_kind==EL_TEXT,"wv_eltx: Not text."); + wv_verb(p_el->el_text); +} + +/******************************************************************************/ + +LOCAL void wv_elin P_((p_el_t)); +LOCAL void wv_elin(p_el) +/* Weaves a invocation element. */ +p_el_t p_el; +{ + as_cold(p_el->el_kind==EL_INVC,"wv_elin: Not an invocation."); + + /* Write out the macro call proper. */ + wv_manm(p_el->el_p_mac,1L); + + /* Write out the actual parameter list, if any. */ + if (ls_len(p_el->el_parls) > 0) + { + ulong parnum; + wf_wr(&f_t,"\\fwoparen "); + ls_fir(p_el->el_parls); /* List of actual parameter expressions. */ + ls_fir(p_el->el_pretx); /* List of crud before each expression. */ + ls_fir(p_el->el_postx); /* List of crud after each expression. */ + parnum=1; + while (TRUE) + { + p_ells_t *pp_exp; + p_scls_t *pp_scls1; + p_scls_t *pp_scls2; + ls_nxt(p_el->el_parls,PPV &pp_exp); + ls_nxt(p_el->el_pretx,PPV &pp_scls1); + ls_nxt(p_el->el_postx,PPV &pp_scls2); + if (pp_exp==NULL) break; + if (parnum++>1) wf_wr(&f_t,"\\fwcomma "); + wv_verb(*pp_scls1); + wf_wr(&f_t,"\\fwoquote"); wv_ex(*pp_exp); wf_wr(&f_t,"\\fwcquote"); + wv_verb(*pp_scls2); + } + wf_wr(&f_t,"\\fwcparen "); + } +} + +/******************************************************************************/ + +LOCAL void wv_elpr P_((p_el_t)); +LOCAL void wv_elpr(p_el) +/* Weaves a parameter element. */ +p_el_t p_el; +{ + as_cold(p_el->el_kind==EL_PARM,"wv_elpr: Not a parameter."); + wf_wr (&f_t,"\\fwparam{"); + wf_dec(&f_t,(ulong) p_el->el_parno); + wf_wr (&f_t,"}"); +} + + +/******************************************************************************/ + +LOCAL void wv_ex(p_exp) +/* Weaves a expression. */ +p_ells_t p_exp; +{ + + ls_fir(p_exp); + while (TRUE) + { + p_el_t p_el; + ls_nxt(p_exp,PPV &p_el); + if (p_el==NULL) break; + switch (p_el->el_kind) + { + case EL_TEXT: wv_eltx(p_el); break; + case EL_INVC: wv_elin(p_el); break; + case EL_PARM: wv_elpr(p_el); break; + default: as_bomb("wv_ex: Case defaulted."); + } + } +} + +/******************************************************************************/ + +LOCAL void wv_usage P_((p_ma_t)); +LOCAL void wv_usage(p_ma) +/* Weaves a list of all the sections in which the specified macro is used. */ +p_ma_t p_ma; +{ + ulong no_calls = ls_len(p_ma->ma_calls); + ulong callno; + + /* If it is a file macro then it is not allowed to be called. */ + if (p_ma->ma_defn.md_isfil) + { + wf_wl(&f_t,"\\fwisafile{This macro is attached to an output file.}"); + return; + } + + if (no_calls==0) + { + wf_wl(&f_t,"\\fwusedin{This macro is NEVER invoked.}"); + return; + } + + wf_wr(&f_t,"\\fwusedin{This macro is invoked in definition"); + if (no_calls>1) wf_chr(&f_t,'s'); + wf_chr(&f_t,' '); + ls_fir(p_ma->ma_calls); + for (callno=1;callno<=no_calls;callno++) + { + p_mc_t p_mc; + ls_nxt(p_ma->ma_calls,PPV &p_mc); as_cold(p_mc!=NULL,"wv_usage: Awk!"); + wf_dec(&f_t,(ulong) p_mc->mc_seq); + if (callno < no_calls-1) + wf_wr(&f_t,", "); + else + if (callno==no_calls-1) + if (no_calls>2) + wf_wr(&f_t,", and "); + else + wf_wr(&f_t," and "); + } + wf_wl(&f_t,".}"); +} + +/******************************************************************************/ + +LOCAL void wv_defin P_((p_ma_t)); +LOCAL void wv_defin(p_ma) +/* Just after each macro body part definition comes a note listing the */ +/* containing the macro's body parts. This function weaves that list. */ +p_ma_t p_ma; +{ + ulong no_parts = ls_len(p_ma->ma_defn.md_body); + ulong partno; + + /* If it's not an additive macro, we don't write anything. */ + if (!p_ma->ma_defn.md_isadd) return; + + wf_wr(&f_t,"\\fwseealso{This macro is defined in definitions "); + ls_fir(p_ma->ma_defn.md_body); + for (partno=1;partno<=no_parts;partno++) + { + p_bp_t p_bp; + ls_nxt(p_ma->ma_defn.md_body,PPV &p_bp); + as_cold(p_bp!=NULL,"wv_defin: Awk!"); + wf_dec(&f_t,(ulong) p_bp->bp_seq); + if (partno<no_parts-1) + wf_wr(&f_t,", "); + else + if (partno==no_parts-1) + if (no_parts>2) + wf_wr(&f_t,", and "); + else + wf_wr(&f_t," and "); + } + wf_wl(&f_t,".}"); +} + +/******************************************************************************/ + +LOCAL void wv_dcma P_((p_dc_t)); +LOCAL void wv_dcma(p_dc) +/* Weaves a macro definition. */ +p_dc_t p_dc; +{ + p_bp_t p_bp; + + /* Macro calls sandwich the definition itsef. */ + wf_wl(&f_t,"\\fwbeginmacro"); + + /* Write out the macro's name. */ + wv_manm(p_dc->dc_p_ma,p_dc->dc_part); + + /* Write the formal parameter list. */ + if (p_dc->dc_p_ma->ma_defn.md_npar>0) + { + wf_wr(&f_t,"\\fwparams{"); + wf_dec(&f_t,(ulong) p_dc->dc_p_ma->ma_defn.md_npar); + wf_wr(&f_t,"}"); + } + + /* Zero and many options are indicated by bold letters. */ + if (p_dc->dc_p_ma->ma_defn.md_iszer) wf_wr(&f_t,"\\fwzero{}"); + if (p_dc->dc_p_ma->ma_defn.md_isman) wf_wr(&f_t,"\\fwmany{}"); + + /* Write the "equals". */ + if (p_dc->dc_p_ma->ma_defn.md_isadd) + wf_wr(&f_t,"\\fwplusequals "); + else + wf_wr(&f_t,"\\fwequals "); + + /* Write out this particular body part. */ + ls_loo(p_dc->dc_p_ma->ma_defn.md_body,p_dc->dc_part,PPV &p_bp); + wf_wr(&f_t,"\\fwodef "); + wv_ex(p_bp->bp_ex); + wf_wl(&f_t,"\\fwcdef "); + + /* After the macro definition, write out notes on the macro. */ + wf_wl(&f_t,"\\fwbeginmacronotes"); + wv_defin(p_dc->dc_p_ma); /* A list of the macro's body parts. */ + wv_usage(p_dc->dc_p_ma); /* A list of where the macro is used. */ + wf_wl(&f_t,"\\fwendmacronotes"); + + /* The macro definition is sandwiched by macro calls. */ + wf_wl(&f_t,"\\fwendmacro"); +} + +/******************************************************************************/ + +LOCAL void wv_dc P_((p_dc_t)); +LOCAL void wv_dc(p_dc) +/* Weaves a document component (p_dc). */ +p_dc_t p_dc; +{ + switch (p_dc->dc_kind) + { + case DC_TEXT: wv_dctx(p_dc); break; + case DC_TYPE: wv_dcty(p_dc); break; + case DC_MACR: wv_dcma(p_dc); break; + default : as_bomb("wv_dc: Case defaulted."); + } +} + +/******************************************************************************/ + +LOCAL void wv_docnt P_((void)); +LOCAL void wv_docnt() +/* Weaves the entire document. This functions and most functions above assume */ +/* that the output stream f_t is prepared for writing. */ +{ + /* Write the typeset header. */ + wv_head(); + + /* Writes the library macros. */ + tex_head(&f_t); + + ls_fir(document_list); + literal=FALSE; + while (TRUE) + { + p_dc_t p_dc; + ls_nxt(document_list,PPV &p_dc); + if (p_dc == NULL) break; + wv_dc(p_dc); + } + wf_wl(&f_t,"\\bye"); + + /* Write the typeset trailer. */ + wv_tail(); +} + +/******************************************************************************/ + +EXPORT void weave() +/* Writes out the documentation file. */ +{ + fn_t fn_tmp; /* Name of temporary file. */ + fn_t fn_targ; /* Name of documentation file. */ + bool renfil; /* Do we wish to rename output file? */ + bool istarg; /* Does a target file already exist? */ + + /* Writing the documentation file differs from some other output files. With */ + /* non critical files such as the listing file that are really just logs, */ + /* generation of half a listing file is acceptable if not desirable. However */ + /* in the case of the documentation file, it is very bad to generate half an */ + /* output file; far better to generate none at all. For this reason, and */ + /* also because of the presence of the D option (which prevents the writing */ + /* of output files identical to existing files (to prevent MAKE */ + /* propagations)) it is best to write a temporary file and then rename it. */ + + /* We shouldn't be here if the weave option is off! */ + as_cold(option.op_t_b,"weave: option.op_t_b==FALSE!"); + + /* Work out what the typeset file's name should be. */ + strcpy(fn_targ,""); /* Start with an empty string. */ + fn_ins(fn_targ,option.op_f_s); + fn_ins(fn_targ,".tex"); + fn_ins(fn_targ,option.op_t_s); + + /* The temporary file has to inherit too, because the output directory may */ + /* not be the default directory and some computers can't rename across */ + /* directories (and we have to rename it later). */ + strcpy(fn_tmp,fn_targ); + fn_ins(fn_tmp,fn_temp()); + + /* Expand the macro to the temporary file. */ + wf_ini(&f_t,TRUE); + wf_ope(&f_t,fn_tmp); + if (wf_err(&f_t)) + { + sprintf(linet1,"Error creating temporary documentation file \"%s\".",&fn_tmp[0]); + wl_sjl(linet1); + (void) remove(fn_tmp); + goto severe; + } + + wv_docnt(); + + /* Make sure that there weren't any errors writing to the temporary file. */ + if (wf_err(&f_t)) + { + sprintf(linet1,"Error writing to temporary documentation file \"%s\".",&fn_tmp[0]); + wl_sjl(linet1); + (void) remove(fn_tmp); + goto severe; + } + + /* Close the temporary file. */ + wf_clo(&f_t); + if (wf_err(&f_t)) + { + sprintf(linet1,"Error closing temporary documentation file \"%s\".",&fn_tmp[0]); + wl_sjl(linet1); + remove(fn_tmp); + goto severe; + } + + /* The rest of the code in this function copes with the renaming. */ + + /* By default, we wish to rename the temporary file. */ + renfil=TRUE; + + /* Deal with any existing file of the target name. */ + istarg=fexists(fn_targ); + if (istarg && option.op_d_b) + { + /* A target already exists, and the D option is on. If the target is */ + /* identical to the temporary, we can simply delete the temporary! */ + char *errstr; + bool same; + errstr=eq_files(fn_tmp,fn_targ,&same); + if (errstr != NULL) + { + wl_sjl("Error comparing temporary file with previous documentation file."); + wl_sjl("(A comparison was attempted because the D option was turned on.)"); + wl_sjl("Error from comparison routine was as follows (first=temp):"); + wr_sjl(" ");wl_sjl(errstr); + sprintf(linet1,"Temporary file name was \"%s\".",&fn_tmp[0]); + wl_sjl(linet1); + sprintf(linet1,"Output file name was \"%s\".",&fn_targ[0]); + wl_sjl(linet1); + wl_sjl("FunnelWeb will leave both files intact so you can look at them."); + goto severe; + } + /* If the two files are the same, we can simply delete the temporary. */ + if (same) + { + int status; + status=remove(fn_tmp); + if (status != REMOVE_S) + { + sprintf(linet1,"Error deleting (under +D option) temporary file \"%s\".",&fn_tmp[0]); + wl_sjl(linet1); + goto severe; + } + sprintf(linet1,"Deleted identical documentation file \"%s\".",&fn_targ[0]); + wl_sjl(linet1); + renfil=FALSE; + } + } + + if (renfil) + { + int status; + /* We need to delete any existing file of the target name. */ + if (istarg) + { + status=remove(fn_targ); + if (status != REMOVE_S) + { + sprintf(linet1,"Error deleting existing documentation file \"%s\".",&fn_targ[0]); + wl_sjl(linet1); + goto severe; + } + } + /* Rename the temporary file to the output file. */ + status=rename(fn_tmp,fn_targ); + if (status != RENAME_S) + { + wl_sjl("Error renaming temporary documentation file to documentation file."); + sprintf(linet1,"Temporary file name was \"%s\".",&fn_tmp[0]); + wl_sjl(linet1); + sprintf(linet1,"Output file name was \"%s\".",&fn_targ[0]); + wl_sjl(linet1); + wl_sjl("FunnelWeb will leave both files intact so you can look at them."); + goto severe; + } + } + + /* Tell everyone that we have written an output file. */ + sprintf(linet1,"Weave : Completed %s.",fn_nodir(&fn_targ[0])); + wl_s(linet1); + if (option.op_b7_b) + sprintf(linet1,"Weave: Completed %s.",SUPPNAME); + wl_j(linet1); + wl_l(linet1); + return; + + /* Jump here is a nasty file error occurs. */ + severe: + sprintf(linet1,"A problem occurred during the generation of documentation file \"%s\".",&fn_targ[0]); + wl_sjl(linet1); + wl_sjl("S: Aborting..."); + num_sev++; + return; +} + +/******************************************************************************/ + |