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/tango-weevil |
Initial commit
Diffstat (limited to 'web/tango-weevil')
-rw-r--r-- | web/tango-weevil/CHANGES | 3 | ||||
-rw-r--r-- | web/tango-weevil/COPYING | 339 | ||||
-rw-r--r-- | web/tango-weevil/Makefile | 86 | ||||
-rw-r--r-- | web/tango-weevil/README | 34 | ||||
-rw-r--r-- | web/tango-weevil/c_tango.c | 798 | ||||
-rw-r--r-- | web/tango-weevil/c_weevil.tw | 173 | ||||
-rw-r--r-- | web/tango-weevil/hello.tw | 69 | ||||
-rw-r--r-- | web/tango-weevil/latex.tw | 304 | ||||
-rw-r--r-- | web/tango-weevil/tango.c | 1017 | ||||
-rw-r--r-- | web/tango-weevil/tango.h | 120 | ||||
-rw-r--r-- | web/tango-weevil/tw.sty | 114 | ||||
-rw-r--r-- | web/tango-weevil/userguid.tpc | 506 | ||||
-rw-r--r-- | web/tango-weevil/weevdoc.tex | 44 | ||||
-rw-r--r-- | web/tango-weevil/weevil.tw | 1367 | ||||
-rw-r--r-- | web/tango-weevil/weevinfo.tw | 418 |
15 files changed, 5392 insertions, 0 deletions
diff --git a/web/tango-weevil/CHANGES b/web/tango-weevil/CHANGES new file mode 100644 index 0000000000..bbd4bcd31e --- /dev/null +++ b/web/tango-weevil/CHANGES @@ -0,0 +1,3 @@ + +v1.0 - 08/24/95 - First official release + diff --git a/web/tango-weevil/COPYING b/web/tango-weevil/COPYING new file mode 100644 index 0000000000..a43ea2126f --- /dev/null +++ b/web/tango-weevil/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/web/tango-weevil/Makefile b/web/tango-weevil/Makefile new file mode 100644 index 0000000000..938e231bff --- /dev/null +++ b/web/tango-weevil/Makefile @@ -0,0 +1,86 @@ + +unexport GCC_EXEC_PREFIX + +CC = gcc + +CFLAGS=$(CDEBUGFLAGS) + +all: weevil + +TANGO_OBJS = tango.o c_tango.o + +TANGO_SRCS = tango.c c_tango.c + +WEEVIL_OBJS = weevil.o c_weevil.o latex.o + +WEEVIL_SRCS = weevil.c c_weevil.c latex.c + +WEEVIL_TANGOS = weevil.tw c_weevil.tw latex.tw + +WEEVIL_DOCS = weevinfo.tex weevil.tex c_weevil.tex latex.tex + +weevil: $(WEEVIL_OBJS) + $(CC) $(CFLAGS) -o weevil $(WEEVIL_OBJS) + +weevil.c: tango weevil.tw weevinfo.h + ./tango -autoxref -lang c weevil.tw >weevil.c + +c_weevil.c: tango c_weevil.tw weevinfo.h + ./tango -autoxref -lang c c_weevil.tw >c_weevil.c + +latex.c: tango latex.tw weevinfo.h + ./tango -autoxref -lang c latex.tw >latex.c + +weevinfo.h: tango weevinfo.tw + ./tango -autoxref -lang c weevinfo.tw >weevinfo.h + +weevdoc.dvi: $(WEEVIL_DOCS) + latex weevdoc + +weevinfo.tex: weevil weevinfo.tw + ./weevil -lang c weevinfo.tw >weevinfo.tex + +weevil.tex: weevil weevil.tw weevilref.xfr + ./weevil -lang c weevil.tw >weevil.tex + +weevilref.xfr: c_weevil.xfr latex.xfr + cat c_weevil.xfr latex.xfr >weevilref.xfr + +c_weevil.tex: weevil c_weevil.tw + ./weevil -lang c c_weevil.tw >c_weevil.tex + +latex.tex: weevil latex.tw + ./weevil -lang c latex.tw >latex.tex + +tango: $(TANGO_OBJS) + $(CC) $(CFLAGS) -o tango $(TANGO_OBJS) + +tango.o: tango.c tango.h + +c_tango.o: c_tango.c + +co: + co tango.c weevil.tw tw.sty + +doc: weevdoc.dvi userguid.dvi + +userguid.dvi: userguid.tex + latex userguid.tex + latex userguid.tex + +userguid.tex: userguid.tpc + gpic -t userguid.tpc >userguid.tex + +clean: + rm -f $(TANGO_OBJS) $(WEEVIL_OBJS) + rm -f weevil tango + rm -f weevinfo.xfr weevinfo.h weevinfo.tex weevinfo.aux + rm -f weevil.xfr weevil.c weevil.tex weevil.aux weevilref.xfr + rm -f c_weevil.xfr c_weevil.c c_weevil.tex c_weevil.aux + rm -f latex.xfr latex.c latex.tex latex.aux + rm -f hello.xfr hello.idx hello.ilg hello.ind hello.tex hello.aux + rm -f hello.log hello.dvi hello hello.c + rm -f weevdoc.idx weevdoc.ilg weevdoc.ind weevdoc.aux weevdoc.log + rm -f weevdoc.dvi weevdoc.toc + rm -f userguid.idx userguid.ilg userguid.ind userguid.aux + rm -f userguid.log userguid.dvi userguid.tex diff --git a/web/tango-weevil/README b/web/tango-weevil/README new file mode 100644 index 0000000000..4d72bc00b6 --- /dev/null +++ b/web/tango-weevil/README @@ -0,0 +1,34 @@ +This is the tango/weevil literate programming tool. It is a rather +simple system for literate programming (much in the spirit of noweb) +but it has some features I have not seen in other tools: + + It is able to generate intra-file cross references. This + way, a code chunk can refer to code chunks in another + file without linking the files together. + + The tangler is used to generate the cross references. + This makes it easy, since the tangler knows where the + output code is going (making determination of variable + instances easier) and it knows what code chunks it came + from. + + It is written completely in ANSI-C; no esoteric languages + like icon are used so porting should be easy. Note that + this is ANSI C, if you are using old K&R you are working + in the dark. + +Tango/weevil will support multiple languages and output formats, +although only ANSI C and LaTeX2e are currently supported. If you +don't have LaTeX2e, get it, it is much nicer to use than plain LaTeX. +Otherwise, you will have to write you own output routine for plain +LaTeX, since many LaTeX2e features are used. + +A start of a user's document is included. It is named userguid.tpc +and has gpic pictures in it. A file named userguid.tex is also +included, it has the pictures already converted to tpic specials. + +This software has been tested for a while and seems stable. Still, +it might not work for you. No guarantees. + +Corey Minyard +minyard@metronet.com diff --git a/web/tango-weevil/c_tango.c b/web/tango-weevil/c_tango.c new file mode 100644 index 0000000000..78d11de8d7 --- /dev/null +++ b/web/tango-weevil/c_tango.c @@ -0,0 +1,798 @@ +/* + Tango/Weevil - A WEB Tangler and Weaver + Copyright (C) 1995 Corey Minyard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Corey Minyard - minyard@metronet.com +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "tango.h" + +#define GLOBAL 1 +#define PROC_DEFN 2 +#define INPROC 3 +#define PROC_CAN_DEF 4 + +#define DEFINE 3 +#define VARDEF 4 +#define FOUND_OPER 5 + +#define OPER 6 +#define NAME 7 +#define NONE 8 +#define PERIOD 9 +#define RESERVED_WORD 10 +#define SEMI 11 +#define NUMSIGN 12 +#define STRUCT 13 + +typedef struct +{ + int code_state; + int last_token; + int nest_level; + int sqbrace_nest; + int paren_level; + bool name_seen; + bool is_static; + bool in_define; + bool in_number; + bool define_name_found; + bool lastescape; + char quotechar; + bool in_struct; + bool in_enum; + bool in_enum_define; + bool in_extern_c; + int struct_nest; + bool comment_after_star; + t_namelist *local_vars; + t_namelist *last_name; + int rword; + int extern_c_nest; +} t_codeinfo_c; + +#define WHILE_RW 1 +#define DO_RW 2 +#define FOR_RW 3 +#define IF_RW 4 +#define SWITCH_RW 5 +#define CASE_RW 6 +#define DEFAULT_RW 7 +#define GOTO_RW 8 +#define CONTINUE_RW 9 +#define BREAK_RW 10 +#define RETURN_RW 11 +#define AUTO_RW 12 +#define REGISTER_RW 13 +#define STATIC_RW 14 +#define EXTERN_RW 15 +#define TYPEDEF_RW 16 +#define CONST_RW 17 +#define VOLATILE_RW 18 +#define STRUCT_RW 19 +#define UNION_RW 20 +#define SIZEOF_RW 21 +#define ELSE_RW 22 +#define ENUM_RW 23 + +static struct rword_s +{ + char *name; + int val; + int length; +} rwords[] = +{ + { "while", WHILE_RW, 5 }, + { "do", DO_RW, 2 }, + { "for", FOR_RW, 3 }, + { "if", IF_RW, 2 }, + { "switch", SWITCH_RW, 6 }, + { "case", CASE_RW, 4 }, + { "default", DEFAULT_RW, 7 }, + { "goto", GOTO_RW, 4 }, + { "continue", CONTINUE_RW, 8 }, + { "break", BREAK_RW, 5 }, + { "return", RETURN_RW, 6 }, + { "auto", AUTO_RW, 4 }, + { "register", REGISTER_RW, 8 }, + { "static", STATIC_RW, 6 }, + { "extern", EXTERN_RW, 6 }, + { "typedef", TYPEDEF_RW, 7 }, + { "const", CONST_RW, 5 }, + { "volatile", VOLATILE_RW, 8 }, + { "struct", STRUCT_RW, 6 }, + { "union", UNION_RW, 5 }, + { "sizeof", SIZEOF_RW, 6 }, + { "else", ELSE_RW, 4 }, + { "enum", ENUM_RW, 4 } +}; +static const int num_rw = (sizeof(rwords) / sizeof(struct rword_s)); + +static int +is_reserved_word(char *name, + int length) +{ + int i; + + for (i=0; i<num_rw; i++) + { + if ( (length == rwords[i].length) + && (strncmp(name, rwords[i].name, length) == 0)) + { + return(rwords[i].val); + } + } + + return(-1); +} + +static char *(intrinsic_types[]) = +{ + "int", "void", "char", "long", "unsigned", "float", "double", + "short", NULL +}; + +static bool +is_intrinsic_type(char *name) +{ + int i; + + for (i=0; intrinsic_types[i] != NULL; i++) + { + if (strcmp(name, intrinsic_types[i]) == 0) + { + break; + } + } + + return(intrinsic_types[i] != NULL); +} + +static void +add_name_define(t_lptangodat *lptd, + t_namelist *item, + t_codeinfo_c *ci) +{ + if (! (lptd->auto_xref)) + { + free_namelist_item(lptd, item); + } + else if (ci->in_define) + { + list_insert_unique(lptd, &(lptd->curr_macro->pounddefs), item); + } + else if (! ci->in_struct) + { + switch(ci->code_state) + { + case GLOBAL: + if (ci->is_static) + { + list_insert_unique(lptd, &(lptd->curr_macro->staticdefs), item); + } + else + { + list_insert_unique(lptd, &(lptd->curr_macro->globaldefs), item); + } + break; + + case PROC_DEFN: + case PROC_CAN_DEF: + list_insert_unique(lptd, &(ci->local_vars), item); + item->u.nest_val = ci->nest_level; + break; + + default: + free_namelist_item(lptd, item); + } + } + else + { + free_namelist_item(lptd, item); + } +} + +static void +add_name_use(t_lptangodat *lptd, + t_namelist *item, + t_codeinfo_c *ci) +{ + if (! (lptd->auto_xref)) + { + free_namelist_item(lptd, item); + } + else if (find_name_in_list(lptd, ci->local_vars, item) != NULL) + { + /* Don't track uses of local variables. */ + free_namelist_item(lptd, item); + } + else if (!is_intrinsic_type(item->name)) + { + list_insert_unique(lptd, &(lptd->curr_macro->uses), item); + } + else + { + free_namelist_item(lptd, item); + } +} + +static void +destroy_locals(t_lptangodat *lptd, + t_codeinfo_c *ci) +{ + t_namelist *hold; + + + while ( (ci->local_vars != NULL) + && (ci->local_vars->u.nest_val >= ci->nest_level)) + { + hold = ci->local_vars; + ci->local_vars = hold->next; + free_namelist_item(lptd, hold); + } +} + +void +c_scan_input(t_lptangodat *lptd, + char *line, + int length, + int lineno) +{ + char *cp; + char *start_tok; + int count; + t_namelist *defname; + t_codeinfo_c *ci; + + ci = lptd->code_info; + count = 0; + cp = line; + start_tok = NULL; + while (count < length) + { + if (lptd->instring) + { + while (TRUE) + { + if ( (*cp == ci->quotechar) + && (! ci->lastescape)) + { + lptd->instring = FALSE; + break; + } + + if (count >= length) + { + break; + } + if ( (*cp == '\\') + && (! ci->lastescape)) + { + ci->lastescape = TRUE; + } + else + { + ci->lastescape = FALSE; + } + cp++; + count++; + } + if ( (ci->last_token == RESERVED_WORD) + && (ci->rword == EXTERN_RW) + && (count > 2) + && ((*(cp-1) == 'C') && (*(cp-2) == '"'))) + { + ci->in_extern_c = TRUE; + ci->extern_c_nest = ci->nest_level; + } + ci->last_token = NAME; + } + else if (lptd->in_comment) + { + while (TRUE) + { + if (count >= length) + { + break; + } + else + { + if ( (ci->comment_after_star) + && (*cp == '/')) + { + lptd->in_comment = FALSE; + break; + } + + if (*cp == '*') + { + ci->comment_after_star = TRUE; + } + else + { + ci->comment_after_star = FALSE; + } + } + + cp++; + count++; + } + } + else if ( ((*cp >= 'a') && (*cp <= 'z')) + || ((*cp >= 'A') && (*cp <= 'Z')) + || ((start_tok != NULL) && ((*cp >= '0') && (*cp <= '9'))) + || (*cp == '_')) + { + if ((start_tok == NULL) && (!ci->in_number)) + { + start_tok = cp; + } + } + else if ( ((*cp >= '0') && (*cp <= '9')) + || ( ci->in_number + && ( (*cp == '.') + || (*cp == '+') + || (*cp == '-')))) + { + ci->in_number = TRUE; + } + else + { + ci->in_number = FALSE; + + if (ci->in_define) + { + if ( (*cp == '/') + && ((count+1) < length) + && (*(cp+1) == '*')) + { + cp++; + count++; + lptd->in_comment = TRUE; + } + else if (ci->last_token == NUMSIGN) + { + ci->last_token = NONE; + + /* got a # directive. We are only really + interested in defines */ + if ( (start_tok != NULL) + && ((cp - start_tok) == 6) + && (strncmp(start_tok, "define", 6) == 0)) + { + ci->define_name_found = FALSE; + } + else + { + ci->define_name_found = TRUE; + } + } + else if ( (start_tok != NULL) + && (! ci->define_name_found)) + { + defname = create_namelist_item(lptd, + start_tok, + cp - start_tok); + add_name_define(lptd, defname, ci); + ci->define_name_found = TRUE; + } + else if ( (*cp == '\\') + && ((count+1) < length)) + { + cp++; + count++; + } + else if (*cp == '\n') + { + ci->in_define = FALSE; + } + start_tok = NULL; + } + else + { + if ( (start_tok != NULL) /* We just finished with a name */ + && (ci->last_token != PERIOD)) /* Ignore struct elements */ + { + ci->rword = is_reserved_word(start_tok, cp - start_tok); + if (ci->rword != -1) + { + ci->last_token = RESERVED_WORD; + + /* If static, save that for later. */ + if (ci->rword == STATIC_RW) + { + ci->is_static = TRUE; + } + if ( ( (ci->rword == STRUCT_RW) + || (ci->rword == UNION_RW)) + && (! ci->in_struct)) + { + ci->in_struct = TRUE; + ci->struct_nest = ci->nest_level; + ci->last_token = STRUCT; + } + else if (ci->rword == ENUM_RW) + { + ci->in_enum = TRUE; + } + /* no other reserved words really matter. */ + } + + /* The first name in a line is a name use, the rest + must be definitions unless then are in {}. Also, + a name right after struct or union is a define */ + else + { + if (ci->code_state == GLOBAL) + { + /* The nesting level must be 0 or at the same + level as an extern "C", a name see, and not + in square braces. */ + if ( ( (ci->nest_level == 0) + || ( (ci->in_extern_c) + && (ci->nest_level <= (ci->extern_c_nest+1))) + || (ci->in_struct)) + && (ci->name_seen) + && (ci->sqbrace_nest == 0)) + { + /* This is a variable or type declaration. */ + ci->last_name = create_namelist_item(lptd, + start_tok, + cp - start_tok); + add_name_define(lptd, ci->last_name, ci); + } + /* Everything in an enum is a definition */ + else if (ci->in_enum_define) + { + /* This is a variable or type declaration. */ + ci->last_name = create_namelist_item(lptd, + start_tok, + cp - start_tok); + add_name_define(lptd, ci->last_name, ci); + } + else + { + /* This is a variable or type use. */ + + ci->name_seen = TRUE; + ci->last_name = create_namelist_item(lptd, + start_tok, + cp - start_tok); + add_name_use(lptd, ci->last_name, ci); + } + } + else + { + /* Handle a name in a procedure or parameter list. */ + if ( ( (ci->code_state == PROC_CAN_DEF) + || (ci->code_state == PROC_DEFN)) + && (ci->name_seen) + && (ci->sqbrace_nest == 0)) + { + /* This is a variable or type declaration. */ + ci->last_name = create_namelist_item(lptd, + start_tok, + cp - start_tok); + add_name_define(lptd, ci->last_name, ci); + } + else + { + /* This is a variable or type use. */ + + ci->name_seen = TRUE; + ci->last_name = create_namelist_item(lptd, + start_tok, + cp - start_tok); + add_name_use(lptd, ci->last_name, ci); + } + } + ci->last_token = NAME; + } + } + start_tok = NULL; + + switch(*cp) + { + case ' ': case '\t': + break; + + case ';': + if ( ( (ci->nest_level == 0) + || ( (ci->in_extern_c) + && (ci->nest_level <= (ci->extern_c_nest+1)))) + && (ci->code_state == INPROC)) + { + destroy_locals(lptd, ci); + ci->code_state = GLOBAL; + } + if ( (ci->in_struct) + && (ci->nest_level <= ci->struct_nest)) + { + ci->in_struct = FALSE; + } + + ci->last_name = NULL; + ci->is_static = FALSE; + ci->name_seen = FALSE; + + ci->last_token = SEMI; + break; + + case ',': + if (ci->code_state == PROC_DEFN) + { + /* In a list of procedure parameters, make commas + look like semicolons. */ + ci->last_name = NULL; + ci->last_token = SEMI; + ci->is_static = FALSE; + ci->name_seen = FALSE; + } + else if (ci->last_token != NAME) + { + /* Probably passing parameters to a procedure call, + just ignore. */ + } + break; + + case ')': + ci->paren_level--; + if ( (ci->paren_level == 0) + && (ci->code_state == PROC_DEFN)) + { + ci->code_state = INPROC; + } + break; + + case '=': + if (ci->code_state == PROC_CAN_DEF) + { + /* We've seen an assign, we are no longer + in definition mode. */ + ci->code_state = INPROC; + + if ( (ci->last_name != NULL) + && (ci->last_name == ci->local_vars)) + { + /* I thought the last name was a define, + it is acually a use. */ + ci->local_vars = ci->last_name->next; + + add_name_use(lptd, ci->last_name, ci); + } + } + + ci->last_token = OPER; + break; + + case '(': + if ( (ci->code_state == GLOBAL) + && (ci->last_token == NAME)) + { + ci->code_state = PROC_DEFN; + ci->paren_level = 1; + ci->last_name = NULL; + ci->name_seen = FALSE; + } + else + { + ci->paren_level++; + if ( (ci->last_token == NAME) + && (ci->code_state == PROC_CAN_DEF) + && (ci->last_name != NULL) + && (ci->last_name == ci->local_vars)) + { + /* I thought the last name was a define, + it is acually a use. */ + ci->local_vars = ci->last_name->next; + + add_name_use(lptd, ci->last_name, ci); + } + } + break; + + case '.': + ci->last_token = PERIOD; + break; + + case '-': + if ( ((count+1) < length) + && (*(cp+1) == '>')) + { + cp++; + count++; + /* Even though '->' is not a period, it works + about the same for our purposes. */ + ci->last_token = PERIOD; + } + else + { + ci->last_token = OPER; + } + break; + + case '{': + if (ci->code_state == INPROC) + { + ci->code_state = PROC_CAN_DEF; /* Can define local + vars now. */ + } + else if ( (ci->code_state == GLOBAL) + && (ci->last_name != NULL) + && (lptd->curr_macro->uses != NULL) + && (lptd->curr_macro->uses == ci->last_name) + && ( ( (ci->in_struct) + && (ci->struct_nest == ci->nest_level)) + || (ci->in_enum))) + { + /* I thought the last item was a use, it was actually + a structure or enum define. */ + lptd->curr_macro->uses = ci->last_name->next; + + if (ci->in_struct) + { + ci->in_struct = FALSE; + add_name_define(lptd, ci->last_name, ci); + ci->in_struct = TRUE; + } + else + { + ci->in_enum_define = TRUE; + add_name_define(lptd, ci->last_name, ci); + } + } + ci->nest_level++; + ci->last_name = NULL; + ci->name_seen = FALSE; + + ci->last_token = NONE; + break; + + case '}': + /* Make sure to get rid of anything declared in this block */ + destroy_locals(lptd, ci); + ci->nest_level--; + + ci->in_enum = FALSE; /* an rbrace will alway finish an enum */ + ci->in_enum_define = FALSE; + + /* check to see if this is the end of an extern "C" { } */ + if ((ci->in_extern_c) && (ci->extern_c_nest == ci->nest_level)) + { + ci->in_extern_c = FALSE; + } + + /* An end brace can be like a name, such as at the end of + a structure definition. */ + ci->last_token = NAME; + ci->name_seen = TRUE; + + if ( (ci->in_struct) + && (ci->struct_nest >= ci->nest_level)) + { + ci->in_struct = FALSE; + } + + if ( ( (ci->nest_level == 0) + || ( (ci->in_extern_c) + && (ci->nest_level <= (ci->extern_c_nest+1)))) + && ( (ci->code_state == INPROC) + || (ci->code_state == PROC_CAN_DEF))) + { + ci->code_state = GLOBAL; + ci->name_seen = FALSE; + ci->last_token = NONE; + } + else if (ci->code_state == PROC_CAN_DEF) + { + ci->code_state = INPROC; + } + break; + + case '"': + case '\'': + ci->quotechar = *cp; + lptd->instring = TRUE; + ci->lastescape = FALSE; + break; + + case '[': + (ci->sqbrace_nest)++; + break; + + case ']': + (ci->sqbrace_nest)--; + break; + + case '#': + ci->in_define = TRUE; + ci->last_token = NUMSIGN; + break; + + case '/': + if ( ((count+1) < length) + && (*(cp+1) == '*')) + { + cp++; + count++; + lptd->in_comment = TRUE; + ci->comment_after_star = FALSE; + } + else + { + ci->last_token = OPER; + } + break; + + default: + ci->last_token = OPER; + break; + } + } + } + + cp++; + count++; + } +} + +void +c_output_linenum(t_lptangodat *lptd, + int lineno, + char *filename) +{ + fprintf(lptd->outfile, "#line %d \"%s\"\n", lineno, filename); +} + +void +init_c_lang(t_lptangodat *lptd) +{ + t_codeinfo_c *ci; + + lptd->code_info = malloc(sizeof(t_codeinfo_c)); + if (lptd->code_info == NULL) + { + fprintf(stderr, "Unable to allocate enough memory\n"); + exit(1); + } + + ci = lptd->code_info; + ci->code_state = GLOBAL; + ci->last_token = NONE; + ci->nest_level = 0; + ci->sqbrace_nest = 0; + ci->paren_level = 0; + ci->name_seen = FALSE; + ci->is_static = FALSE; + ci->in_define = FALSE; + ci->in_number = FALSE; + ci->define_name_found = FALSE; + ci->lastescape = FALSE; + ci->comment_after_star = FALSE; + ci->quotechar = '\0'; + ci->local_vars = NULL; + ci->last_name = NULL; + ci->in_struct = FALSE; + ci->in_enum = FALSE; + ci->in_enum_define = FALSE; + ci->in_extern_c = FALSE; + ci->struct_nest = 0; +} diff --git a/web/tango-weevil/c_weevil.tw b/web/tango-weevil/c_weevil.tw new file mode 100644 index 0000000000..a856fdef5a --- /dev/null +++ b/web/tango-weevil/c_weevil.tw @@ -0,0 +1,173 @@ +% +% Tango/Weevil - A WEB Tangler and Weaver +% Copyright (C) 1995 Corey Minyard +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +% +% Corey Minyard - minyard@metronet.com +% +@includefile weevinfo.xfr + +\section{C Language Handler Intro} +This module holds the routines used to process ``C'' code for weevil. + +@code <*> +@<Includes> +@<Structures> +@<c_handle_char> +@<init_c_lang> +@endcode + +\section{Includes} + +@code <Includes> +#include <stdio.h> +#include <stdlib.h> +#include "weevinfo.h" +@endcode + +\section{Structures} + +@code <Structures> +typedef struct +{ + bool in_comment; + int quote_char; +} t_c_lang; +@endcode + +\section{c\_handler\_char} + +\begin{twproc}{c\_handle\_char} +\Description Handles a character for the ``C'' language. It will + scan the input and return the proper code depending + on if the code is currently in a string or comment. +\ReturnValues \verb|int| - May be one of the following: + \begin{twparmlist} + \item[PROCESS\_CHAR\_NORMALLY] This is a standard + character that + should be handled normally by weevil. + \item[PROCESS\_CHAR\_INSTRING] This character is in + a string or + comment, it should not be scanned for special + weevil characters. + \item[PROCESS\_CHAR\_SKIPNEXT] This character and the + following character should be skipped without + being scanned for special weevil characters. + \end{twparmlist} +\Inputs \begin{twparmlist} + \item[lpwd] Weevil information. + \item[c] The current input character. + \item[next\_c] The next input character. + \end{twparmlist} +\StartCode +@code <c_handle_char> +int +c_handle_char(t_lpweevildat *lpwd, + char c, + char next_c) +{ + int retval; + t_c_lang *c_info; + + + c_info = lpwd->lang_data; + if (c_info->in_comment) + { + if ((c == '*') && (next_c == '/')) + { + c_info->in_comment = FALSE; + retval = PROCESS_CHAR_SKIPNEXT; + } + else + { + retval = PROCESS_CHAR_INSTRING; + } + } + else if (c_info->quote_char != -1) + { + if (c == c_info->quote_char) + { + c_info->quote_char = -1; + retval = PROCESS_CHAR_INSTRING; + } + else if (c == '\\') + { + retval = PROCESS_CHAR_SKIPNEXT; + } + else + { + retval = PROCESS_CHAR_INSTRING; + } + } + else + { + retval = PROCESS_CHAR_NORMALLY; + switch(c) + { + case '\'': + case '"': + c_info->quote_char = c; + retval = PROCESS_CHAR_INSTRING; + break; + + case '/': + if (next_c == '*') + { + c_info->in_comment = TRUE; + retval = PROCESS_CHAR_INSTRING; + break; + } + break; + + default: + /* Nothing to do. */ + break; + } + } + + return(retval); +} +@endcode +\end{twproc} + +\section{init\_c\_lang} + +\begin{twproc}{init\_c\_lang} +\Description Initializes the ``C'' language handler. It allocates + some memory and initializes it. +\Inputs \begin{twparmlist} + \item[lpwd] Weevil information. + \end{twparmlist} +\StartCode +@code <init_c_lang> +void +init_c_lang(t_lpweevildat *lpwd) +{ + t_c_lang *c_info; + + c_info = malloc(sizeof(*c_info)); + if (c_info == NULL) + { + fprintf(stderr, "Unable to allocate enough memory\n"); + exit(1); + } + + c_info->quote_char = -1; + c_info->in_comment = FALSE; + lpwd->lang_data = c_info; +} +@endcode +\end{twproc} diff --git a/web/tango-weevil/hello.tw b/web/tango-weevil/hello.tw new file mode 100644 index 0000000000..b43f32f45d --- /dev/null +++ b/web/tango-weevil/hello.tw @@ -0,0 +1,69 @@ +\documentclass{article} +\usepackage{tw,makeidx} +\title{Hello World --- A Famous Program} +\author{Corey Minyard} +\date{\today} +\makeindex +\begin{document} +\maketitle +\section{Description} + +This program is the famous ``hello world'' program. Certainly we have +all written one of these programs. + +\section{Code} + +\subsection{Start Macro} +This is the main part of the program. +@code <*> +@<Includes> +@<Routines> +@endcode + +\subsection{Includes} +This program needs \texttt{stdio.h} because it uses printf. +@code <Includes> +#include <stdio.h> +@endcode + +\subsection{Routines} +This program only has one routine, the main routine. + +@code <Routines> +@<main> +@endcode + +\begin{twproc}{main} +\Description The main routine. +\ReturnValues \verb|int| - Program return code +\SideEffects Well, it prints something! +\Inputs \begin{twparmlist} + \item[argc] Argument count + \item[argv] The arguments + \end{twparmlist} +\StartCode +@code <main> +int +main(int argc, + char *argv) +{ + @<Output Hello> + @<Leave Program> +} +@endcode + +\subsection{Output Hello} +This macro actually does the output. +@code <Output Hello> + printf("hello world\n"); +@endcode + +\subsection{Leave Program} +This macro causes the program to exit. +@code <Leave Program> + exit(0); +@endcode +\end{twproc} + +\printindex +\end{document} diff --git a/web/tango-weevil/latex.tw b/web/tango-weevil/latex.tw new file mode 100644 index 0000000000..33d0352c24 --- /dev/null +++ b/web/tango-weevil/latex.tw @@ -0,0 +1,304 @@ +% +% Tango/Weevil - A WEB Tangler and Weaver +% Copyright (C) 1995 Corey Minyard +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +% +% Corey Minyard - minyard@metronet.com +% +@includefile weevinfo.xfr + +\section{\LaTeX\ Output Handler Intro} +Due to the large number of functions of such a trivial nature, this +routine is not currently commented very well. The formatting given +depends a lot on the tw.sty file that holds the \LaTeX\ macros used +by this file. + +Note that in labels, the names are stripped of everything but periods +and asterisks, so names must be unique to that level. + +@code <*> +#include <stdio.h> +#include <stdlib.h> +#include "weevinfo.h" + +@<t_latex_data> +@<output_name> +@<strip_name> +@<Init> +@<Macro Routines> +@<Output Character> +@<Uses Routines> +@<Declare Routines> +@endcode + +@code <t_latex_data> +typedef struct s_latex_data +{ + int column; +} t_latex_data; +@endcode + +@code <output_name> +static void +output_name(t_lpweevildat *lpwd, + char *name) +{ + while (*name != '\0') + { + if ( ((*name >= 'a') && (*name <= 'z')) + || ((*name >= 'A') && (*name <= 'Z')) + || ((*name >= '0') && (*name <= '9')) + || ((*name == ' '))) + { + fputc(*name, lpwd->outfile); + } + else if (*name == '_') + { + fputs("\\_", lpwd->outfile); + } + else + { + fprintf(lpwd->outfile, "\\char%d ", *name); + } + + name++; + } +} +@endcode + +@code <strip_name> +static void +strip_name(t_lpweevildat *lpwd, + char *name) +{ + while (*name != '\0') + { + if ( ((*name >= 'a') && (*name <= 'z')) + || ((*name >= 'A') && (*name <= 'Z')) + || ((*name >= '0') && (*name <= '9'))) + { + fputc(*name, lpwd->outfile); + } + else + { + switch(*name) + { + case '.': + case '*': + fputc(*name, lpwd->outfile); + break; + } + } + + name++; + } +} +@endcode + +@code <Init> +void +latex_mode_init(t_lpweevildat *lpwd) +{ + t_latex_data *ltd; + + ltd = malloc(sizeof(*ltd)); + if (ltd == NULL) + { + fprintf(stderr, "Unable to allocate memory\n"); + exit(1); + } + ltd->column = 1; + lpwd->mode_data = ltd; +} +@endcode + +@code <Macro Routines> +void +latex_begin_macro(t_lpweevildat *lpwd, + char *name) +{ + fprintf(lpwd->outfile, "\\begin{twcodechunk}\n"); + fprintf(lpwd->outfile, "\\begingroup\\medskip\n"); + fprintf(lpwd->outfile, "\\twmacrodecl{"); + output_name(lpwd, name); + fprintf(lpwd->outfile, "}{"); + strip_name(lpwd, lpwd->curr_filename); + fprintf(lpwd->outfile, ":macro:"); + strip_name(lpwd, name); + fprintf(lpwd->outfile, "}\n"); + fprintf(lpwd->outfile, "\\begin{verbatimcmd}\\twstartcode\n"); +} + +void +latex_end_macro(t_lpweevildat *lpwd) +{ + fprintf(lpwd->outfile, "\\twendcode\\end{verbatimcmd}\n"); + fprintf(lpwd->outfile, "\\endgroup"); +} + +void +latex_finalend_chunk(t_lpweevildat *lpwd) +{ + fprintf(lpwd->outfile, "\\end{twcodechunk}\n"); +} + +void +latex_output_macro_use(t_lpweevildat *lpwd, + char *macro_name) +{ + fprintf(lpwd->outfile, "\\twmacrouse{"); + output_name(lpwd, macro_name); + fprintf(lpwd->outfile, "}{"); + strip_name(lpwd, lpwd->curr_filename); + fprintf(lpwd->outfile, ":macro:"); + strip_name(lpwd, macro_name); + fprintf(lpwd->outfile, "}"); +} + +void +latex_output_linenum(t_lpweevildat *lpwd) +{ + t_latex_data *ltd; + + ltd = lpwd->mode_data; + fprintf(lpwd->outfile, "%4d ", lpwd->code_lineno); + ltd->column = 1; +} + +void +latex_output_after_xrefs(t_lpweevildat *lpwd) +{ + fprintf(lpwd->outfile, "\\medskip\n"); +} +@endcode + +@code <Output Character> +void +latex_output_char(t_lpweevildat *lpwd, + char c) +{ + t_latex_data *ltd; + + ltd = lpwd->mode_data; + if ((c == '{') || (c == '}')) + { + fputc('\\', lpwd->outfile); + fputc(c, lpwd->outfile); + ltd->column++; + } + else if (c == '\\') + { + fputs("\\(\\backslash\\)", lpwd->outfile); + ltd->column++; + } + else if (c == '\t') + { + fputc(' ', lpwd->outfile); + ltd->column++; + while ((ltd->column % 8) != 1) + { + fputc(' ', lpwd->outfile); + ltd->column++; + } + } + else if (c == '\n') + { + fputc(c, lpwd->outfile); + ltd->column = 1; + } + else + { + fputc(c, lpwd->outfile); + ltd->column++; + } +} +@endcode + +@code <Uses Routines> +void +latex_output_begin_uses(t_lpweevildat *lpwd) +{ + fprintf(lpwd->outfile, "\\begin{twuses}\n"); +} + +void +latex_output_end_uses(t_lpweevildat *lpwd) +{ + fprintf(lpwd->outfile, "\\end{twuses}\n"); +} + +void +latex_output_use_start(t_lpweevildat *lpwd, + char *name) +{ + fprintf(lpwd->outfile, "\\twuse{"); + output_name(lpwd, name); + fprintf(lpwd->outfile, "} "); +} + +void +latex_output_use_end(t_lpweevildat *lpwd, + char *name) +{ + fprintf(lpwd->outfile, "\\\\\n"); +} + +void +latex_output_use_ref(t_lpweevildat *lpwd, + char *file, + char *macro, + char *name) +{ + fputc('(', lpwd->outfile); + if (file != NULL) + { + output_name(lpwd, file); + fputc(':', lpwd->outfile); + } + output_name(lpwd, macro); + fputc(')', lpwd->outfile); +} + +void +latex_output_between_use_refs(t_lpweevildat *lpwd, + char *name) +{ + fputc(' ', lpwd->outfile); +} +@endcode + +@code <Declare Routines> +void +latex_output_begin_decls(t_lpweevildat *lpwd) +{ + fprintf(lpwd->outfile, "\\begin{twdecls}\n"); +} + +void +latex_output_end_decls(t_lpweevildat *lpwd) +{ + fprintf(lpwd->outfile, "\\end{twdecls}\n"); +} + +void +latex_output_decl(t_lpweevildat *lpwd, + char *name) +{ + fprintf(lpwd->outfile, "\\twdecl{"); + output_name(lpwd, name); + fprintf(lpwd->outfile, "}\\\\\n"); +} +@endcode diff --git a/web/tango-weevil/tango.c b/web/tango-weevil/tango.c new file mode 100644 index 0000000000..0ef355e22a --- /dev/null +++ b/web/tango-weevil/tango.c @@ -0,0 +1,1017 @@ +/* + Tango/Weevil - A WEB Tangler and Weaver + Copyright (C) 1995 Corey Minyard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Corey Minyard - minyard@metronet.com +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + + +#define SKIP_DOCUMENTATION 1 +#define OUTPUT_CODE 2 + +#define GOT_LINE 1 +#define END_OF_FILE 0 + +#define MAX_FILENAME_LENGTH 200 + +#include "tango.h" + +char * +r_strtok(char *data, + char *sstr, + char **next_data) +{ + char *retval; + + + while ( (*data != '\0') + && (strchr(sstr, *data) != NULL)) + { + data++; + } + + retval = data; + while ( (*data != '\0') + && (strchr(sstr, *data) == NULL)) + { + data++; + } + + if (retval == data) + { + retval = NULL; + } + else + { + if (*data != '\0') + { + *data = '\0'; + data++; + } + + if (next_data != NULL) + { + *next_data = data; + } + } + + return(retval); +} + +char * +stralloc(char *str, + int length) +{ + char *retval; + + if (length == 0) + { + length = strlen(str); + } + + retval = malloc(length+1); + if (retval == NULL) + { + fprintf(stderr, + "Fatal: Unable to allocate memory\n"); + exit(1); + } + + strncpy(retval, str, length); + retval[length] = '\0'; + return(retval); +} + +void +free_namelist_item(t_lptangodat *lptd, + t_namelist *item) +{ + if (item->name != NULL) + { + free(item->name); + } + if (item != NULL) + { + free(item); + } +} + +t_namelist * +create_namelist_item(t_lptangodat *lptd, + char *str, + int length) +{ + t_namelist *retval; + + retval = malloc(sizeof(*retval)); + if (retval == NULL) + { + fprintf(stderr, + "Fatal: Unable to allocate memory\n"); + exit(1); + } + + retval->name = stralloc(str, length); + + return(retval); +} + +t_namelist * +find_name_in_list(t_lptangodat *lptd, + t_namelist *list, + t_namelist *item) +{ + while (list != NULL) + { + if (strcmp(list->name, item->name) == 0) + { + break; + } + + list = list->next; + } + + return(list); +} + +void +list_insert_unique(t_lptangodat *lptd, + t_namelist **list, + t_namelist *item) +{ + if (find_name_in_list(lptd, *list, item) == NULL) + { + item->next = *list; + *list = item; + } + else + { + free_namelist_item(lptd, item); + } +} + +/****************************************************************************/ + +static int +get_input_line(t_lptangodat *lptd) +{ +#define FLUSHSIZE 20 + int retval; + int length; + char flushbuf[FLUSHSIZE]; + int flushlength; + + + (lptd->curr_lineno)++; + + if (fgets(lptd->line, lptd->maxlinesize, lptd->infile) == NULL) + { + retval = END_OF_FILE; /* Did not get a line, end of file. */ + } + else + { + retval = GOT_LINE; + length = strlen(lptd->line); + + /* If the line was too long (it was the max size and the last character + was not a newline), print a log and flush to end of line. */ + if ( (length == (lptd->maxlinesize - 1)) + && (lptd->line[length-1] != '\n')) + { + lptd->line[length-1] = '\n'; + + fprintf(stderr, + "Warning, line %d too long, flushing to end of line\n", + lptd->curr_lineno); + if (fgets(flushbuf, FLUSHSIZE, lptd->infile) == NULL) + { + flushlength = 0; + } + else + { + flushlength = strlen(flushbuf); + } + while ( (flushlength == (FLUSHSIZE - 1)) + && (lptd->line[flushlength-1] != '\n')) + { + if (fgets(flushbuf, FLUSHSIZE, lptd->infile) == NULL) + { + flushlength = 0; + } + else + { + flushlength = strlen(flushbuf); + } + } + } + } + + return(retval); +} + +static void +process_cmd(t_lptangodat *lptd) +{ + char *strhold; + char *cmd; + char *name; + int namelength; + + + cmd = r_strtok(&(lptd->line[1]), " \t\n", &strhold); + if (cmd == NULL) + { + fprintf(stderr, + "Error on line %d: Missing command\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else if (strcmp(cmd, "code") == 0) + { + if (lptd->outstate == OUTPUT_CODE) + { + fprintf(stderr, + "Error on line %d: Macro begin when already processing a macro\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + lptd->curr_macro = malloc(sizeof(*(lptd->curr_macro))); + if (lptd->curr_macro == NULL) + { + fprintf(stderr, + "Fatal: Unable to allocate memory\n"); + exit(1); + } + + while ((*strhold == ' ') || (*strhold == '\t') || (*strhold == '\n')) + { + strhold++; + } + + if (*strhold != '<') + { + fprintf(stderr, + "Error on line %d: Macro name must be surrounded by <>\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + strhold++; + } + + name = r_strtok(strhold, ">", &strhold); + lptd->curr_macro->name = stralloc(name, 0); + lptd->curr_macro->filename = lptd->curr_filename; + lptd->curr_macro->startline = lptd->curr_lineno + 1; + lptd->curr_macro->firstline = NULL; + lptd->curr_macro->lastline = NULL; + lptd->curr_macro->staticdefs = NULL; + lptd->curr_macro->globaldefs = NULL; + lptd->curr_macro->pounddefs = NULL; + lptd->curr_macro->uses = NULL; + + lptd->outstate = OUTPUT_CODE; + } + } + else if (strcmp(cmd, "file") == 0) + { + if (lptd->outstate == OUTPUT_CODE) + { + fprintf(stderr, + "Error on line %d: File name change not allowed in code\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + name = r_strtok(strhold, " \t\n", &strhold); + if (name == NULL) + { + fprintf(stderr, + "Error on line %d: File name not given with file command\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + lptd->curr_filename = stralloc(name, 0); + namelength = strlen(name); + + name = r_strtok(strhold, " \t\n", &strhold); + if (name == NULL) + { + lptd->curr_lineno = 0; + } + else + { + lptd->curr_lineno = atoi(name) - 1; + } + } + } + else if (strcmp(cmd, "line") == 0) + { + if (lptd->outstate == OUTPUT_CODE) + { + fprintf(stderr, + "Error on line %d: Line number change not allowed in code\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + name = r_strtok(strhold, " \t\n", &strhold); + if (name == NULL) + { + fprintf(stderr, + "Error on line %d: Line number not specified in line command\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + lptd->curr_lineno = atoi(name) - 1; + } + } + } + else if ( (strcmp(cmd, "includefile") == 0) + || (strcmp(cmd, "reffile") == 0)) + { + if (lptd->outstate == OUTPUT_CODE) + { + fprintf(stderr, + "Error on line %d: %s not allowed in code\n", + lptd->curr_lineno, + cmd); + lptd->retcode = 2; + } + /* This is ignored in tangling. */ + } + else + { + fprintf(stderr, + "Error on line %d: Invalid command\n", + lptd->curr_lineno); + lptd->retcode = 2; + } +} + +static void +process_codeline(t_lptangodat *lptd) +{ + t_linelist *thisline; + + thisline = malloc(sizeof(*thisline)); + if (thisline == NULL) + { + fprintf(stderr, + "Fatal: Unable to allocate memory\n"); + exit(1); + } + + thisline->line = stralloc(lptd->line, 0); + thisline->next = NULL; + if (lptd->curr_macro->lastline == NULL) + { + lptd->curr_macro->lastline = thisline; + lptd->curr_macro->firstline = thisline; + } + else + { + lptd->curr_macro->lastline->next = thisline; + lptd->curr_macro->lastline = thisline; + } +} + +static void output_macro(t_lptangodat *lptd, + t_lpmac *outmac); + +static void +output_line(t_lptangodat *lptd, + t_linelist *outline, + char *filename, + int lineno) +{ + t_lpmac *cm; + t_lpmac *prev_macro; + char *cp; + int currchar; + char *name; + + + currchar = 0; + cp = outline->line; + + while (cp[currchar] != '\0') + { + if (cp[currchar] == '@') + { + if (currchar > 0) + { + fwrite(cp, 1, currchar, lptd->outfile); + lptd->scan_input(lptd, cp, currchar, lineno); + } + if ( (lptd->instring) + || (lptd->in_comment)) + { + fputc('@', lptd->outfile); + lptd->scan_input(lptd, "@", 1, lineno); + currchar++; + cp = &(cp[currchar]); + currchar = 0; + } + else + { + currchar++; + if (cp[currchar] == '@') + { + fputc('@', lptd->outfile); + lptd->scan_input(lptd, "@", 1, lineno); + currchar++; + cp = &(cp[currchar]); + currchar = 0; + } + else if (cp[currchar] == '<') + { + fputc('\n', lptd->outfile); + currchar++; + name = r_strtok(&(cp[currchar]), ">", &cp); + currchar = 0; + + cm = lptd->macros; + while ( (cm != NULL) + && (strcmp(cm->name, name) != 0)) + { + cm = cm->next; + } + + if (cm == NULL) + { + fprintf(stderr, + "Error: macro %s used on line %d but not found\n", + name, + lineno); + lptd->retcode = 2; + } + else + { + prev_macro = lptd->curr_macro; + lptd->curr_macro = cm; + output_macro(lptd, cm); + lptd->curr_macro = prev_macro; + if (lptd->do_linenums) + { + lptd->output_linenum(lptd, lineno, filename); + } + } + } + else + { + fprintf(stderr, + "Error on line %d: Invalid operation \"@%c\"\n", + lineno, + cp[currchar]); + lptd->retcode = 2; + } + } + } + else + { + currchar++; + } + } + + if (currchar > 0) + { + fwrite(cp, 1, currchar, lptd->outfile); + lptd->scan_input(lptd, cp, currchar, lineno); + } +} + +static void +output_macro(t_lptangodat *lptd, + t_lpmac *outmac) +{ + t_linelist *cl; + int lineno; + + + lineno = outmac->startline; + cl = outmac->firstline; + if (lptd->do_linenums) + { + lptd->output_linenum(lptd, lineno, outmac->filename); + } + while (cl != NULL) + { + lptd->curr_line = cl; + output_line(lptd, cl, outmac->filename, lineno); + lineno++; + cl = cl->next; + } +} + +static void +output_macros(t_lptangodat *lptd) +{ + t_lpmac *cm; + + cm = lptd->macros; + while ( (cm != NULL) + && (strcmp(cm->name, lptd->start_macro) != 0)) + { + cm = cm->next; + } + + if (cm == NULL) + { + fprintf(stderr, + "Fatal: Start macro %s not found\n", + lptd->start_macro); + exit(1); + } + + lptd->curr_macro = cm; + output_macro(lptd, cm); +} + +static char * +get_arg_str(int *argidx, + int argc, + char *argv[], + int start_offset) +{ + char *retval; + + + if (argv[*argidx][start_offset] == '\0') + { + (*argidx)++; + if ((*argidx) == argc) + { + retval = NULL; + } + else + { + retval = argv[*argidx]; + } + } + else + { + retval = &(argv[*argidx][start_offset]); + } + + return(retval); +} + +void c_scan_input(t_lptangodat *lptd, + char *line, + int length, + int lineno); + +void c_output_linenum(t_lptangodat *lptd, + int lineno, + char *filename); + +void init_c_lang(t_lptangodat *lptd); + +static struct s_langs +{ + char *lang_name; + input_scanner scanner; + linenum_output output_linenum; + scanner_init init; +} langs[] = +{ + { "c", &c_scan_input, &c_output_linenum, &init_c_lang } +}; + +static const num_langs = sizeof(langs) / sizeof(struct s_langs); + +static void +process_args(t_lptangodat *lptd, + int argc, + char *argv[], + int *argidx) +{ + char *str; + int i; + + + *argidx = 1; + while (argv[*argidx][0] == '-') + { + if (strcmp(argv[*argidx], "--") == 0) + { + (*argidx)++; + break; + } + else if (strncmp(argv[*argidx], "-autoxref", 5) == 0) + { + lptd->do_xref = TRUE; + lptd->auto_xref = TRUE; + } + else if (strncmp(argv[*argidx], "-xref", 5) == 0) + { + lptd->do_xref = TRUE; + } + else if (strncmp(argv[*argidx], "-lang", 5) == 0) + { + if (lptd->scan_input != NULL) + { + fprintf(stderr, + "%s: Error: only 1 -lang is allowed\n", + argv[0]); + exit(1); + } + + str = get_arg_str(argidx, argc, argv, 5); + if (str == NULL) + { + fprintf(stderr, + "%s: Error: -lang specified but no lang given\n", + argv[0]); + exit(1); + } + + for (i=0; i<num_langs; i++) + { + if (strcmp(str, langs[i].lang_name) == 0) + { + lptd->scan_input = langs[i].scanner; + lptd->output_linenum = langs[i].output_linenum; + langs[i].init(lptd); + break; + } + } + + if (lptd->scan_input == NULL) + { + fprintf(stderr, + "%s: Error: Invalid lang given: %s\n", + argv[0], str); + exit(1); + } + } + else if (strncmp(argv[*argidx], "-nolinenum", 10) == 0) + { + lptd->do_linenums = FALSE; + } + else if (strncmp(argv[*argidx], "-xreffile", 9) == 0) + { + lptd->do_xref = TRUE; + str = get_arg_str(argidx, argc, argv, 9); + if (str == NULL) + { + fprintf(stderr, + "%s: Error: -xreffile specified but no file given\n", + argv[0]); + exit(1); + } + + lptd->xreffile = fopen(str, "w"); + if (lptd->xreffile == NULL) + { + fprintf(stderr, + "%s: Error: Could not open cross ref file: %s\n", + argv[0], + str); + exit(1); + } + } + else if (strncmp(argv[*argidx], "-start_macro", 11) == 0) + { + lptd->start_macro = get_arg_str(argidx, argc, argv, 11); + if (lptd->start_macro == NULL) + { + fprintf(stderr, + "Error: -s specified but no start macro given\n"); + exit(1); + } + } + else + { + fprintf(stderr, + "Error: Invalid option specified: %s\n", argv[*argidx]); + exit(1); + } + + (*argidx)++; + } +} + +static void +print_list(t_lptangodat *lptd, + char *prefix, + t_namelist *names) +{ + while (names != NULL) + { + fprintf(lptd->xreffile, "%s %s\n", prefix, names->name); + names = names->next; + } +} + +static void +output_xref(t_lptangodat *lptd) +{ + t_lpmac *macros; + + fprintf(lptd->xreffile, "f %s\n", lptd->curr_filename); + macros = lptd->macros; + while (macros != NULL) + { + fprintf(lptd->xreffile, "m %s\n", macros->name); + print_list(lptd, "d", macros->pounddefs); + print_list(lptd, "e", macros->globaldefs); + print_list(lptd, "s", macros->staticdefs); + print_list(lptd, "u", macros->uses); + + macros = macros->next; + } +} + +int +main(int argc, + char *argv[]) +{ + t_lptangodat *lptd; + int argidx; + char *str; + char fname[MAX_FILENAME_LENGTH+1]; + int len; + char *name; + t_namelist *item; + + + lptd = malloc(sizeof(*lptd)); + if (lptd == NULL) + { + fprintf(stderr, "Unable to allocate enough memory\n"); + exit(1); + } + + lptd->scan_input = NULL; + + lptd->maxlinesize = MAXLINESIZE; + lptd->curr_lineno = 0; + lptd->macros = NULL; + lptd->start_macro = "*"; + lptd->outfile = stdout; + lptd->xreffile = NULL; + lptd->do_xref = FALSE; + lptd->auto_xref = FALSE; + lptd->in_comment = FALSE; + lptd->do_linenums = TRUE; + lptd->instring = FALSE; + lptd->retcode = 0; + + process_args(lptd, argc, argv, &argidx); + + if (lptd->scan_input == NULL) + { + fprintf(stderr, "%s: No lang specified\n", argv[0]); + exit(1); + } + + if (argc != (argidx + 1)) + { + fprintf(stderr, "%s: No input file specified\n", argv[0]); + exit(1); + } + else + { + lptd->infile = fopen(argv[argidx], "r"); + if (lptd->infile == NULL) + { + fprintf(stderr, "%s: Unable to open file %s\n", + argv[0], argv[argidx]); + exit(1); + } + lptd->curr_filename = argv[argidx]; + } + + if ((lptd->do_xref) && (lptd->xreffile == NULL)) + { + str = strrchr(argv[argidx], '.'); + if (str != NULL) + { + len = str - argv[argidx]; + } + else + { + len = strlen(argv[argidx]); + } + + if ((len+4) >= MAX_FILENAME_LENGTH) /* Add 4 for the .xfr */ + { + fprintf(stderr, "filename to long, no xref file generated\n"); + } + else + { + memcpy(fname, argv[argidx], len); + strcpy(&(fname[len]), ".xfr"); + + lptd->xreffile = fopen(fname, "w"); + if (lptd->xreffile == NULL) + { + fprintf(stderr, + "%s: Error: Could not open cross ref file: %s\n", + argv[0], + str); + exit(1); + } + } + } + + lptd->outstate = SKIP_DOCUMENTATION; + + while (get_input_line(lptd) == GOT_LINE) + { + if (lptd->outstate == OUTPUT_CODE) + { + if (strncmp(lptd->line, "@endcode", 8) == 0) + { + lptd->curr_macro->next = lptd->macros; + lptd->macros = lptd->curr_macro; + + lptd->outstate = SKIP_DOCUMENTATION; + } + else if (strncmp(lptd->line, "@uses", 5) == 0) + { + if (lptd->outstate != OUTPUT_CODE) + { + fprintf(stderr, + "Error on line %d: %s not allowed outside of code\n", + lptd->curr_lineno, + lptd->line); + lptd->retcode = 2; + } + else + { + name = r_strtok(&(lptd->line[5]), " \t\n", NULL); + if (name == NULL) + { + fprintf(stderr, + "Error on line %d: name not given with uses command\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + item = create_namelist_item(lptd, + name, + 0); + list_insert_unique(lptd, &(lptd->curr_macro->uses), item); + } + } + + strcpy(lptd->line, "\n"); + process_codeline(lptd); + } + else if (strncmp(lptd->line, "@defines", 8) == 0) + { + if (lptd->outstate != OUTPUT_CODE) + { + fprintf(stderr, + "Error on line %d: %s not allowed outside of code\n", + lptd->curr_lineno, + lptd->line); + lptd->retcode = 2; + } + else + { + name = r_strtok(&(lptd->line[8]), " \t\n", NULL); + if (name == NULL) + { + fprintf(stderr, + "Error on line %d: name not given with defines command\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + item = create_namelist_item(lptd, + name, + 0); + list_insert_unique(lptd, &(lptd->curr_macro->pounddefs), item); + } + } + + strcpy(lptd->line, "\n"); + process_codeline(lptd); + } + else if (strncmp(lptd->line, "@externdecls", 12) == 0) + { + if (lptd->outstate != OUTPUT_CODE) + { + fprintf(stderr, + "Error on line %d: %s not allowed outside of code\n", + lptd->curr_lineno, + lptd->line); + lptd->retcode = 2; + } + else + { + name = r_strtok(&(lptd->line[12]), " \t\n", NULL); + if (name == NULL) + { + fprintf(stderr, + "Error on line %d: name not given with externdecls command\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + item = create_namelist_item(lptd, + name, + 0); + list_insert_unique(lptd, &(lptd->curr_macro->globaldefs), item); + } + } + + strcpy(lptd->line, "\n"); + process_codeline(lptd); + } + else if (strncmp(lptd->line, "@staticdecls", 12) == 0) + { + if (lptd->outstate != OUTPUT_CODE) + { + fprintf(stderr, + "Error on line %d: %s not allowed outside of code\n", + lptd->curr_lineno, + lptd->line); + lptd->retcode = 2; + } + else + { + name = r_strtok(&(lptd->line[12]), " \t\n", NULL); + if (name == NULL) + { + fprintf(stderr, + "Error on line %d: name not given with staticdecls command\n", + lptd->curr_lineno); + lptd->retcode = 2; + } + else + { + item = create_namelist_item(lptd, + name, + 0); + list_insert_unique(lptd, &(lptd->curr_macro->staticdefs), item); + } + } + + strcpy(lptd->line, "\n"); + process_codeline(lptd); + } + else + { + process_codeline(lptd); + } + } + else if (lptd->line[0] == '@') + { + process_cmd(lptd); + } + /* Ignore documentation. */ + } + + if (lptd->outstate == OUTPUT_CODE) + { + fprintf(stderr, + "Warning: File %s ended while outputting code", + lptd->curr_filename); + lptd->retcode = 2; + lptd->outstate = SKIP_DOCUMENTATION; + } + + output_macros(lptd); + + if (lptd->xreffile != NULL) + { + output_xref(lptd); + } + + return(lptd->retcode); +} diff --git a/web/tango-weevil/tango.h b/web/tango-weevil/tango.h new file mode 100644 index 0000000000..834c0a02a5 --- /dev/null +++ b/web/tango-weevil/tango.h @@ -0,0 +1,120 @@ +/* + Tango/Weevil - A WEB Tangler and Weaver + Copyright (C) 1995 Corey Minyard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Corey Minyard - minyard@metronet.com +*/ + + +#define MAXLINESIZE 200 + +typedef struct linelist +{ + char *line; + struct linelist *next; +} t_linelist; + +typedef struct namelist +{ + char *name; + struct namelist *next; + union + { + int nest_val; + } u; +} t_namelist; + +typedef int bool; +#define TRUE 1 +#define FALSE 0 + + + +typedef struct lpmac +{ + char *name; + t_linelist *firstline; + t_linelist *lastline; + struct lpmac *next; + char *filename; + int startline; + t_namelist *staticdefs; + t_namelist *globaldefs; + t_namelist *pounddefs; + t_namelist *uses; +} t_lpmac; + +struct s_lptangodat; + +typedef void (*input_scanner)(struct s_lptangodat *lptd, + char *line, + int length, + int lineno); + +typedef void (*linenum_output)(struct s_lptangodat *lptd, + int lineno, + char *filename); + +typedef void (*scanner_init)(struct s_lptangodat *lptd); + +typedef struct s_lptangodat +{ + FILE *infile; + FILE *outfile; + FILE *xreffile; + char line[MAXLINESIZE]; + int outstate; + char *curr_filename; + int maxlinesize; + int curr_lineno; + t_lpmac *curr_macro; + t_linelist *curr_line; + t_lpmac *macros; + char *start_macro; + bool do_xref; + bool auto_xref; + bool instring; + bool in_comment; + bool do_linenums; + void *code_info; /* Language-dependant info. */ + input_scanner scan_input; + linenum_output output_linenum; + int retcode; /* Program return code. */ +} t_lptangodat; + + +void list_insert_unique(t_lptangodat *lptd, + t_namelist **list, + t_namelist *item); + +t_namelist *find_name_in_list(t_lptangodat *lptd, + t_namelist *list, + t_namelist *item); + +char *r_strtok(char *data, + char *sstr, + char **next_data); + +char *stralloc(char *str, + int length); + +void free_namelist_item(t_lptangodat *lptd, + t_namelist *item); + +t_namelist *create_namelist_item(t_lptangodat *lptd, + char *str, + int length); diff --git a/web/tango-weevil/tw.sty b/web/tango-weevil/tw.sty new file mode 100644 index 0000000000..64ffd9b3d8 --- /dev/null +++ b/web/tango-weevil/tw.sty @@ -0,0 +1,114 @@ +\def\filedate{1995/2/6} +\def\fileversion{v1.0} +% +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{tw} +\RequirePackage{moreverb} +\RequirePackage{multicol} +\typeout{Package: `tw' \fileversion \space <\filedate>} +% +\newlength{\tw@width} +% +\newcommand{\twstartcode}{\begingroup\footnotesize} +\newcommand{\twendcode}{\endgroup} +\newcommand{\twmacrodecl}[2]{\par\parindent=0pt\parskip=0pt\label{#2}% + {\noindent\sffamily\bfseries Code Chunk: #1}% + \index{#1@\textit{#1}|textbf}} +\newcommand{\twmacrouse}[2]{{\rm @\(<\)#1\(>\)% + (Macro \ref{#2} on page \pageref{#2})}% + \index{#1@\textit{#1}}} +\newcommand{\tw@use}[1]{\index{#1}#1}% +\newcommand{\tw@decl}[1]{\index{#1|textbf}#1}% +\newcommand{\u@line}[1]{\settowidth{\tw@width}{#1}\parbox{\tw@width}% + {#1\hspace{-\tw@width}\rule[-2pt]{\tw@width}{.2pt}}} +\newenvironment{twuses} +{ + \parindent=0pt% + \multicolsep=0pt% + \let\twuse\tw@use% + \begin{multicols}{2}[\u@line{\sffamily Uses:}] + \raggedright +} +{\end{multicols}\smallskip} +% +\newenvironment{twdecls} +{ + \parindent=0pt% + \multicolsep=0pt% + \let\twdecl\tw@decl% + \begin{multicols}{2}[\u@line{\sffamily Declares:}] + \raggedright +} +{\end{multicols}\smallskip} +% +\newcommand{\tw@Description}{\end{minipage}\par\smallskip +\parbox[t]{1in}{\hfill Description:\hspace*{1em}}\begin{minipage}[t]{3.5in}} +% +\newcommand{\tw@SideEffects}{\end{minipage}\par\smallskip +\parbox[t]{1in}{\hfill Side Effects:\hspace*{1em}}\begin{minipage}[t]{3.5in}} +% +\newcommand{\tw@ReturnValues}{\end{minipage}\par\smallskip +\parbox[t]{1in}{\hfill Return Vals:\hspace*{1em}}\begin{minipage}[t]{3.5in}} +% +\newcommand{\tw@Inputs}{\end{minipage}\par\smallskip +\parbox[t]{1in}{\hfill Inputs:\hspace*{1em}}\begin{minipage}[t]{3.5in}} +% +\newcommand{\tw@Outputs}{\end{minipage}\par\smallskip +\parbox[t]{1in}{\hfill Outputs:\hspace*{1em}}\begin{minipage}[t]{3.5in}} +% +\newcommand{\tw@StartCode}{\end{minipage}\par} +\newcommand{\twiscode}{\expandafter\ifx\csname twnocode\endcsname\relax}% +\newcommand{\twisproc}{\expandafter\ifx\csname twnoproc\endcsname\relax}% +% +\twisproc + \newenvironment{twproc}[1] + { + \par + \parskip=0pt% + \let\Description\tw@Description% + \let\SideEffects\tw@SideEffects% + \let\ReturnValues\tw@ReturnValues% + \let\Inputs\tw@Inputs% + \let\Outputs\tw@Outputs% + \let\StartCode\tw@StartCode% + \begin{minipage}{4.5in}\textbf{Procedure: #1}} + {} +\else% + \newenvironment{twproc}[1] + { + \@bsphack + \let\do\@makeother\dospecials\catcode`\^^M\active + \let\verbatim@startline\relax + \let\verbatim@addtoline\@gobble + \let\verbatim@processline\relax + \let\verbatim@finish\relax + \verbatim@ + } + {\@esphack} +\fi +% +\twiscode + \newenvironment{twcodechunk}{}{} +\else% + \newenvironment{twcodechunk} + { + \@bsphack + \let\do\@makeother\dospecials\catcode`\^^M\active + \let\verbatim@startline\relax + \let\verbatim@addtoline\@gobble + \let\verbatim@processline\relax + \let\verbatim@finish\relax + \verbatim@ + } + {\@esphack} +\fi +% +\newcommand{\twparmlist@label}[1]{\mbox{\texttt{#1} - }\hfil} +\newenvironment{twparmlist} +{% + \begin{list}{}% + {\setlength{\itemsep}{0pt}% + \setlength{\parsep}{0pt}% + \renewcommand{\makelabel}{\twparmlist@label}}% +}% +{\end{list}} diff --git a/web/tango-weevil/userguid.tpc b/web/tango-weevil/userguid.tpc new file mode 100644 index 0000000000..b9843d5805 --- /dev/null +++ b/web/tango-weevil/userguid.tpc @@ -0,0 +1,506 @@ +\expandafter\ifx\csname DocumentPart\endcsname\relax +\documentclass{book} +\usepackage{tw} +\title{Tango/Weevil --- A WEB Tangler and Weaver} +\author{Corey Minyard} +\date{\today} +\makeindex +\begin{document} +\maketitle +\fi +\section{Introduction} + +Tango/Weevil was born out of my frustration with the state of literate +programming software available. All the stuff I found was inadequate +for large projects, made you do to much work, or didn't do everything +a nice literate programming tool should do. The \texttt{noweb} +package came very close, but it didn't support inter-document cross +references and it used somewhat esoteric programming languages. + +Although I don't claim Tango/Weevil is the end-all for literate +programming, it is a start onto something more useful in a broad +sense. It is written completely in C. It generates cross-references +for variables in C programs. It generates variable cross-references +that may be shared between files. + +I came up with the name Tango/Weevil on a late-night programming +session. I don't guess my mind was working clearly and it sounded +good at the time. It sounds a little hokey now. + +\section{Requirements} + +Tango/Weevil requires a C compiler and \LaTeX2e. It will not work +with the old \LaTeX. \LaTeX2e\ is a whole lot better, anyway, so you +should get it if you haven't already. + +\section{Theory of Operation} + +Tango/Weevil consists of two programs named \texttt{tango} and +\texttt{weevil}. The \texttt{tango} program takes a literate program +and extracts code onto standard out. It also generates the cross +reference file (if invoked with the proper parameters). The +\texttt{weevil} program takes a literate program and generates a +\LaTeX document from it. It will use the cross reference file +generated by \texttt{tango} to list the variables. +Figure~\ref{fig:tango-weevil-operation} shows this operations. + +\begin{figure}[ht] +.PS +right +If: box "Input File" "(\textit{file}.tw)" invis +arrow +Tb: ellipse "tango" +arrow +Of: box width 1.5 * boxwid "Program Source" "(\textit{file}.c)" invis +move to Tb.b +down +arrow +Cb: box "Cross Reference" "(\textit{file}.xfr)" invis +arrow +Wb: ellipse "weevil" +move to If.b +line to Wb.l + (-linewid,0) +right +arrow to Wb.l +move to Wb.r +arrow +box width 1.1 * boxwid "\LaTeX Source" "(\textit{file}.tex)" invis +.PE +\centerline{\box\graph} +\caption{Tango/Weevil operation} +\label{fig:tango-weevil-operation} +\end{figure} + +A Tango/Weevil file has two main parts: code and documentation. These +may be interspersed in the document. Both programs start in +documentation mode, a \verb|@code| directive switches to code mode and +a \verb|@endcode| switched back to documentation mode. The +\verb|code| directive takes a macro name, everything between that +\verb|@code| and the next \verb|@endcode| is called a macro and may be +referred to with the macro's name. + +\section{Procedure Headers} + +The tango/weevil styles provide a nice format for documenting +procedures and functions in \LaTeX. Other document processor could be +designed to take this information and use it. The format is: + +\begin{verbatim} +\begin{twproc}{Procedure\_Name} +\Description Describes the function. The format of the input is not + really important, the output will indent it properly. +\ReturnValues \verb|int| - A function return value +\SideEffects Things that the procedure changes that will have effects + on other things. +\Inputs \begin{twparmlist} + \item[inparm1] This is an input parameter + \item[inparm2] This is another input parameter + \end{twparmlist} +\Outputs \begin{twparmlist} + \item[outparm] This is for non-return value output + parameters. + \end{twparmlist} +\StartCode % @code should come after this statement. +@code <Procedure_Name> +int +Procedure_Name(int inparm1, + char *inparm2, + int *outparm) +{ +} +@endcode +\end{twproc} +\end{verbatim} + +The output from this would be: + +\begin{twproc}{Procedure\_Name} +\Description Describes the function. The format of the input is not + really important, the output will indent it properly. +\ReturnValues \verb|int| - A function return value +\SideEffects Things that the procedure changes that will have effects + on other things. +\Inputs \begin{twparmlist} + \item[inparm1] This is an input parameter + \item[inparm2] This is another input parameter + \end{twparmlist} +\Outputs \begin{twparmlist} + \item[outparm] This is for non-return value output + parameters. + \end{twparmlist} +\StartCode +\begingroup\medskip +\twmacrodecl{Procedure\_Name}{userguid.tpc:macro:ProcedureName} +\begin{verbatimcmd}\twstartcode + 1 int + 2 Procedure_Name(int inparm1, + 3 char *inparm2, + 4 int *outparm) + 5 { + 6 } +\twendcode\end{verbatimcmd} +\endgroup +\end{twproc} + +\noindent All the sections (except \verb|\StartCode|) are optional. + +\section{Conditional Documentation} + +A special macro is created for including/excluding sections in +certain situations. If the command + +\begin{verbatim} + \def\twnocode{} +\end{verbatim} + +is included before the ``tw'' package is included (at the very top +of the file), everything between the \verb|@code| and \verb|@endcode| +will not be output. If the command + +\begin{verbatim} + \def\twnoproc{} +\end{verbatim} + +is included before the ``tw'' package is included (at the very top +of the file), everything between the \verb|\begin{twproc}| +and \verb|\end{twproc}| will not be output. The commands + +\begin{verbatim} + \twiscode + text + \fi +\end{verbatim} + +will not generate any output for anything between \verb|\twiscode| and +\verb|\fi| if \verb|\twnocode| was defined. Similarly, + +\begin{verbatim} + \twisproc + text + \fi +\end{verbatim} + +will not generate any output for anything between \verb|\twisproc| and +\verb|\fi| if \verb|\twnoproc| was defined. + +\section{File Processing} + +When \texttt{tango} processes a file, it does not spit out the macro +text as it receives it. Instead, it collects all the macro chunks +until the end. After it has collected all the macro chunks, it looks +for a special macro named ``*'', that is the start macro. It starts +outputting that macro. Whenever it sees a \verb|@<name>|, it will go +to the macro with the given name and output it. Assuming no recursive +macro references exit, the process will eventually end and a source +file will be produced. For example, consider this small program: + +\begin{verbatim} +\documentclass{article} +\usepackage{tw,makeidx} +\title{Hello World --- A Famous Program} +\author{Corey Minyard} +\date{\today} +\makeindex +\begin{document} +\maketitle +\section{Description} + +This program is the famous ``hello world'' program. Certainly +we have all written one of these programs. + +\section{Code} + +\subsection{Start Macro} +This is the main part of the program. +@code <*> +@<Includes> +@<Routines> +@endcode + +\subsection{Includes} +This program needs \texttt{stdio.h} because it uses printf. +@code <Includes> +#include <stdio.h> +@endcode + +\subsection{Routines} +This program only has one routine, the main routine. + +@code <Routines> +@<main> +@endcode + +\begin{twproc}{main} +\Description The main routine. +\ReturnValues \verb|int| - Program return code +\SideEffects Well, it prints something! +\Inputs \begin{twparmlist} + \item[argc] Argument count + \item[argv] The arguments + \end{twparmlist} +\StartCode +@code <main> +int +main(int argc, + char *argv) +{ + @<Output Hello> + @<Leave Program> +} +@endcode + +\subsection{Output Hello} +This code chunk actually does the output. +@code <Output Hello> + printf("hello world\n"); +@endcode + +\subsection{Leave Program} +This code chunk causes the program to exit. +@code <Leave Program> + exit(0); +@endcode +\end{twproc} + +\printindex +\end{document} +\end{verbatim} + +You can notice a few things about the program. Standard \LaTeX\ stuff +is used in the documentation. The code is not in the file in the same +order that it will be output to the C file to be compiled. A file +named hello.tw should be included in the distribution that is the same +as this file, try the following commands on that file: + +\begin{verbatim} + tango -autoxref -lang c hello.tw >hello.c + cc -o hello hello.c + ./hello + weevil -lang c hello.tw >hello.tex + latex hello + latex hello + makeindex hello.idx + latex hello +\end{verbatim} + +These command will create an executable \verb|hello| program and a +\verb|hello.dvi| file. Look at the dvi file by using a dvi previewer +or by printing it out. The output will be nicely formatted with the +code line numbered and cross references after every macro chunk. + +\section{Use} +Most of the major features of and concepts of Tango/Weevil have been +presented. This section presents the rest. + +\subsection{Cross Referencing to Other Files} +As mentioned before, Tango/Weevil lets you reference other files for +cross references. This is done with the following directives followed +by a filename. This must be the entire filename, including the +\verb|.xfr| suffix. + +\begin{tabular}{lp{3in}} +\verb|@includefile| & The given file is included (using a \#include) in + the current file.\\ +\verb|@reffile| & This file uses variables and functions in the + given file.\\ +\end{tabular} + +\subsection{Creating Your Own Cross References} +If you don't trust the cross reference generator or you are using a +language that \texttt{tango} will not generate cross references for, +you can put the cross references in yourself. The following +directives define different types of cross references. They are all +followed by a string that is the cross reference name. + +\begin{tabular}{lp{3in}} +\verb|@uses| & The given variable is used in the code chunk.\\ +\verb|@defines| & The given variable is a \#define in the code chunk.\\ +\verb|@externdecls| & The given variable is declared in the code chunk + and may be used outside the file.\\ +\verb|@staticdecls| & The given variable is declared in the code chunk + but may only be used in the file.\\ +\end{tabular} + +These directives must appear in the code chunk they are referencing. +They may not occur outside a code chunk. When these are used, auto +cross reference generation should be turned off but cross referencing +should be turned on. + +\subsection{Controlling Input Files and Line Numbers} +Since Tango/Weevil files may be produced by another program, some way +must exist for that file name and the proper line numbers to be passed +along to the compiler so debuggers work correctly. This is done using +the following directives: + +\begin{tabular}{lp{3in}} +\verb|@line| & The line number is set to the number following + the directive.\\ +\verb|@file| & The file name is set to the name following the + directive.\\ +\end{tabular} + +\subsection{Other Stuff} + +If an \verb|@| is needed in the code (outside of a quote or comment) +then \verb|@@| will produce on at-sign. + +\subsection{Running the programs} + +\subsubsection{tango} + +The \verb|tango| program has the following format: + +\begin{verbatim} + tango [options] inputfile +\end{verbatim} + +The output is sent to standard out. Valid options are: + +\begin{tabular}{lp{3in}} +-xref & Generate cross references\\ +-autoxref & Automatically generate cross references. + This will only work for ``C'' programs.\\ +-xreffile \emph{fname} & Send the output for the crossreferences + to \emph{fname} instead of the input file + prefix with \verb|.xfr| on the end.\\ +-lang \emph{lang} & Sets the programming language mode. + Currently only ``c'' is supported. This + options is required, so it's not really + an option.\\ +-nolinenum & Don't generate \#line references in the + output file.\\ +\end{tabular} + +\subsubsection{weevil} + +The \verb|weevil| program takes several options for specifying input +language modes, output formatter modes, and include directories for +cross reference files. + +\begin{tabular}{lp{3in}} +-I \emph{inc dir} & Adds a directory to the list of places to look + for cross reference (.xfr) files.\\ +-lang \emph{lang} & Sets the programming language mode. + Currently only ``c'' is supported. This + options is required, so it's not really + an option.\\ +-mode \emph{formatter} & Sets the output formatter mode. + Currently only ``latex'' is supported. This + options is required, so it's not really + an option.\\ +\end{tabular} + +\section{Internals} + +\subsection{The Cross Reference Document Format} + +The cross reference files have a very simple line-oriented format. +The first character on a line identifies the type of cross reference, +or identifies the file or macro name. The second character must be a +space. The third character to the end of the line gives a name. The +hello world program presented earlier has the following cross +reference file: +\begin{verbatim} +f hello.tw +m Leave Program +u exit +m Output Hello +u printf +m Routines +e main +m Includes +m * +\end{verbatim} + +The following first characters are valid: + +\begin{tabular}{lp{4in}} +f & The file name. This must be the first entry in the file, but + may occur more than once in the file. This allows multiple + files to be concatenated into one big file with the filenames + the entries come from preserved.\\ +m & A macro name. This sets the current macro; all cross references + between this line and the next macro name will belong to this + macro.\\ +u & A variable use. A variable (static, external, or \#defined) + was used.\\ +e & An external define. A variable visible to other modules was + defined.\\ +s & A static define. A variable was defined that will not be + visible to modules that cross reference this module. The + variable will be visible to modules that include this module.\\ +d & A \#define. A constant or macro was defined. This will only + be visible to modules that include this module.\\ +\end{tabular} + +\subsection{Adding New Languages} + +Adding new languages to \verb|tango| and \verb|weevil| is relatively +easy. The language must provide two routines and must add these to +the array of valid languages in \verb|tango| and \verb|weevil|. For +\verb|tango| the routines are: + +\begin{tabular}{lp{3in}} +init\_\emph{x}\_mode & Initialize the language \emph{x} mode. A + variable name \verb|code_info| is available + in the \verb|t_lptangodat| structure that the + language mode may use to store a pointer.\\ +\emph{x}\_scan\_input & Takes an input block of data as a character + pointer, a length, and a line number. At + a minimum, it must set the \verb|inquote| + and \verb|in_comment| variables in the + \verb|t_lptangodat| structure if the code + was in a quote or a comment at the end of + each code block. Cross reference may also + be created if the \verb|auto_xref| + variable in the structure is set TRUE.\\ +\end{tabular} + +For \verb|weevil| the routines are: + +\begin{tabular}{lp{3in}} +init\_\emph{x}\_mode & Initialize the language \emph{x} mode. A + variable name \verb|code_info| is available + in the \verb|t_lptangodat| structure that the + language mode may use to store a pointer.\\ +\emph{x}\_handle\_char & Takes a single character at a time from + the input file. It must set the \verb|inquote| + and \verb|in_comment| variables in the + \verb|t_lptangodat| structure if the code + was in a quote or a comment after each + character.\\ +\end{tabular} + +The reason for quote and comment sensing is to avoid having the user +have to enter \verb|@@| in comments and quotes. + +If a language mode is emitting cross references, it must put those +into lists of names. Four types of lists are kept for each macro; one +for each type of cross reference. These lists are kepts in one-way +linked lists of \verb|t_namelist| items. Each item has a character +pointer with the name and a pointer to the next item. The routine +\verb|create_namelist_item| should be used to allocate this structure. +The routine \verb|free_namelist_item| should be used to free the +structure. To add a structure to a list, use the +\verb|list_insert_unique| routine: +\begin{verbatim} + /* Add the item to the macro's list of variable uses. */ + list_insert_unique(lptd, &(lptd->curr_macro->uses), item); + /* Add the item to the list of external declarations. */ + list_insert_unique(lptd, &(lptd->curr_macro->globaldefs), item); + /* Add the item to the list of static declarations. */ + list_insert_unique(lptd, &(lptd->curr_macro->staticdefs), item); + /* Add the item to the list of #defines. */ + list_insert_unique(lptd, &(lptd->curr_macro->pounddefs), item); +\end{verbatim} + +The following routines are also available for use: +\begin{verbatim} + find_name_in_list - Find a name in a namelist. + r_strtok - A reentrant strtok. + stralloc - Allocates and copies a string. +\end{verbatim} + +\expandafter\ifx\csname DocumentPart\endcsname\relax +\end{document} +\fi diff --git a/web/tango-weevil/weevdoc.tex b/web/tango-weevil/weevdoc.tex new file mode 100644 index 0000000000..8e9e236b07 --- /dev/null +++ b/web/tango-weevil/weevdoc.tex @@ -0,0 +1,44 @@ +\documentclass{book} +\def\twnocode{} +%\def\twnoproc{} +\usepackage{tw,makeidx} +\title{Weevil - A WEB Weaver} +\author{Corey Minyard} +\date{\today} +\pagenumbering{roman} +\makeindex +\newcommand{\clearemptydoublepage}{\newpage{\pagestyle{empty}\cleardoublepage}} +\begin{document} +\maketitle +\clearemptydoublepage +\tableofcontents +\clearemptydoublepage +\pagenumbering{arabic} +\chapter{Introduction} + +This \verb|weevil| program generates \LaTeX2e\ output from a literate +program. It doesn't do a whole lot; it just takes the code and +converts it to \LaTeX\ format and generates the appropriate stuff to +go around the code. It is up to the user to supply the appropriate +\LaTeX\ headers, trailers and documentation for the code. + +\section{Operation} + +All directives except \verb|code| and \verb|endcode| are ignored by +\verb|weevil|. These directives are converted to \verb|\begin{alltt}| +and \verb|\end{alltt}| respectively. The code inbetween has all +braces and backslashes converted to appropriate \LaTeX\ codes so they +will print properly. Variable use and define directives are collected +and issued at the bottom of the list. Macro definitions and uses are +cross-referenced. + +\chapter{weevinfo} +\include{weevinfo} +\chapter{weevil} +\include{weevil} +\chapter{c\_weevil} +\include{c_weevil} +\chapter{\LaTeX} +\include{latex} +\printindex +\end{document} diff --git a/web/tango-weevil/weevil.tw b/web/tango-weevil/weevil.tw new file mode 100644 index 0000000000..89b46f5f72 --- /dev/null +++ b/web/tango-weevil/weevil.tw @@ -0,0 +1,1367 @@ +% +% Tango/Weevil - A WEB Tangler and Weaver +% Copyright (C) 1995 Corey Minyard +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +% +% Corey Minyard - minyard@metronet.com +% +\section{Weevil Main File Intro} + +The following code holds the main file structure: + +@includefile weevinfo.xfr +%The following holds cross references for all the file used by weevil +@reffile weevilref.xfr +@reffile c_weevil.xfr +@reffile latex.xfr + +@code <*> +@<Includes> +@<Defines> +@<Code> +@endcode + +\subsection{Includes} +Just some standard stuff to include. The \verb|weevinfo| file +contains all the major data structures for weevil. + +@code <Includes> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include "weevinfo.h" +@endcode + +\subsection{Defines} +Since all the language and output specific information is not in this +file, not much is left to define. + +@code <Defines> +/* Return values for the get_input_line routine. */ +#define GOT_LINE 1 +#define END_OF_FILE 0 + +/* These are for input_xreffile(). */ +#define INCLUDE_TYPE 1 +#define XREF_TYPE 2 +#define MAIN_TYPE 3 + +#define MAX_FILENAME_LENGTH 200 +@endcode + +\subsection{Code} +Herer is the meat of the matter here. The actual code. + +@code <Code> +@<Utils> +@<open_include_file> +@<input_xreffile> +@<process_cmd> +@<process_codeline> +@<output_xref> +@<Get Args> +@<main> +@endcode + +\subsubsection{Utils} +These are various utilities that may be used both inside and outside +this module. + +@code <Utils> +@<get_input_line> +@<r_strtok> +@<stralloc> +@<find_name_in_xref> +@<find_macro_in_xref> +@endcode + +\begin{twproc}{r\_strtok} +\Description A re-entrant strtok. This routine scans the + string \verb|data| for tokens. These tokens + are separated by characters in the \verb|sstr| + string. A pointer to the next token and a + starting place for the next \verb|r_strtok| call is + returned. +\SideEffects The string in the \verb|data| variable is modified + by insertion of null characters (\verb|'\0'|) to + separate the tokens. +\ReturnValues \verb|char *| - A pointer to the next token. +\Inputs \begin{twparmlist} + \item[data] The string to scan for token + \item[sstr] A string containing characters that + should separate the tokens. + \end{twparmlist} +\Outputs \begin{twparmlist} + \item[next\_data] A pointer to the next place + to start scanning. This may be used as + the next \verb|data| parameter to get + successive tokens from a string. + \end{twparmlist} +\StartCode +@code <r_strtok> +char * +r_strtok(char *data, + char *sstr, + char **next_data) +{ + char *retval; + + + while ( (*data != '\0') + && (strchr(sstr, *data) != NULL)) + { + data++; + } + + retval = data; + while ( (*data != '\0') + && (strchr(sstr, *data) == NULL)) + { + data++; + } + + if (retval == data) + { + retval = NULL; + } + else + { + if (*data != '\0') + { + *data = '\0'; + data++; + } + + if (next_data != NULL) + { + *next_data = data; + } + } + + return(retval); +} +@endcode +\end{twproc} + +\begin{twproc}{stralloc} +\Description Allocates some memory for a string and copies the + string into it. +\ReturnValues \verb|char *| - The newly allocated string. +\Inputs \begin{twparmlist} + \item[str] The string to allocate the data for + and copy data into. + \end{twparmlist} +\StartCode +@code <stralloc> +char * +stralloc(char *str) +{ + char *retval; + + retval = malloc(strlen(str) + 1); + if (retval == NULL) + { + fprintf(stderr, + "Fatal: Unable to allocate memory\n"); + exit(1); + } + + strcpy(retval, str); + return(retval); +} +@endcode +\end{twproc} + +\begin{twproc}{get\_input\_line} +\Description Get a line of text from the input file. If the line + is too long, it will truncate the line and skip + everything after the maximum line size. +\SideEffects The input file is read from. The current line number + is advanced. The current input line string is set + to the new input line. +\ReturnValues \verb|int| - This will be either \verb|END_OF_FILE| + or \verb|GOT_LINE|. +\Inputs \begin{twparmlist} + \item[lpwd] Holds ``global'' data for the program + \end{twparmlist} +\StartCode +@code <get_input_line> +int +get_input_line(t_lpweevildat *lpwd) +{ +#define FLUSHSIZE 20 + int retval; + int length; + char flushbuf[FLUSHSIZE]; + int flushlength; + + + (lpwd->curr_lineno)++; + + if (fgets(lpwd->line, lpwd->maxlinesize, lpwd->infile) == NULL) + { + retval = END_OF_FILE; /* Did not get a line, end of file. */ + } + else + { + retval = GOT_LINE; + length = strlen(lpwd->line); + + /* If the line was too long (it was the max size and the + last character was not a newline), print a log and flush + to end of line. */ + if ( (length == (lpwd->maxlinesize - 1)) + && (lpwd->line[length-1] != '\n')) + { + fprintf(stderr, + "Warning, line %d too long," + " flushing to end of line\n", + lpwd->curr_lineno); + if (fgets(flushbuf, FLUSHSIZE, lpwd->infile) == NULL) + { + flushlength = 0; + } + else + { + flushlength = strlen(flushbuf); + } + while ( (flushlength == (FLUSHSIZE - 1)) + && (lpwd->line[flushlength-1] != '\n')) + { + if (fgets(flushbuf, FLUSHSIZE, lpwd->infile) == NULL) + { + flushlength = 0; + } + else + { + flushlength = strlen(flushbuf); + } + } + } + else + { + lpwd->line[length-1] = '\0'; /* Get rid of the newline. */ + } + } + + return(retval); +} +@endcode +\end{twproc} + +\begin{twproc}{find\_name\_in\_xref} +\Description Finds a name (string in a name field) the same + as a specified string. +\ReturnValues \verb|xreflist *| - A pointer to the list item + holding the name. +\Inputs \begin{twparmlist} + \item[start] The list start + \item[name] The name to search for + \end{twparmlist} +\StartCode +@code <find_name_in_xref> +xreflist * +find_name_in_xref(xreflist *start, + char *name) +{ + while (start != NULL) + { + if (strcmp(start->name, name) == 0) + { + break; + } + + start = start->next; + } + + return(start); +} +@endcode +\end{twproc} + +\begin{twproc}{find\_macro\_in\_xref} +\Description Finds a macro name (string in a macro field) the same + as a specified string. +\ReturnValues \verb|xreflist *| - A pointer to the list item + holding the macro name. +\Inputs \begin{twparmlist} + \item[start] The list start + \item[name] The macro name to search for + \end{twparmlist} +\StartCode +@code <find_macro_in_xref> +xreflist * +find_macro_in_xref(xreflist *start, + char *macro) +{ + while (start != NULL) + { + if ( (start->file == NULL) + && (strcmp(start->macro, macro) == 0)) + { + break; + } + start = start->next; + } + + return(start); +} +@endcode +\end{twproc} + +\subsubsection{Command Processing} +All commands encountered (commands start with \verb|@| and must start +in column 1) while processing documentation will be handled by this +code. + +\begin{twproc}{process\_cmd} +\Description Handles a \verb|@| command while in documentation (not code) + state. +\SideEffects \begin{twparmlist} + \item[codestate] + \item[curr\_filename] + \item[curr\_macro] + \end{twparmlist} +\Inputs \begin{twparmlist} + \item[lpwd] Holds ``global'' data for the program + \end{twparmlist} +\StartCode +@code <process_cmd> +void +process_cmd(t_lpweevildat *lpwd) +{ + char *strhold; + char *cmd; + char *name; + int namelength; + + + cmd = r_strtok(&(lpwd->line[1]), " \t", &strhold); + if (cmd == NULL) + { + fprintf(stderr, + "Error on line %d: Missing command\n", + lpwd->curr_lineno); + lpwd->retcode = 2; + } + @<Handle code command> + @<Handle file command> + else if (strcmp(cmd, "includefile") == 0) + { + name = r_strtok(strhold, " \t", &strhold); + input_xreffile(lpwd, name, INCLUDE_TYPE); + } + else if (strcmp(cmd, "reffile") == 0) + { + name = r_strtok(strhold, " \t", &strhold); + input_xreffile(lpwd, name, XREF_TYPE); + } + else if (strcmp(cmd, "line") == 0) + { + /* Ignored when weevilling. */ + } + else + { + fprintf(stderr, + "Error on line %d: Invalid command\n", + lpwd->curr_lineno); + lpwd->retcode = 2; + } +} +@endcode + +The following code handle a \verb|@code| command. It will get the +name of the code macro (between \verb|<| and \verb|>|) and call the +output routines to handle macro beginnings. + +@code <Handle code command> +else if (strcmp(cmd, "code") == 0) +{ + if (lpwd->codestate == IN_CODE) + { + fprintf(stderr, + "Error on line %d:" + " Macro begin when already processing a macro\n", + lpwd->curr_lineno); + lpwd->retcode = 2; + } + else + { + while ((*strhold == ' ') || (*strhold == '\t')) + { + strhold++; + } + + if (*strhold != '<') + { + fprintf(stderr, + "Error on line %d:" + " Macro name must be surrounded by <>\n", + lpwd->curr_lineno); + lpwd->retcode = 2; + } + else + { + strhold++; + } + + name = r_strtok(strhold, ">", &strhold); + lpwd->curr_macro = stralloc(name); + + lpwd->codestate = IN_CODE; + + lpwd->begin_macro(lpwd, name); + } +} +@endcode + +The following code handles a file command. It scans for the file name +and sets the current file name to the name specified in the command. + +@code <Handle file command> +else if (strcmp(cmd, "file") == 0) +{ + if (lpwd->codestate == IN_CODE) + { + fprintf(stderr, + "Error on line %d:" + " File name change not allowed in code\n", + lpwd->curr_lineno); + lpwd->retcode = 2; + } + else + { + name = r_strtok(strhold, " \t", &strhold); + if (name == NULL) + { + fprintf(stderr, + "Error on line %d:" + " File name not given with file command\n", + lpwd->curr_lineno); + lpwd->retcode = 2; + } + lpwd->curr_filename = stralloc(name); + namelength = strlen(name); + } +} +@endcode +\end{twproc} + +\subsubsection{Process Code} +Output code goes through the following routine where it will be output +by the mode-dependent output routines. + +\begin{twproc}{process\_codeline} +\Description Processed a line of code. This will scan for + \verb|@<>| macro uses in the code and format + the macro name. It will also convert + \verb|@@| in the input to \verb|@|. +\SideEffects \begin{twparmlist} + \item[code\_lineno] + \end{twparmlist} +\Inputs \begin{twparmlist} + \item[lpwd] Holds ``global'' data for the program + \end{twparmlist} +\StartCode +@code <process_codeline> +void +process_codeline(t_lpweevildat *lpwd) +{ + char *cp; + char *macro_name; + int code_proc; + + cp = lpwd->line; + lpwd->output_linenum(lpwd); + lpwd->code_lineno++; + while (*cp != '\0') + { + code_proc = lpwd->handle_char(lpwd, *cp, *(cp+1)); + if (code_proc == PROCESS_CHAR_INSTRING) + { + lpwd->output_char(lpwd, *cp); + cp++; + } + else if (code_proc == PROCESS_CHAR_SKIPNEXT) + { + lpwd->output_char(lpwd, *cp); + cp++; + lpwd->output_char(lpwd, *cp); + cp++; + } + else + { + switch(*cp) + { + case '@': + cp++; + switch(*cp) + { + case '@': + lpwd->output_char(lpwd, '@'); + lpwd->output_char(lpwd, '@'); + cp++; + break; + + case '<': + cp++; + macro_name = r_strtok(cp, ">", &cp); + lpwd->output_macro_use(lpwd, macro_name); + break; + + default: + fprintf(stderr, + "Error on line %d:" + " Invalid @ operator: %c\n", + lpwd->curr_lineno, + *cp); + lpwd->retcode = 2; + break; + } + break; + + default: + lpwd->output_char(lpwd, *cp); + cp++; + } + } + } + + lpwd->output_char(lpwd, '\n'); +} +@endcode +\end{twproc} + +\subsubsection{Cross Reference Routines} +These routine handle inputting and outputting cross-references. + +\begin{twproc}{output\_xref} +\Description Outputs the cross references for a macro. This + routine will scan all cross reference lists for the + given macro name. Uses references will be output + with the declaration location(s), if available. + All other references will just be output. Many + output-mode dependent handlers do the dirty work + of the output; they are called form this routine. +\Inputs \begin{twparmlist} + \item[lpwd] Holds ``global'' data for the program + \end{twparmlist} +\StartCode +@code <output_xref> +void +output_xref(t_lpweevildat *lpwd) +{ + xreflist *list; + xreflist *def; + bool declares_output;/* Since declarations can come from + multiple sources, this tracks if + any declarations have been output + so appropriate cleanup can be + done. */ + bool any_xrefs; /* This tells if any references have + been output. This is used because + a special output routine is called + if any references have been output. */ + + + any_xrefs = FALSE; + list = find_macro_in_xref(lpwd->uses, lpwd->curr_macro); + if (list != NULL) + { + any_xrefs = TRUE; + lpwd->output_begin_uses(lpwd); + while (list != NULL) + { + def = find_name_in_xref(lpwd->pounddefs, list->name); + if (def == NULL) + { + def = find_name_in_xref(lpwd->vardefs, list->name); + } + + lpwd->output_use_start(lpwd, list->name); + while (def != NULL) + { + lpwd->output_use_ref(lpwd, + def->file, + def->macro, + list->name); + + def = find_name_in_xref(def->next, list->name); + if (def != NULL) + { + lpwd->output_between_use_refs(lpwd, list->name); + } + } + lpwd->output_use_end(lpwd, list->name); + list = find_macro_in_xref(list->next, lpwd->curr_macro); + } + lpwd->output_end_uses(lpwd); + } + + declares_output = FALSE; + list = find_macro_in_xref(lpwd->vardefs, lpwd->curr_macro); + if (list != NULL) + { + any_xrefs = TRUE; + declares_output = TRUE; + lpwd->output_begin_decls(lpwd); + while (list != NULL) + { + lpwd->output_decl(lpwd, list->name); + + list = find_macro_in_xref(list->next, lpwd->curr_macro); + } + } + + list = find_macro_in_xref(lpwd->pounddefs, lpwd->curr_macro); + if (list != NULL) + { + any_xrefs = TRUE; + if (!declares_output) + { + declares_output = TRUE; + lpwd->output_begin_decls(lpwd); + } + while (list != NULL) + { + lpwd->output_decl(lpwd, list->name); + + list = find_macro_in_xref(list->next, lpwd->curr_macro); + } + } + + if (declares_output) + { + lpwd->output_end_decls(lpwd); + } + + if (any_xrefs) + { + lpwd->output_after_xrefs(lpwd); + } +} +@endcode +\end{twproc} + +\begin{twproc}{open\_include\_file} +\Description Opens a cross-reference file, looking in the include + list if necessary. +\Inputs \begin{twparmlist} + \item[lpwd] Holds ``global'' data for the program + \item[filename] The file name to open + \end{twparmlist} +\StartCode +@code <open_include_file> +FILE * +open_include_file(t_lpweevildat *lpwd, + char *filename) +{ + xreflist *includes; + FILE *file; + char fname[MAX_FILENAME_LENGTH+1]; + int filename_length; + + + filename_length = strlen(filename); + file = NULL; + if (filename[0] != '/') + { + includes = lpwd->includes; + while ((file == NULL) && (includes != NULL)) + { + if ( (strlen(includes->name) + filename_length + 1) + > MAX_FILENAME_LENGTH) + { + fprintf(stderr, + "Warning: Include path + filename too long: %s/%s\n", + includes->name, + filename); + } + else + { + strcpy(fname, includes->name); + strcat(fname, "/"); + strcat(fname, filename); + file = fopen(fname, "r"); + } + includes = includes->next; + } + } + if (file == NULL) + { + file = fopen(filename, "r"); + } + + return(file); +} +@endcode +\end{twproc} + +\begin{twproc}{input\_xreffile} +\Description Reads a cross reference file and store the references + in the reference tables. +\SideEffects \begin{twparmlist} + \item[pounddefs] + \item[vardefs] + \item[uses] + \end{twparmlist} +\Inputs \begin{twparmlist} + \item[lpwd] Holds ``global'' data for the program + \item[filename] The file name the references are to + be read from. + \item[incltype] The type of include. This may be + \begin{twparmlist} + \item[INCLUDE\_TYPE] a \verb|@includefile| + \item[XREF\_TYPE] a \verb|@reffile| + \item[MAIN\_TYPE] the cross reference file for the + file being processed. + \end{twparmlist} + \end{twparmlist} +\StartCode +@code <input_xreffile> +void +input_xreffile(t_lpweevildat *lpwd, + char *filename, + int incltype) +{ + char *line; + FILE *in_file; + char linebuf[MAXLINESIZE]; + xreflist *elem; + char *curr_macro; + char *actfilename; + curr_macro = "invalid"; + in_file = open_include_file(lpwd, filename); + if (in_file == NULL) + { + fprintf(stderr, "Unable to open xref file: %s\n", + filename); + } + else + { + line = fgets(linebuf, MAXLINESIZE, in_file); + if ( (line == NULL) + || (strncmp(line, "f ", 2) != 0)) + { + fprintf(stderr, + "Invalid xref file: %s\n", + filename); + fclose(in_file); + } + else + { + if (incltype == MAIN_TYPE) + { + actfilename = NULL; + } + else + { + actfilename = stralloc(r_strtok(&(line[2]), + "\n", + NULL)); + } + + line = fgets(linebuf, MAXLINESIZE, in_file); + while (line != NULL) + { + elem = malloc(sizeof(*elem)); + elem->file = actfilename; + elem->macro = curr_macro; + if (elem == NULL) + { + fprintf(stderr, + "Unable to allocate enough memory\n"); + exit(1); + } + + if (strncmp(line, "f ", 2) == 0) + { + actfilename = stralloc(r_strtok(&(line[2]), + "\n", + NULL)); + } + else if (strncmp(line, "u ", 2) == 0) + { + if (incltype == MAIN_TYPE) + { + elem->name = stralloc(r_strtok(&(line[2]), + "\n", + NULL)); + elem->next = lpwd->uses; + lpwd->uses = elem; + } + } + else if (strncmp(line, "s ", 2) == 0) + { + if ( (incltype == MAIN_TYPE) + || (incltype == INCLUDE_TYPE)) + { + elem->name = stralloc(r_strtok(&(line[2]), + "\n", + NULL)); + elem->next = lpwd->vardefs; + lpwd->vardefs = elem; + } + } + else if (strncmp(line, "d ", 2) == 0) + { + if ( (incltype == MAIN_TYPE) + || (incltype == INCLUDE_TYPE)) + { + elem->name = stralloc(r_strtok(&(line[2]), + "\n", + NULL)); + elem->next = lpwd->pounddefs; + lpwd->pounddefs = elem; + } + } + else if (strncmp(line, "e ", 2) == 0) + { + elem->name = stralloc(r_strtok(&(line[2]), + "\n", + NULL)); + elem->next = lpwd->vardefs; + lpwd->vardefs = elem; + } + else if (strncmp(line, "m ", 2) == 0) + { + curr_macro = stralloc(r_strtok(&(line[2]), + "\n", + NULL)); + free(elem); + } + else + { + fprintf(stderr, + "Invalid prefix (%c%c) in xreffile: %s\n", + line[0], line[1], filename); + free(elem); + } + + line = fgets(linebuf, MAXLINESIZE, in_file); + } + } + } +} +@endcode +\end{twproc} + +\subsubsection{Get Args} +The following code will process the command line for arguments. + +@code <Get Args> +@<get_arg_str> +@<Argument Data> +@<set_mode> +@<process_args> +@endcode + +\begin{twproc}{get\_arg\_str} +\Description Returns a parameter for an argument. If the + argument is in the same string as the command, + that will be returned. Otherwise, the next + argument will be returned. +\ReturnValues \verb|static char *| - A pointer to the + argument parameter. +\Inputs \begin{twparmlist} + \item[argidx] A index for the current argument. + \item[argc] The argument count + \item[argv] The arguments + \item[start\_offset] The character offset into the + current argument that is right after the + command finishes. + \end{twparmlist} +\Outputs \begin{twparmlist} + \item[argidx] The next argument + \end{twparmlist} +\StartCode +@code <get_arg_str> +static char * +get_arg_str(int *argidx, + int argc, + char *argv[], + int start_offset) +{ + char *retval; + + + if (argv[*argidx][start_offset] == '\0') + { + (*argidx)++; + if ((*argidx) == argc) + { + retval = NULL; + } + else + { + retval = argv[*argidx]; + } + } + else + { + retval = &(argv[*argidx][start_offset]); + } + + return(retval); +} +@endcode +\end{twproc} + +The following holds a list of languages that are supported and a list +of output modes that are supported. Each language and output mode has +a set of handler routines that are given in these lists. + +@code <Argument Data> +static struct s_langs +{ + char *lang_name; + char_handler handler; + handler_init init; +} langs[] = +{ + { "c", &c_handle_char, &init_c_lang } +}; + +static const num_langs = sizeof(langs) / sizeof(struct s_langs); + +static struct s_modes +{ + char *mode_name; + mode_init init; + begin_macro_handler begin_macro; + end_macro_handler end_macro; + finalend_chunk_handler finalend_chunk; + output_macro_use_handler output_macro_use; + output_linenum_handler output_linenum; + output_char_handler output_char; + output_begin_uses_handler output_begin_uses; + output_end_uses_handler output_end_uses; + output_use_start_handler output_use_start; + output_use_end_handler output_use_end; + output_use_ref_handler output_use_ref; + output_between_use_refs_handler output_between_use_refs; + output_begin_decls_handler output_begin_decls; + output_end_decls_handler output_end_decls; + output_decl_handler output_decl; + output_after_xrefs_handler output_after_xrefs; +} modes[] = +{ + { "latex", &latex_mode_init, + &latex_begin_macro, &latex_end_macro, + &latex_finalend_chunk, + &latex_output_macro_use, &latex_output_linenum, + &latex_output_char, &latex_output_begin_uses, + &latex_output_end_uses, &latex_output_use_start, + &latex_output_use_end, &latex_output_use_ref, + &latex_output_between_use_refs, &latex_output_begin_decls, + &latex_output_end_decls, &latex_output_decl, + &latex_output_after_xrefs } +}; + +static const num_modes = sizeof(modes) / sizeof(struct s_modes); +@endcode + +\begin{twproc}{set\_mode} +\Description Sets the output mode. +\SideEffects The left side of every assignment in the routine + is a side-effect. +\Inputs \begin{twparmlist} + \item[lpwd] Holds ``global'' data for the program + \item[mode] The mode to set. + \end{twparmlist} +\StartCode +@code <set_mode> +void +set_mode(t_lpweevildat *lpwd, + struct s_modes *mode) +{ + lpwd->begin_macro = mode->begin_macro; + lpwd->end_macro = mode->end_macro; + lpwd->finalend_chunk = mode->finalend_chunk; + lpwd->output_macro_use = mode->output_macro_use; + lpwd->output_linenum = mode->output_linenum; + lpwd->output_char = mode->output_char; + lpwd->output_begin_uses = mode->output_begin_uses; + lpwd->output_end_uses = mode->output_end_uses; + lpwd->output_use_start = mode->output_use_start; + lpwd->output_use_end = mode->output_use_end; + lpwd->output_use_ref = mode->output_use_ref; + lpwd->output_between_use_refs = mode->output_between_use_refs; + lpwd->output_begin_decls = mode->output_begin_decls; + lpwd->output_end_decls = mode->output_end_decls; + lpwd->output_decl = mode->output_decl; + lpwd->output_after_xrefs = mode->output_after_xrefs; +} +@endcode +\end{twproc} + +\begin{twproc}{process\_args} +\Description Actually handles the high-level processing of + arguments. It scans for each argument and + handles each one individually. It will return + after the last argument. +\Inputs \begin{twparmlist} + \item[lpwd] Holds ``global'' data for the program + \item[argc] The argument count + \item[argv] The argument data + \end{twparmlist} +\Outputs \begin{twparmlist} + \item[argidx] The index of the first argument + after the option arguments. + \end{twparmlist} +\StartCode +@code <process_args> +void +process_args(t_lpweevildat *lpwd, + int argc, + char *argv[], + int *argidx) +{ + char *str; + int i; + int my_mode; + + + my_mode = 0; + + *argidx = 1; + while (argv[*argidx][0] == '-') + { + /* This forces the end of option processing. */ + if (strcmp(argv[*argidx], "--") == 0) + { + (*argidx)++; + break; + } + @<Handle lang option> + @<Handle mode option> + @<Handle include option> + else + { + fprintf(stderr, + "%s: Error: Invalid option specified: %s\n", + argv[0], + argv[*argidx]); + exit(1); + } + + (*argidx)++; + } + + set_mode(lpwd, &(modes[i])); + modes[i].init(lpwd); +} +@endcode + +The following code get a language option and tries to find it in the +language list. If it is successful, it will set the language handlers +to the ones for the language. If an error occurs, it will print an +error and exit. + +@code <Handle lang option> +else if (strncmp(argv[*argidx], "-lang", 5) == 0) +{ + if (lpwd->handle_char != NULL) + { + fprintf(stderr, + "%s: Error: only 1 -lang is allowed\n", + argv[0]); + exit(1); + } + + str = get_arg_str(argidx, argc, argv, 5); + if (str == NULL) + { + fprintf(stderr, + "%s: Error: -lang specified but no lang given\n", + argv[0]); + exit(1); + } + + for (i=0; i<num_langs; i++) + { + if (strcmp(str, langs[i].lang_name) == 0) + { + lpwd->handle_char = langs[i].handler; + langs[i].init(lpwd); + break; + } + } + + if (lpwd->handle_char == NULL) + { + fprintf(stderr, + "%s: Error: Invalid lang given: %s\n", + argv[0], str); + exit(1); + } +} +@endcode + +The following handles an output mode. It scans the mode list for the +output mode. If it finds it, it sets the mode handlers. If it +doesn't, it prints an error and returns. + +@code <Handle mode option> +else if (strncmp(argv[*argidx], "-mode", 5) == 0) +{ + if (lpwd->begin_macro != NULL) + { + fprintf(stderr, + "%s: Error: only 1 -mode is allowed\n", + argv[0]); + exit(1); + } + + str = get_arg_str(argidx, argc, argv, 5); + if (str == NULL) + { + fprintf(stderr, + "%s: Error: -mode specified but no mode given\n", + argv[0]); + exit(1); + } + + for (i=0; i<num_modes; i++) + { + if (strcmp(str, modes[i].mode_name) == 0) + { + my_mode = i; + break; + } + } + + if (lpwd->handle_char == NULL) + { + fprintf(stderr, + "%s: Error: Invalid mode given: %s\n", + argv[0], str); + exit(1); + } +} +@endcode +\end{twproc} + +@code <Handle include option> +else if (strncmp(argv[*argidx], "-I", 2) == 0) +{ + xreflist *newinclude; + xreflist *includelist; + + + str = get_arg_str(argidx, argc, argv, 2); + if (str == NULL) + { + fprintf(stderr, + "%s: Error: -I specified but no include directory given\n", + argv[0]); + exit(1); + } + + newinclude = malloc(sizeof(*newinclude)); + if (newinclude == NULL) + { + fprintf(stderr, + "%s: Error: Out of memory\n", + argv[0]); + exit(1); + } + + newinclude->name = str; + newinclude->next = NULL; + includelist = lpwd->includes; + if (includelist == NULL) + { + lpwd->includes = newinclude; + } + else + { + while (includelist->next != NULL) + { + includelist = includelist->next; + } + includelist->next = newinclude; + } +} +@endcode + +\begin{twproc}{main} +\Description The main routine. This routine initializes some data, + processes the input options, then crunches on input + lines. +\ReturnValues \verb|int| - system return value. +\Inputs \begin{twparmlist} + \item[argc] The number of arguments given to the program + \item[argv] The actual arguments. + \end{twparmlist} +\StartCode +@code <main> +int +main(int argc, + char *argv[]) +{ + t_lpweevildat *lpwd; + int argidx; + int len; + char *str; + char fname[MAX_FILENAME_LENGTH+1]; + + + @<Main init> + + process_args(lpwd, argc, argv, &argidx); + + if (lpwd->handle_char == NULL) + { + fprintf(stderr, "%s: No lang specified\n", argv[0]); + exit(1); + } + + if (lpwd->begin_macro == NULL) + { + /* No mode specified, default to the first one. */ + set_mode(lpwd, &(modes[0])); + } + + if (argc <= argidx) + { + fprintf(stderr, "%s: No filename specified\n", + argv[0]); + exit(1); + } + + @<Open input file> + + @<Scan input> + + if (lpwd->codestate == IN_CODE) + { + fprintf(stderr, + "Warning: File %s ended while outputting code", + lpwd->curr_filename); + lpwd->codestate = NOT_IN_CODE; + lpwd->retcode = 2; + } + + return(lpwd->retcode); +} +@endcode + +Allocate an initialize the global data structure. + +@code <Main init> + lpwd = malloc(sizeof(*lpwd)); + if (lpwd == NULL) + { + fprintf(stderr, "Unable to allocate enough memory\n"); + exit(1); + } + + lpwd->maxlinesize = MAXLINESIZE; + lpwd->curr_lineno = 0; + lpwd->code_lineno = 1; + lpwd->outfile = stdout; + lpwd->pounddefs = NULL; + lpwd->vardefs = NULL; + lpwd->uses = NULL; + lpwd->begin_macro = NULL; + lpwd->handle_char = NULL; + lpwd->includes = NULL; + lpwd->retcode = 0; +@endcode + +Open an input file. Also, get the prefix (everything before the last +period in the name), generate a cross reference filename, and input +the cross reference file. + +@code <Open input file> + lpwd->infile = fopen(argv[argidx], "r"); + while ((argidx < argc) && (lpwd->infile == NULL)) + { + fprintf(stderr, "%s: Unable to open file %s\n", + argv[0], argv[argidx]); + argidx++; + lpwd->infile = fopen(argv[argidx], "r"); + } + lpwd->curr_filename = argv[argidx]; + + str = strrchr(argv[argidx], '.'); + if (str != NULL) + { + len = str - argv[argidx]; + } + else + { + len = strlen(argv[argidx]); + } + + if ((len+4) >= MAX_FILENAME_LENGTH) /* Add 4 for the .xfr */ + { + fprintf(stderr, "filename to long, no xref file input\n"); + } + else + { + memcpy(fname, argv[argidx], len); + strcpy(&(fname[len]), ".xfr"); + input_xreffile(lpwd, fname, MAIN_TYPE); + } +@endcode + +Get the input file a line at a time and process it. The program +toggles between two input states. When \verb|IN_CODE|, it is handling +stuff between \verb|@code| and \verb|@endcode|. Otherwise, it is +processing documentation. + +@code <Scan input> + lpwd->codestate = NOT_IN_CODE; + while (get_input_line(lpwd) == GOT_LINE) + { + if (lpwd->codestate == IN_CODE) + { + if (strncmp(lpwd->line, "@endcode", 8) == 0) + { + lpwd->end_macro(lpwd); + lpwd->codestate = NOT_IN_CODE; + output_xref(lpwd); + lpwd->finalend_chunk(lpwd); + } + else if ( (strncmp(lpwd->line, "@staticdecls", 12) == 0) + || (strncmp(lpwd->line, "@defines", 8) == 0) + || (strncmp(lpwd->line, "@externdecls", 12) == 0) + || (strncmp(lpwd->line, "@uses", 5) == 0)) + { + /* These are ignored */ + } + else + { + process_codeline(lpwd); + } + } + else if (lpwd->line[0] == '@') + { + process_cmd(lpwd); + } + else + { + fputs(lpwd->line, lpwd->outfile); + fputc('\n', lpwd->outfile); + } + } +@endcode +\end{twproc} + diff --git a/web/tango-weevil/weevinfo.tw b/web/tango-weevil/weevinfo.tw new file mode 100644 index 0000000000..7f800b4a95 --- /dev/null +++ b/web/tango-weevil/weevinfo.tw @@ -0,0 +1,418 @@ +% +% Tango/Weevil - A WEB Tangler and Weaver +% Copyright (C) 1995 Corey Minyard +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +% +% Corey Minyard - minyard@metronet.com +% +\section{Weevil Information Include Introduction} + +This file contains the information used by weevil and the language and +output handlers that interface to weevil. + +@code <*> +#ifdef __cplusplus__ +extern "C" +{ +#endif + +@<Defines> +@<Structures> +@<C Language Routines> +@<LaTeX Routines> + +#ifdef __cplusplus__ +} +#endif +@endcode + +\section{Interfacing Languages to Weevil} + +A language definition requires two simple procedure calls. A +character handler handles all code a character at a time. It's job is +to tell weevil how to handle the character. It is passed the standard +weevil data, a character, and the next character in the sequence. It +returns the following codes: + +\begin{description} +\item[PROCESS\_CHAR\_NORMALLY] This is a standard character that +should be handled normally by weevil. +\item[PROCESS\_CHAR\_INSTRING] This character is in a string or +comment, it should not be scanned for special weevil characters. +\item[PROCESS\_CHAR\_SKIPNEXT] This character and the following +character should be skipped without being scanned for special weevil +characters. +\end{description} + +The other procedure is an initialization handler, which may initialize +the language handler. + +\section{Interfacing Output Types to Weevil} + +An output type requires a lot of function calls but is conceptually +simple. Each function call output a certain portion of the output, +just provide the proper output mechanism, add all the functions to the +list of output modes, and it's done. Section~\ref{weevinfo-defines} +has the typedefs and descriptions for all the functions. + +\begin{sloppypar} +Weevil has a number of output modes. Documentation modes is the mode +weevil starts in, it will just output everything as-is until it finds +a code chunk (marked by \verb|@code|). Then it switches to code mode +after calling \verb|begin_macro_handler|. Code mode calls the +language character handler on each character to find out if it is in a +string, comment, etc. It will output characters normally until it +sees an \verb|@| (unless in a string or comment). If it sees an +\verb|@@|, it will output a single \verb|@|. If it sees a +\verb|@<name>|, it will call \verb|output_macro_use_handler| with the +name. It will \emph{not} output the \verb|@<name>|. At the end of +the code chunk, \verb|end_macro_handler| will be called. At the +beginning of each line, \verb|output_linenum_handler| will be called. +\end{sloppypar} + +After a code chunk has been output, if cross references for any +variable uses exist for the code chunk then uses mode will be entered, +where each variable used by the code chunk will be output. A begin +handler is called, then for each cross reference the name handle is +called and a handler is called for each place where the name is +declared is given. Then an end name handler is called. After all +uses have been output, an end handler is called. + +If declaration cross references exist, they are output next. A begin +handler is called, then a handler for each variable name, then an end +handler for the declaration list. + +If any cross references were output, a handler is called after all +cross references have been output. Then documentation mode is entered +again. + +\section{Defines} +\label{weevinfo-defines} +The following code chunk has miscellaneous definitions and the +typedefs for all the language-specific and output-specific procedures +used by weevil. Each procedure is also described in detail in the +comment before it. + +@code <Defines> +#define MAXLINESIZE 200 + +#define TRUE 1 +#define FALSE 0 +typedef int bool; + + +/* Is the processor processing stuff between @code and @endcode or + is is processing documentation? Used by the codestate + variable. */ +#define IN_CODE 1 +#define NOT_IN_CODE 2 + + +#define PROCESS_CHAR_NORMALLY 0 +#define PROCESS_CHAR_INSTRING 1 +#define PROCESS_CHAR_SKIPNEXT 2 + +struct s_lpweevildat; + +/**************************************************************** +** +** These are for languages. +** +****************************************************************/ + +/* + * Process the code a character at a time. The current and next + * character are provided. Return PROCESS_CHAR_NORMALLY for + * standard output, PROCESS_CHAR_INSTRING if in a string or + * comment, or PROCESS_CHAR_SKIPNEXT if the current and next + * character should not be processed (just output). + */ +typedef int (*char_handler)(struct s_lpweevildat *lpwd, + char c, + char next_c); + +/* + * Initialize the language handler. This may use the + * language-specific pointer in lpwd to store info for + * itself. + */ +typedef void (*handler_init)(struct s_lpweevildat *lpwd); + + +/**************************************************************** +** +** These are for documentation output. +** +****************************************************************/ + +/* + * Initialize the mode handler. This may use the mode-specific + * pointer in lpwd to store info for itself. + */ +typedef void (*mode_init)(struct s_lpweevildat *lpwd); + +/* + * Handle the beginning of a code chunk. The name of the code + * chunk is passed in. + */ +typedef void (*begin_macro_handler)(struct s_lpweevildat *lpwd, + char *name); + +/* + * Handle the end of a code chunk. + */ +typedef void (*end_macro_handler)(struct s_lpweevildat *lpwd); + +/* + * Handle the end after all cross references and everything. + */ +typedef void (*finalend_chunk_handler)(struct s_lpweevildat *lpwd); + +/* + * Output the use of a code chunk in some code. The name of + * the code chunk is given. + */ +typedef void +(*output_macro_use_handler)(struct s_lpweevildat *lpwd, + char *macro_name); + +/* + * Output the line number at the beginning of the code chunk. + */ +typedef void +(*output_linenum_handler)(struct s_lpweevildat *lpwd); + +/* + * Output a single normal character. + */ +typedef void +(*output_char_handler)(struct s_lpweevildat *lpwd, + char c); + +/* + * Handle the beginning of a list of uses cross-references. + */ +typedef void +(*output_begin_uses_handler)(struct s_lpweevildat *lpwd); + +/* + * Handle the end of a list of uses cross-references. + */ +typedef void +(*output_end_uses_handler)(struct s_lpweevildat *lpwd); + +/* + * Handle the start of a single use cross reference. The name of + * the variable is given. + */ +typedef void +(*output_use_start_handler)(struct s_lpweevildat *lpwd, + char *name); + +/* + * Handle the end of a single use cross reference. The name of + * the variable is given. + */ +typedef void +(*output_use_end_handler)(struct s_lpweevildat *lpwd, + char *name); + +/* + * Output a use cross reference location. The file and macro + * of the declaration is given. The name being cross-referenced + * is also given. + */ +typedef void +(*output_use_ref_handler)(struct s_lpweevildat *lpwd, + char *file, + char *macro, + char *name); + +/* + * Handle between each use of a cross reference. + */ +typedef void +(*output_between_use_refs_handler)(struct s_lpweevildat *lpwd, + char *name); + +/* + * Handle the beginning of a list of declares cross-references. + */ +typedef void +(*output_begin_decls_handler)(struct s_lpweevildat *lpwd); + +/* + * Handle the end of a list of declares cross-references. + */ +typedef void +(*output_end_decls_handler)(struct s_lpweevildat *lpwd); + +/* + * Handle the output of a single declaration. + */ +typedef void +(*output_decl_handler)(struct s_lpweevildat *lpwd, + char *name); + +/* + * Handle the end of cross references. This is only called if + * cross-references were created. + */ +typedef void +(*output_after_xrefs_handler)(struct s_lpweevildat *lpwd); +@endcode + +\section{Structures} +Various structures used by weevil are put here. + +@code <Structures> +@<xreflist> +@<lpweevildat> +@endcode + +\subsection{xreflist} +This list contains list of cross references and information on where +they are declared. + +@code <xreflist> +typedef struct s_xreflist +{ + struct s_xreflist *next; + char *file; + char *macro; + char *name; +} xreflist; +@endcode + +\subsection{lpweevildat} +This large structure contains everything there is to know about what +weevil is doing. + +@code <lpweevildat> +typedef struct s_lpweevildat +{ + FILE *infile; /* File to get input from. */ + FILE *outfile; /* File to send output. */ + char line[MAXLINESIZE]; /* Current input line. */ + + int codestate; /* Current output state. */ + char *curr_filename; /* Name of the input file being + processed. */ + char *curr_macro; /* Current macro being input. */ + int maxlinesize; /* Maximum size of an input line. */ + int curr_lineno; /* Current input line number. */ + int code_lineno; /* Current code output line number. */ + + int retcode; /* Program return code. */ + + void *lang_data; /* Data for language mode. */ + void *mode_data; /* Data for output mode. */ + + xreflist *pounddefs; /* List of #defines. */ + xreflist *vardefs; /* List of variable defines. */ + xreflist *uses; /* List of variable uses. */ + + xreflist *includes; /* List of places to look for + .xfr files. */ + + /* Routine to scan code in a language-specific way. */ + char_handler handle_char; + + /* Routines to output data for the output mode. */ + begin_macro_handler begin_macro; + end_macro_handler end_macro; + finalend_chunk_handler finalend_chunk; + output_macro_use_handler output_macro_use; + output_linenum_handler output_linenum; + output_char_handler output_char; + output_begin_uses_handler output_begin_uses; + output_end_uses_handler output_end_uses; + output_use_start_handler output_use_start; + output_use_end_handler output_use_end; + output_use_ref_handler output_use_ref; + output_between_use_refs_handler output_between_use_refs; + output_begin_decls_handler output_begin_decls; + output_end_decls_handler output_end_decls; + output_decl_handler output_decl; + output_after_xrefs_handler output_after_xrefs; +} t_lpweevildat; +@endcode + +\section{Language Dependent Routines} +Here are forward-declared all the routines for the various languages +handled by weevil. + +\subsection{C} + +@code <C Language Routines> +int c_handle_char(t_lpweevildat *lpwd, + char c, + char next_c); + +void init_c_lang(t_lpweevildat *lpwd); +@endcode + +\section{Output Mode Routines} +Here are forward-declared all the routines for the various output modes +handled by weevil. + +\subsection{\LaTeX} + +@code <LaTeX Routines> +void latex_mode_init(t_lpweevildat *lpwd); + +void latex_begin_macro(t_lpweevildat *lpwd, + char *name); + +void latex_end_macro(t_lpweevildat *lpwd); + +void latex_finalend_chunk(t_lpweevildat *lpwd); + +void latex_output_macro_use(t_lpweevildat *lpwd, + char *macro_name); + +void latex_output_linenum(t_lpweevildat *lpwd); + +void latex_output_char(t_lpweevildat *lpwd, + char c); + +void latex_output_begin_uses(t_lpweevildat *lpwd); + +void latex_output_end_uses(t_lpweevildat *lpwd); + +void latex_output_use_start(t_lpweevildat *lpwd, + char *name); + +void latex_output_use_end(t_lpweevildat *lpwd, + char *name); + +void latex_output_use_ref(t_lpweevildat *lpwd, + char *file, + char *macro, + char *name); + +void latex_output_between_use_refs(t_lpweevildat *lpwd, + char *name); + +void latex_output_begin_decls(t_lpweevildat *lpwd); + +void latex_output_end_decls(t_lpweevildat *lpwd); + +void latex_output_decl(t_lpweevildat *lpwd, + char *name); + +void latex_output_after_xrefs(t_lpweevildat *lpwd); +@endcode |